kestrel.markets
Version:
A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.
234 lines (230 loc) • 6.88 kB
JavaScript
import {
bearerHeader,
consumeOperationStream,
jsonHeaders,
mintTrialCapability,
parseValidateDiagnostics,
postEffectful
} from "./bin-qewrvqtv.js";
import {
CliError,
EXIT
} from "./bin-t9ggwnv5.js";
import {
canonicalPlansText
} from "./bin-29be75ss.js";
import {
decodeOperation
} from "./bin-2ywrx58g.js";
// src/cli/backend/sse.ts
function httpErr(status, m) {
const exit = status === 404 ? EXIT.NOT_FOUND : status >= 500 ? EXIT.RUNTIME_UNAVAILABLE : EXIT.GENERIC;
return new CliError({ code: `HTTP_${status}`, exit, message: `${m} (HTTP ${status})` });
}
function unknownEvent(type) {
return new CliError({
code: "SSE_PROTOCOL_VIOLATION",
exit: EXIT.RUNTIME_UNAVAILABLE,
message: `unknown SSE event type ${JSON.stringify(type)} — not in the OperationEvent contract vocabulary`
});
}
// src/cli/backend/remote.ts
class RemoteBackend {
kind = "remote";
base;
fetch;
cap;
bearer;
jwtSigner;
constructor(opts) {
this.base = opts.baseUrl.replace(/\/+$/, "");
const f = opts.fetch ?? globalThis.fetch;
if (f === undefined)
throw new CliError({
code: "RUNTIME_UNAVAILABLE",
exit: EXIT.RUNTIME_UNAVAILABLE,
message: "global fetch unavailable — need node ≥18 or bun for --api"
});
this.fetch = f;
if (opts.jwtSigner !== undefined) {
this.jwtSigner = opts.jwtSigner;
}
if (opts.capability !== undefined) {
this.cap = opts.capability;
this.bearer = opts.capability.capabilityId;
} else if (opts.bearer !== undefined) {
this.bearer = opts.bearer;
}
}
async ensureBearer() {
if (this.bearer !== undefined)
return;
await this.bootstrapCapability();
}
async bootstrapCapability() {
if (this.cap)
return this.cap;
const { capability, bearer } = await mintTrialCapability({
fetch: this.fetch,
base: this.base,
onHttpError: remoteHttpErr
});
this.bearer = bearer;
this.cap = capability;
return this.cap;
}
async getPercept(_args) {
throw new CliError({
code: "REMOTE_UNSUPPORTED",
exit: EXIT.USAGE,
message: "the frame/percept View has no remote contract route (M1: catalog, validate, sim, grade, operations, offers, proof, events)",
hint: "run `frame` locally — omit --api; a remote View projection is not in the platform contract"
});
}
async submitPlan(documents) {
const auth = await this.authHeaders();
const canonicalText = canonicalPlansText(documents);
const res = await this.fetch(`${this.base}/validate`, {
method: "POST",
headers: { ...jsonHeaders(), ...auth },
body: JSON.stringify({ source: canonicalText })
});
if (res.status === 422) {
return {
gated: false,
value: { ok: false, canonicalText, documents, diagnostics: await parseValidateDiagnostics(res) }
};
}
if (!res.ok)
throw await remoteHttpErr(res, "POST /validate failed");
const wire = await res.json();
return {
gated: false,
value: {
ok: wire.valid === true,
canonicalText,
documents,
diagnostics: (wire.diagnostics ?? []).map((d) => `${d.severity} ${d.code}: ${d.message}`)
}
};
}
async openSession(_scope, args) {
return this.runSim(args, false);
}
async openDaySession(_scope, args) {
return this.runSim(args, true);
}
async runSim(args, _day) {
const source = canonicalPlansText(args.documents);
const artifactId = args.busPath;
if (artifactId === undefined)
throw new CliError({
code: "USAGE",
exit: EXIT.USAGE,
message: "remote sim requires a dataset — pass a catalog artifact id (the bus/data reference)"
});
const req = {
source,
dataset: { artifact_id: artifactId },
params: { fill_model: args.fillModel, r_usd: args.rUsd }
};
const opened = await this.effectfulPost("/sim", req);
if (opened.gated)
return opened;
const { payload } = await this.consumeStream(opened.value);
const report = payload["report"];
if (report === undefined)
throw httpErr(502, "sim stream carried no report artifact");
const blotter = payload["blotter"];
if (blotter === undefined)
throw httpErr(502, "sim stream carried no blotter artifact");
const wakes = payload["wakes"] ?? [];
return {
gated: false,
value: {
report,
plansText: source,
wakes,
operation: decodeOperation(opened.value),
blotter
}
};
}
async grade(subject, opts) {
const blotters = [blotterId(subject), ...(opts.corpus ?? []).map((b) => b.sessionId)];
const req = { blotters };
const opened = await this.effectfulPost("/grade", req);
if (opened.gated)
return opened;
const { payload } = await this.consumeStream(opened.value);
const result = payload["grade"] ?? payload["result"];
if (result === undefined)
throw httpErr(502, "grade stream carried no grade artifact");
return { gated: false, value: result };
}
async effectfulPost(path, body) {
return postEffectful({
fetch: this.fetch,
base: this.base,
path,
body,
authHeaders: await this.authHeaders(),
httpError: httpErr,
onHttpError: remoteHttpErr
});
}
async consumeStream(op) {
return consumeOperationStream({
fetch: this.fetch,
base: this.base,
operationId: op.operation_id,
cursor: op.cursor,
headers: await this.authHeaders(),
httpError: httpErr,
unknownEventError: unknownEvent
});
}
async authHeaders() {
if (this.jwtSigner !== undefined) {
return { authorization: `Bearer ${await this.jwtSigner()}` };
}
await this.ensureBearer();
return bearerHeader(this.bearer);
}
}
async function remoteHttpErr(res, message) {
const base = httpErr(res.status, message);
const domainCode = await readProblemCode(res);
if (domainCode === undefined)
return base;
return new CliError({
code: domainCode,
exit: base.exit,
message: base.message,
...base.hint !== undefined ? { hint: base.hint } : {}
});
}
var MAX_DOMAIN_CODE = 128;
async function readProblemCode(res) {
try {
const body = await res.json();
if (body === null || typeof body !== "object")
return;
const code = body["code"];
if (typeof code !== "string" || code.length === 0 || code.length > MAX_DOMAIN_CODE)
return;
return code;
} catch {
return;
}
}
function blotterId(subject) {
if (typeof subject.sessionId === "string")
return subject.sessionId;
throw new CliError({
code: "USAGE",
exit: EXIT.USAGE,
message: "remote grade requires a Blotter artifact id — run the sim remotely first, then grade its Blotter"
});
}
export { RemoteBackend };