@aexhq/sdk 0.39.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -16
- package/dist/_contracts/api-key.d.ts +49 -0
- package/dist/_contracts/api-key.js +87 -0
- package/dist/_contracts/bundle-manifest.d.ts +86 -0
- package/dist/_contracts/bundle-manifest.js +157 -0
- package/dist/_contracts/error-codes.d.ts +26 -0
- package/dist/_contracts/error-codes.js +79 -0
- package/dist/_contracts/error-factory.d.ts +32 -0
- package/dist/_contracts/error-factory.js +142 -0
- package/dist/_contracts/event-envelope.d.ts +33 -10
- package/dist/_contracts/event-envelope.js +46 -10
- package/dist/_contracts/event-view.d.ts +123 -0
- package/dist/_contracts/event-view.js +120 -0
- package/dist/_contracts/http.js +12 -3
- package/dist/_contracts/index.d.ts +8 -4
- package/dist/_contracts/index.js +11 -7
- package/dist/_contracts/models.d.ts +15 -0
- package/dist/_contracts/models.js +33 -0
- package/dist/_contracts/operations.d.ts +53 -2
- package/dist/_contracts/operations.js +119 -7
- package/dist/_contracts/run-record.d.ts +3 -2
- package/dist/_contracts/runtime-types.d.ts +126 -36
- package/dist/_contracts/runtime-types.js +33 -1
- package/dist/_contracts/sdk-errors.d.ts +61 -2
- package/dist/_contracts/sdk-errors.js +83 -3
- package/dist/_contracts/sdk-secrets.js +31 -6
- package/dist/_contracts/stable.d.ts +14 -0
- package/dist/_contracts/stable.js +14 -0
- package/dist/_contracts/status.d.ts +28 -1
- package/dist/_contracts/status.js +48 -3
- package/dist/_contracts/submission.d.ts +79 -2
- package/dist/_contracts/submission.js +148 -5
- package/dist/_contracts/suggest.d.ts +15 -0
- package/dist/_contracts/suggest.js +54 -0
- package/dist/asset-upload.d.ts +26 -0
- package/dist/asset-upload.js +218 -3
- package/dist/asset-upload.js.map +1 -1
- package/dist/bundle.d.ts +14 -3
- package/dist/bundle.js +34 -14
- package/dist/bundle.js.map +1 -1
- package/dist/canonical-zip.d.ts +68 -0
- package/dist/canonical-zip.js +307 -0
- package/dist/canonical-zip.js.map +1 -0
- package/dist/cli.mjs +1923 -338
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +217 -58
- package/dist/client.js +749 -261
- package/dist/client.js.map +1 -1
- package/dist/file.d.ts +33 -6
- package/dist/file.js +120 -54
- package/dist/file.js.map +1 -1
- package/dist/index.d.ts +17 -8
- package/dist/index.js +26 -9
- package/dist/index.js.map +1 -1
- package/dist/node-fs.d.ts +26 -9
- package/dist/node-fs.js +13 -38
- package/dist/node-fs.js.map +1 -1
- package/dist/node-walk.d.ts +69 -0
- package/dist/node-walk.js +146 -0
- package/dist/node-walk.js.map +1 -0
- package/dist/retry.d.ts +9 -13
- package/dist/retry.js +18 -17
- package/dist/retry.js.map +1 -1
- package/dist/skill.d.ts +16 -4
- package/dist/skill.js +18 -9
- package/dist/skill.js.map +1 -1
- package/dist/tool.d.ts +14 -2
- package/dist/tool.js +33 -7
- package/dist/tool.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +29 -5
- package/docs/billing.md +6 -0
- package/docs/concepts/agent-tools.md +11 -0
- package/docs/defaults.md +1 -0
- package/docs/errors.md +64 -4
- package/docs/events.md +84 -49
- package/docs/limits-and-quotas.md +24 -0
- package/docs/networking.md +7 -1
- package/docs/outputs.md +36 -7
- package/docs/quickstart.md +17 -7
- package/docs/run-config.md +3 -3
- package/docs/secrets.md +14 -0
- package/examples/feature-tour.ts +4 -6
- package/examples/spike-settle-latency.ts +125 -0
- package/package.json +4 -3
- package/dist/_contracts/event-guards.d.ts +0 -67
- package/dist/_contracts/event-guards.js +0 -36
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { suggest } from "./suggest.js";
|
|
1
2
|
/**
|
|
2
3
|
* Source of truth for the closed model set: each canonical model id maps to the
|
|
3
4
|
* upstream providers that can serve it and the **provider-native** model string
|
|
@@ -149,6 +150,38 @@ export function resolveProviderModelId(model, provider) {
|
|
|
149
150
|
}
|
|
150
151
|
return native;
|
|
151
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The single model→provider resolver shared by the SDK and the CLI, wrapping
|
|
155
|
+
* {@link providersForModel} with the forward-compat unknown-model allowance:
|
|
156
|
+
*
|
|
157
|
+
* - `provider` given: it is honored. If `model` is a KNOWN id, the provider
|
|
158
|
+
* must serve it (else throw). If `model` is UNKNOWN, it is allowed through
|
|
159
|
+
* so a slightly-old client can still run a newly-launched model (the server
|
|
160
|
+
* arbitrates).
|
|
161
|
+
* - `provider` omitted: a known model resolves to its DEFAULT provider (first
|
|
162
|
+
* declared). An UNKNOWN model with no provider throws a `did you mean?`
|
|
163
|
+
* hint — you must name a provider to run a model this client doesn't know.
|
|
164
|
+
*
|
|
165
|
+
* Returns the resolved {@link RunProvider}.
|
|
166
|
+
*/
|
|
167
|
+
export function resolveModelProvider(model, provider) {
|
|
168
|
+
const providers = providersForModel(model);
|
|
169
|
+
if (provider !== undefined) {
|
|
170
|
+
if (providers.length > 0 && !providers.includes(provider)) {
|
|
171
|
+
throw new Error(`model ${JSON.stringify(model)} is not available for provider ${provider}; ` +
|
|
172
|
+
`available: ${providers.join(", ")}`);
|
|
173
|
+
}
|
|
174
|
+
return provider;
|
|
175
|
+
}
|
|
176
|
+
const inferred = providers[0];
|
|
177
|
+
if (inferred === undefined) {
|
|
178
|
+
const hint = suggest(model, RUN_MODELS);
|
|
179
|
+
throw new Error(`${JSON.stringify(model)} is not a known model id` +
|
|
180
|
+
(hint ? ` (did you mean ${JSON.stringify(hint)}?)` : "") +
|
|
181
|
+
"; pass provider explicitly to run it");
|
|
182
|
+
}
|
|
183
|
+
return inferred;
|
|
184
|
+
}
|
|
152
185
|
export function isRunModel(input) {
|
|
153
186
|
return typeof input === "string" && RUN_MODELS.includes(input);
|
|
154
187
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HttpClient } from "./http.js";
|
|
2
|
+
import type { AexEvent } from "./event-envelope.js";
|
|
2
3
|
import type { RunUnit } from "./run-unit.js";
|
|
3
|
-
import type { AgentsMdRecord, BillingCheckoutRequest, BillingHostedSession, BillingLedgerPage, BillingLedgerQuery, BillingPortalRequest, BillingSummary, FileRecord, Output, OutputLink, OutputLinkOptions, OutputFileDownload, OutputFileSelector, OutputFileType, OutputQuery, OutputText, ReadOutputTextOptions, Run,
|
|
4
|
+
import type { AgentsMdRecord, BillingCheckoutRequest, BillingHostedSession, BillingLedgerPage, BillingLedgerQuery, BillingPortalRequest, BillingSummary, ChildRunRef, FileRecord, Output, OutputLink, OutputLinkOptions, OutputFileDownload, OutputFileSelector, OutputFileType, OutputQuery, OutputText, ReadOutputTextOptions, Run, RunListPage, RunListQuery, Session, SessionCreateRequest, SessionEvent, SessionListPage, SessionListQuery, SessionMessageAccepted, SessionMessageRequest, SessionMessagesPage, SessionMessagesQuery, SessionStateChangeAccepted, RunWebhookDelivery, SecretRecord, SecretReveal, SkillRecord, WebhookSigningSecret, WhoAmI } from "./runtime-types.js";
|
|
4
5
|
import type { PlatformRunSubmissionInput } from "./submission.js";
|
|
5
6
|
/**
|
|
6
7
|
* The single source of truth for SDK<->BFF transport. The SDK class
|
|
@@ -39,7 +40,32 @@ export declare function listRuns(http: HttpClient, query?: RunListQuery): Promis
|
|
|
39
40
|
export interface IdempotencyOptions {
|
|
40
41
|
readonly idempotencyKey?: string;
|
|
41
42
|
}
|
|
43
|
+
export interface SubmitOptions extends IdempotencyOptions {
|
|
44
|
+
readonly messageIdempotencyKey?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve a caller-supplied idempotency key to the value that ships on the
|
|
48
|
+
* request. FAIL-FAST: an empty or whitespace-only key THROWS
|
|
49
|
+
* {@link RunConfigValidationError} — a footgun that silently disabled dedup
|
|
50
|
+
* (`?? generate()` kept `''`, then a downstream truthy header-drop shipped no
|
|
51
|
+
* `Idempotency-Key`). An absent key generates a fresh one; a real key is
|
|
52
|
+
* returned verbatim. The single choke point every send/create/run entry uses.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveIdempotencyKey(key?: string): string;
|
|
42
55
|
export declare function createSession(http: HttpClient, request: SessionCreateRequest, options?: IdempotencyOptions): Promise<Session>;
|
|
56
|
+
/** The result of a non-blocking {@link submit}: the run id + the created session. */
|
|
57
|
+
export interface SubmitResult {
|
|
58
|
+
readonly runId: string;
|
|
59
|
+
readonly session: Session;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Fire-and-forget submit — create the session and post its first turn WITHOUT
|
|
63
|
+
* awaiting the turn to settle (the honest counterpart to await-settle `run()`).
|
|
64
|
+
* Returns the `runId` immediately; observe the run via a `webhook`, the event
|
|
65
|
+
* stream, or by re-opening the session. Mirrors {@link createSession}'s
|
|
66
|
+
* idempotency handling.
|
|
67
|
+
*/
|
|
68
|
+
export declare function submit(http: HttpClient, request: SessionCreateRequest, options?: SubmitOptions): Promise<SubmitResult>;
|
|
43
69
|
export declare function getSession(http: HttpClient, sessionId: string): Promise<Session>;
|
|
44
70
|
export declare function listSessions(http: HttpClient, query?: SessionListQuery): Promise<SessionListPage>;
|
|
45
71
|
export declare function sendSessionMessage(http: HttpClient, sessionId: string, request: SessionMessageRequest, options?: IdempotencyOptions): Promise<SessionMessageAccepted>;
|
|
@@ -47,6 +73,16 @@ export declare function listSessionMessages(http: HttpClient, sessionId: string,
|
|
|
47
73
|
export declare function suspendSession(http: HttpClient, sessionId: string, options?: IdempotencyOptions): Promise<SessionStateChangeAccepted>;
|
|
48
74
|
export declare function cancelSession(http: HttpClient, sessionId: string, options?: IdempotencyOptions): Promise<SessionStateChangeAccepted>;
|
|
49
75
|
export declare function resumeSession(http: HttpClient, sessionId: string, options?: IdempotencyOptions): Promise<SessionStateChangeAccepted>;
|
|
76
|
+
/**
|
|
77
|
+
* Request the HITL write-gate: park the session `awaiting_approval` before its
|
|
78
|
+
* next gated action (mirrors {@link suspendSession}). Imperative counterpart to
|
|
79
|
+
* the declarative submission-time `approvalGate`.
|
|
80
|
+
*/
|
|
81
|
+
export declare function requestApproval(http: HttpClient, sessionId: string, options?: IdempotencyOptions): Promise<SessionStateChangeAccepted>;
|
|
82
|
+
/** Approve an `awaiting_approval` session so the held turn resumes (→ running). */
|
|
83
|
+
export declare function approveSession(http: HttpClient, sessionId: string, options?: IdempotencyOptions): Promise<SessionStateChangeAccepted>;
|
|
84
|
+
/** Deny an `awaiting_approval` session so the held turn is cancelled (→ cancelled). */
|
|
85
|
+
export declare function denySession(http: HttpClient, sessionId: string, options?: IdempotencyOptions): Promise<SessionStateChangeAccepted>;
|
|
50
86
|
export declare function deleteSession(http: HttpClient, sessionId: string, options?: IdempotencyOptions): Promise<SessionStateChangeAccepted | void>;
|
|
51
87
|
export declare function listSessionEvents(http: HttpClient, sessionId: string): Promise<readonly SessionEvent[]>;
|
|
52
88
|
export declare function listSessionOutputs(http: HttpClient, sessionId: string, query?: OutputQuery): Promise<readonly Output[]>;
|
|
@@ -57,7 +93,7 @@ export declare function getSessionCoordinatorTicket(http: HttpClient, sessionId:
|
|
|
57
93
|
* pages and returns the FULL accumulated list, preserving the prior single-call
|
|
58
94
|
* contract for callers (download/*, CLI, streamEvents polling).
|
|
59
95
|
*/
|
|
60
|
-
export declare function listRunEvents(http: HttpClient, runId: string): Promise<readonly
|
|
96
|
+
export declare function listRunEvents(http: HttpClient, runId: string): Promise<readonly AexEvent[]>;
|
|
61
97
|
/** A coordinator WS connection grant minted by the hosted API's ticket broker. */
|
|
62
98
|
export interface CoordinatorTicket {
|
|
63
99
|
readonly wsUrl: string;
|
|
@@ -94,6 +130,13 @@ export declare const READ_OUTPUT_TEXT_DEFAULT_BYTES = 50000;
|
|
|
94
130
|
* selector lists the run's outputs to resolve the id; an id selector skips that.
|
|
95
131
|
*/
|
|
96
132
|
export declare function readOutputText(http: HttpClient, runId: string, selector: OutputFileSelector, options?: ReadOutputTextOptions): Promise<OutputText>;
|
|
133
|
+
/**
|
|
134
|
+
* List a run's subagent CHILD runs (`GET /runs/:id/children`). Each row is a
|
|
135
|
+
* {@link ChildRunRef} whose `id` resolves through the run facade (getRun /
|
|
136
|
+
* events / outputs) — so every child the platform hands you is resolvable. An
|
|
137
|
+
* empty array means the run spawned no children.
|
|
138
|
+
*/
|
|
139
|
+
export declare function listRunChildren(http: HttpClient, runId: string): Promise<readonly ChildRunRef[]>;
|
|
97
140
|
export declare function cancelRun(http: HttpClient, runId: string): Promise<void>;
|
|
98
141
|
export declare function deleteRun(http: HttpClient, runId: string): Promise<void>;
|
|
99
142
|
/**
|
|
@@ -146,6 +189,14 @@ export declare function getBillingLedger(http: HttpClient, query?: BillingLedger
|
|
|
146
189
|
*/
|
|
147
190
|
export declare function getWebhookSigningSecret(http: HttpClient): Promise<WebhookSigningSecret>;
|
|
148
191
|
export declare function filterOutputs(outputs: readonly Output[], query: OutputQuery): readonly Output[];
|
|
192
|
+
/**
|
|
193
|
+
* The single filename-matcher for cross-run / per-session output SEARCH. A
|
|
194
|
+
* string is a case-insensitive SUBSTRING match; a RegExp is tested as given (and
|
|
195
|
+
* reset to `lastIndex = 0` so a reused `/g` regex is safe). Sharing this SSoT is
|
|
196
|
+
* what closes the T16 crash class: `searchOutputs` no longer assumes `filename`
|
|
197
|
+
* is a string and passes a RegExp into `escapeRegExp(...).replace(...)`.
|
|
198
|
+
*/
|
|
199
|
+
export declare function toFilenameMatcher(filename: string | RegExp): (name: string) => boolean;
|
|
149
200
|
export declare function classifyOutput(output: Pick<Output, "filename" | "contentType">): OutputFileType;
|
|
150
201
|
export declare function normalizeOutputLinkExpiresIn(input?: OutputLinkOptions["expiresIn"]): number;
|
|
151
202
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { strToU8, zipSync } from "fflate";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { normalizeRunUnit } from "./run-unit.js";
|
|
3
|
-
import { RunStateError } from "./sdk-errors.js";
|
|
4
|
+
import { RunConfigValidationError, RunStateError } from "./sdk-errors.js";
|
|
4
5
|
import { assertRunRecordArchivePublicSafeV1, buildRunRecordDownloadManifestV1 } from "./run-record.js";
|
|
5
6
|
/**
|
|
6
7
|
* The single source of truth for SDK<->BFF transport. The SDK class
|
|
@@ -83,8 +84,41 @@ export async function listRuns(http, query) {
|
|
|
83
84
|
}
|
|
84
85
|
return changed ? { ...page, runs } : page;
|
|
85
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a caller-supplied idempotency key to the value that ships on the
|
|
89
|
+
* request. FAIL-FAST: an empty or whitespace-only key THROWS
|
|
90
|
+
* {@link RunConfigValidationError} — a footgun that silently disabled dedup
|
|
91
|
+
* (`?? generate()` kept `''`, then a downstream truthy header-drop shipped no
|
|
92
|
+
* `Idempotency-Key`). An absent key generates a fresh one; a real key is
|
|
93
|
+
* returned verbatim. The single choke point every send/create/run entry uses.
|
|
94
|
+
*/
|
|
95
|
+
export function resolveIdempotencyKey(key) {
|
|
96
|
+
if (key === undefined) {
|
|
97
|
+
return `aex-idem-${randomUUID()}`;
|
|
98
|
+
}
|
|
99
|
+
if (typeof key !== "string" || key.trim().length === 0) {
|
|
100
|
+
throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
|
|
101
|
+
field: "idempotencyKey",
|
|
102
|
+
value: key
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return key;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Fail-closed idempotency header builder. An EMPTY string throws (defense in
|
|
109
|
+
* depth alongside {@link resolveIdempotencyKey}) rather than silently dropping
|
|
110
|
+
* the header and proceeding non-idempotent; an absent key yields no header.
|
|
111
|
+
*/
|
|
86
112
|
function idempotencyHeaders(options) {
|
|
87
|
-
|
|
113
|
+
if (options?.idempotencyKey === undefined)
|
|
114
|
+
return undefined;
|
|
115
|
+
if (typeof options.idempotencyKey !== "string" || options.idempotencyKey.trim().length === 0) {
|
|
116
|
+
throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
|
|
117
|
+
field: "idempotencyKey",
|
|
118
|
+
value: options.idempotencyKey
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return { "Idempotency-Key": options.idempotencyKey };
|
|
88
122
|
}
|
|
89
123
|
export async function createSession(http, request, options) {
|
|
90
124
|
const headers = idempotencyHeaders(options);
|
|
@@ -95,6 +129,36 @@ export async function createSession(http, request, options) {
|
|
|
95
129
|
});
|
|
96
130
|
return unwrapSession(result);
|
|
97
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Fire-and-forget submit — create the session and post its first turn WITHOUT
|
|
134
|
+
* awaiting the turn to settle (the honest counterpart to await-settle `run()`).
|
|
135
|
+
* Returns the `runId` immediately; observe the run via a `webhook`, the event
|
|
136
|
+
* stream, or by re-opening the session. Mirrors {@link createSession}'s
|
|
137
|
+
* idempotency handling.
|
|
138
|
+
*/
|
|
139
|
+
export async function submit(http, request, options) {
|
|
140
|
+
const createKey = resolveIdempotencyKey(options?.idempotencyKey);
|
|
141
|
+
const messageKey = options?.messageIdempotencyKey !== undefined
|
|
142
|
+
? resolveIdempotencyKey(options.messageIdempotencyKey)
|
|
143
|
+
: `${createKey}:message`;
|
|
144
|
+
const { input, ...createRequest } = request;
|
|
145
|
+
assertSubmitInput(input);
|
|
146
|
+
const created = await createSession(http, createRequest, { idempotencyKey: createKey });
|
|
147
|
+
const sessionId = created.sessionId ?? created.id;
|
|
148
|
+
const accepted = await sendSessionMessage(http, sessionId, { input }, { idempotencyKey: messageKey });
|
|
149
|
+
const session = accepted.session;
|
|
150
|
+
return { runId: session.sessionId ?? session.id, session };
|
|
151
|
+
}
|
|
152
|
+
function assertSubmitInput(input) {
|
|
153
|
+
const ok = (typeof input === "string" && input.length > 0) ||
|
|
154
|
+
(Array.isArray(input) && input.length > 0 && input.every((segment) => typeof segment === "string" && segment.length > 0));
|
|
155
|
+
if (!ok) {
|
|
156
|
+
throw new RunConfigValidationError("submit: request.input must be a non-empty string or string array", {
|
|
157
|
+
field: "input",
|
|
158
|
+
value: input
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
98
162
|
export async function getSession(http, sessionId) {
|
|
99
163
|
const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}`);
|
|
100
164
|
return unwrapSession(result);
|
|
@@ -141,6 +205,25 @@ export async function resumeSession(http, sessionId, options) {
|
|
|
141
205
|
const headers = idempotencyHeaders(options);
|
|
142
206
|
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/resume`, { method: "POST", ...(headers ? { headers } : {}) });
|
|
143
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Request the HITL write-gate: park the session `awaiting_approval` before its
|
|
210
|
+
* next gated action (mirrors {@link suspendSession}). Imperative counterpart to
|
|
211
|
+
* the declarative submission-time `approvalGate`.
|
|
212
|
+
*/
|
|
213
|
+
export async function requestApproval(http, sessionId, options) {
|
|
214
|
+
const headers = idempotencyHeaders(options);
|
|
215
|
+
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/request-approval`, { method: "POST", ...(headers ? { headers } : {}) });
|
|
216
|
+
}
|
|
217
|
+
/** Approve an `awaiting_approval` session so the held turn resumes (→ running). */
|
|
218
|
+
export async function approveSession(http, sessionId, options) {
|
|
219
|
+
const headers = idempotencyHeaders(options);
|
|
220
|
+
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/approve`, { method: "POST", ...(headers ? { headers } : {}) });
|
|
221
|
+
}
|
|
222
|
+
/** Deny an `awaiting_approval` session so the held turn is cancelled (→ cancelled). */
|
|
223
|
+
export async function denySession(http, sessionId, options) {
|
|
224
|
+
const headers = idempotencyHeaders(options);
|
|
225
|
+
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/deny`, { method: "POST", ...(headers ? { headers } : {}) });
|
|
226
|
+
}
|
|
144
227
|
export async function deleteSession(http, sessionId, options) {
|
|
145
228
|
const headers = idempotencyHeaders(options);
|
|
146
229
|
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}`, { method: "DELETE", ...(headers ? { headers } : {}) });
|
|
@@ -212,7 +295,7 @@ export async function findOutput(http, runId, query) {
|
|
|
212
295
|
return null;
|
|
213
296
|
if (matches.length === 1)
|
|
214
297
|
return matches[0];
|
|
215
|
-
throw new RunStateError("
|
|
298
|
+
throw new RunStateError("outputs.findOne: output query matched multiple files", {
|
|
216
299
|
runId,
|
|
217
300
|
matches: matches.map((output) => output.filename ?? output.id)
|
|
218
301
|
});
|
|
@@ -260,7 +343,7 @@ export function resolveOutputFileSelector(outputs, selector, runId) {
|
|
|
260
343
|
if (isPathSelector(selector)) {
|
|
261
344
|
const target = normalizeOutputLookupPath(selector.path);
|
|
262
345
|
if (!target) {
|
|
263
|
-
throw new RunStateError("
|
|
346
|
+
throw new RunStateError("outputs.download: output path must be non-empty", { runId, path: selector.path });
|
|
264
347
|
}
|
|
265
348
|
const matches = outputs.filter((output) => {
|
|
266
349
|
if (typeof output.filename !== "string")
|
|
@@ -274,15 +357,15 @@ export function resolveOutputFileSelector(outputs, selector, runId) {
|
|
|
274
357
|
if (matches.length === 1)
|
|
275
358
|
return matches[0];
|
|
276
359
|
if (matches.length > 1) {
|
|
277
|
-
throw new RunStateError(`
|
|
360
|
+
throw new RunStateError(`outputs.download: output path "${selector.path}" matched multiple files`, { runId, path: selector.path, matches: matches.map((output) => output.filename ?? output.id) });
|
|
278
361
|
}
|
|
279
|
-
throw new RunStateError(`
|
|
362
|
+
throw new RunStateError(`outputs.download: output path "${selector.path}" was not found`, {
|
|
280
363
|
runId,
|
|
281
364
|
path: selector.path
|
|
282
365
|
});
|
|
283
366
|
}
|
|
284
367
|
if (typeof selector?.id !== "string" || selector.id.length === 0) {
|
|
285
|
-
throw new RunStateError("
|
|
368
|
+
throw new RunStateError("outputs.download: selector must include an output id or path", { runId });
|
|
286
369
|
}
|
|
287
370
|
return { ...selector, id: selector.id };
|
|
288
371
|
}
|
|
@@ -387,6 +470,18 @@ function grepLines(text, pattern) {
|
|
|
387
470
|
.filter((line) => test(line))
|
|
388
471
|
.join("\n");
|
|
389
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* List a run's subagent CHILD runs (`GET /runs/:id/children`). Each row is a
|
|
475
|
+
* {@link ChildRunRef} whose `id` resolves through the run facade (getRun /
|
|
476
|
+
* events / outputs) — so every child the platform hands you is resolvable. An
|
|
477
|
+
* empty array means the run spawned no children.
|
|
478
|
+
*/
|
|
479
|
+
export async function listRunChildren(http, runId) {
|
|
480
|
+
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/children`);
|
|
481
|
+
return Array.isArray(result)
|
|
482
|
+
? result
|
|
483
|
+
: result.children;
|
|
484
|
+
}
|
|
390
485
|
export async function cancelRun(http, runId) {
|
|
391
486
|
await http.request(`/api/runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
|
|
392
487
|
}
|
|
@@ -522,6 +617,23 @@ function normalizeOutputLookupPath(path) {
|
|
|
522
617
|
export function filterOutputs(outputs, query) {
|
|
523
618
|
return outputs.filter((output) => outputMatchesQuery(output, query));
|
|
524
619
|
}
|
|
620
|
+
/**
|
|
621
|
+
* The single filename-matcher for cross-run / per-session output SEARCH. A
|
|
622
|
+
* string is a case-insensitive SUBSTRING match; a RegExp is tested as given (and
|
|
623
|
+
* reset to `lastIndex = 0` so a reused `/g` regex is safe). Sharing this SSoT is
|
|
624
|
+
* what closes the T16 crash class: `searchOutputs` no longer assumes `filename`
|
|
625
|
+
* is a string and passes a RegExp into `escapeRegExp(...).replace(...)`.
|
|
626
|
+
*/
|
|
627
|
+
export function toFilenameMatcher(filename) {
|
|
628
|
+
if (typeof filename === "string") {
|
|
629
|
+
const needle = filename.toLowerCase();
|
|
630
|
+
return (name) => name.toLowerCase().includes(needle);
|
|
631
|
+
}
|
|
632
|
+
return (name) => {
|
|
633
|
+
filename.lastIndex = 0;
|
|
634
|
+
return filename.test(name);
|
|
635
|
+
};
|
|
636
|
+
}
|
|
525
637
|
export function classifyOutput(output) {
|
|
526
638
|
const contentType = normalizeContentType(output.contentType);
|
|
527
639
|
if (contentType) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RunCostTelemetry } from "./run-cost.js";
|
|
2
2
|
import { type CustodyManifestV1, type CustodyRedactionFinding } from "./run-custody.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { AexEvent } from "./event-envelope.js";
|
|
4
|
+
import type { Run, Output } from "./runtime-types.js";
|
|
4
5
|
import type { PlatformSubmission } from "./submission.js";
|
|
5
6
|
export declare const RUN_RECORD_SCHEMA_VERSION: "aex.run-record.v1";
|
|
6
7
|
export declare const RUN_RECORD_MANIFEST_SCHEMA_VERSION: "aex.run-record.manifest.v1";
|
|
@@ -22,7 +23,7 @@ export interface RunRecordMetadataV1 {
|
|
|
22
23
|
}
|
|
23
24
|
export interface RunRecordEventsV1 {
|
|
24
25
|
/** Typed `channel: "event"` records in the SDK `events/events.jsonl` export. */
|
|
25
|
-
readonly typed: readonly
|
|
26
|
+
readonly typed: readonly AexEvent[];
|
|
26
27
|
}
|
|
27
28
|
export interface RunRecordV1 {
|
|
28
29
|
readonly schemaVersion: typeof RUN_RECORD_SCHEMA_VERSION;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { SessionStatus } from "./status.js";
|
|
1
|
+
import type { SessionStatus, SessionTerminalOutcome } from "./status.js";
|
|
2
|
+
import type { RunCostProviderUsage } from "./run-cost.js";
|
|
2
3
|
import type { PlatformRunSubmissionInput, PlatformSubmission } from "./submission.js";
|
|
3
4
|
/**
|
|
4
5
|
* Loose record describing a run as the dashboard BFF returns it. Concrete
|
|
@@ -37,6 +38,12 @@ export interface Run {
|
|
|
37
38
|
*/
|
|
38
39
|
readonly usage?: UsageSummary;
|
|
39
40
|
readonly costTelemetry?: import("./run-cost.js").RunCostTelemetry;
|
|
41
|
+
/**
|
|
42
|
+
* The authoritative terminal OUTCOME of the run's last turn — the settle-
|
|
43
|
+
* written outcome (`succeeded`/`failed`/`timed_out`/`cancelled`), distinct
|
|
44
|
+
* from the resumable lifecycle {@link status}. Absent until the run settles.
|
|
45
|
+
*/
|
|
46
|
+
readonly lastTurnOutcome?: SessionTerminalOutcome;
|
|
40
47
|
readonly runtimeManifest?: import("./runtime-manifest.js").RuntimeManifest;
|
|
41
48
|
readonly [key: string]: unknown;
|
|
42
49
|
}
|
|
@@ -68,9 +75,17 @@ export interface Session {
|
|
|
68
75
|
readonly retainedStorageBytes?: number;
|
|
69
76
|
readonly usage?: UsageSummary;
|
|
70
77
|
readonly costUsd?: number;
|
|
78
|
+
/**
|
|
79
|
+
* The authoritative terminal OUTCOME of the session's last turn — the
|
|
80
|
+
* settle-written outcome (`succeeded`/`failed`/`timed_out`/`cancelled`),
|
|
81
|
+
* distinct from the resumable lifecycle {@link status} (`idle`/`suspended`).
|
|
82
|
+
* A cancelled turn reads `cancelled` here even though the session may park
|
|
83
|
+
* `idle`; absent until the turn settles.
|
|
84
|
+
*/
|
|
85
|
+
readonly lastTurnOutcome?: SessionTerminalOutcome;
|
|
71
86
|
readonly errorMessage?: string | null;
|
|
72
87
|
/**
|
|
73
|
-
* Settle-written failure taxonomy for
|
|
88
|
+
* Settle-written failure taxonomy for a `failed` session (e.g.
|
|
74
89
|
* `provider-permanent`, `budget_exhausted`) — the class a caller can branch
|
|
75
90
|
* on, complementing the human-readable `errorMessage`.
|
|
76
91
|
*/
|
|
@@ -158,6 +173,77 @@ export interface UsageSummary {
|
|
|
158
173
|
readonly cacheCreationInputTokens?: number;
|
|
159
174
|
readonly totalTokens?: number;
|
|
160
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Project a {@link UsageSummary} from the settle-written
|
|
178
|
+
* {@link RunCostProviderUsage} entries — the SINGLE server source of token
|
|
179
|
+
* usage ({@link Run.costTelemetry}`.providerUsage`). Sums each field across all
|
|
180
|
+
* provider entries; a field is present only when at least one entry carried it.
|
|
181
|
+
* This retires the dead `session.usage` / `aex.usage`-event usage path: the SDK
|
|
182
|
+
* derives usage from cost telemetry, never re-reads a dual-written top-level
|
|
183
|
+
* `usage`. Pure.
|
|
184
|
+
*/
|
|
185
|
+
export declare function usageFromProviderUsage(providerUsage: readonly RunCostProviderUsage[] | undefined): UsageSummary;
|
|
186
|
+
/**
|
|
187
|
+
* The unified SETTLED-RESULT contract shared by `run()` and `done()`. Because
|
|
188
|
+
* both await the settle commit by default, these fields are ALWAYS present at a
|
|
189
|
+
* terminal read — they are NON-optional, so a code path that forgets to
|
|
190
|
+
* populate `costUsd`/`usage`/the terminal `status` fails to typecheck. The SDK
|
|
191
|
+
* `RunResult`/`SessionTurnResult` extend this one shape so `done()` == `run()`.
|
|
192
|
+
*
|
|
193
|
+
* `costUsd` is the AEX showback estimate in USD (>= 0) and EXCLUDES the
|
|
194
|
+
* customer's BYOK provider spend — price BYOK from `usage` tokens.
|
|
195
|
+
*/
|
|
196
|
+
export interface SettledResult {
|
|
197
|
+
/** The terminal outcome (never a bare resumable `idle`). */
|
|
198
|
+
readonly status: SessionTerminalOutcome;
|
|
199
|
+
/** `succeeded` ⇒ true; `failed`/`timed_out`/`cancelled` ⇒ false. */
|
|
200
|
+
readonly ok: boolean;
|
|
201
|
+
/** AEX showback estimate (USD, >= 0). Excludes BYOK provider spend. */
|
|
202
|
+
readonly costUsd: number;
|
|
203
|
+
/** Aggregate token usage, derived from `costTelemetry.providerUsage`. */
|
|
204
|
+
readonly usage: UsageSummary;
|
|
205
|
+
/** Terminal failure message (from the terminal `RUN_ERROR` event) when `!ok`. */
|
|
206
|
+
readonly error?: string;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The reason a schema-constrained decode did not yield a value:
|
|
210
|
+
* - `schema_violation` — the model output failed schema validation.
|
|
211
|
+
* - `uncertain` — the model signalled low confidence / declined to commit.
|
|
212
|
+
* - `refused` — the model refused the request.
|
|
213
|
+
*/
|
|
214
|
+
export type RunRefusalReason = "schema_violation" | "uncertain" | "refused";
|
|
215
|
+
/**
|
|
216
|
+
* The typed outcome of a `run<T>({ responseFormat })`: EITHER a schema-valid
|
|
217
|
+
* decoded value OR a typed refusal — there is no untyped path that silently
|
|
218
|
+
* yields a hallucinated object. `T` is the decoded value type.
|
|
219
|
+
*/
|
|
220
|
+
export type RunOutcome<T = unknown> = {
|
|
221
|
+
readonly kind: "decoded";
|
|
222
|
+
readonly value: T;
|
|
223
|
+
} | {
|
|
224
|
+
readonly kind: "refused";
|
|
225
|
+
readonly reason: RunRefusalReason;
|
|
226
|
+
readonly detail?: string;
|
|
227
|
+
};
|
|
228
|
+
/** One item's settled result inside a {@link BatchResult}. */
|
|
229
|
+
export interface BatchItemResult<T = unknown> extends SettledResult {
|
|
230
|
+
readonly runId: string;
|
|
231
|
+
/** Present only for a `responseFormat`-decoded item. */
|
|
232
|
+
readonly outcome?: RunOutcome<T>;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* The result of `aex.batch(items, …)`: every item's settled result PLUS a real
|
|
236
|
+
* rollup. The rollup is honest because `run()` awaits settle, so every item's
|
|
237
|
+
* `costUsd`/`usage` is populated — a missing cost is a typed absent, not a
|
|
238
|
+
* silent `$0`.
|
|
239
|
+
*/
|
|
240
|
+
export interface BatchResult<T = unknown> {
|
|
241
|
+
readonly results: readonly BatchItemResult<T>[];
|
|
242
|
+
readonly totalCostUsd: number;
|
|
243
|
+
readonly totalUsage: UsageSummary;
|
|
244
|
+
readonly okCount: number;
|
|
245
|
+
readonly failed: readonly BatchItemResult<T>[];
|
|
246
|
+
}
|
|
161
247
|
/**
|
|
162
248
|
* Filters for {@link import("./operations.js").listRuns} / the CLI's `aex runs`.
|
|
163
249
|
* Every field is optional; omitting all of them lists the most recent runs in the
|
|
@@ -192,6 +278,37 @@ export interface RunListPage {
|
|
|
192
278
|
readonly runs: readonly RunSummary[];
|
|
193
279
|
readonly nextCursor?: string;
|
|
194
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* The minimal capability a value must carry to be RESOLVABLE through the run
|
|
283
|
+
* facade — `getRun` / `listRunEvents` / `listOutputs` all key on this `id`.
|
|
284
|
+
* Encodes the "handed ⇒ resolvable" invariant at the type level: anything the
|
|
285
|
+
* platform hands you as a run reference exposes a resolvable `id`, so a run can
|
|
286
|
+
* never be surfaced as a bare unresolvable string.
|
|
287
|
+
*/
|
|
288
|
+
export interface ResolvableRunRef {
|
|
289
|
+
readonly id: string;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* A subagent CHILD run, enumerated under its parent via `GET /runs/:id/children`.
|
|
293
|
+
* A first-class, lineage-discoverable run reference: it {@link ResolvableRunRef}
|
|
294
|
+
* (its `id` resolves through the run facade — events/outputs/getRun), carries the
|
|
295
|
+
* lineage (`parentRunId`/`depth`) and the terminal outcome/cost, so a child is
|
|
296
|
+
* observable exactly like a top-level run.
|
|
297
|
+
*/
|
|
298
|
+
export interface ChildRunRef extends ResolvableRunRef {
|
|
299
|
+
/** The parent run this child was spawned by. */
|
|
300
|
+
readonly parentRunId: string;
|
|
301
|
+
/** The child's run status. */
|
|
302
|
+
readonly status: string;
|
|
303
|
+
/** Subagent nesting depth (1 = direct child of the top-level run). */
|
|
304
|
+
readonly depth?: number;
|
|
305
|
+
/** Settled AEX showback estimate (USD) for the child, when present. */
|
|
306
|
+
readonly costUsd?: number;
|
|
307
|
+
readonly createdAt?: string;
|
|
308
|
+
readonly terminalAt?: string | null;
|
|
309
|
+
/** The child's terminal outcome once settled. */
|
|
310
|
+
readonly lastTurnOutcome?: SessionTerminalOutcome;
|
|
311
|
+
}
|
|
195
312
|
/**
|
|
196
313
|
* Cross-run output search query (`Aex.sessions.searchOutputs`). Restrict to a
|
|
197
314
|
* corpus with `runIds`; filter by filename substring / extension / content type.
|
|
@@ -202,8 +319,13 @@ export interface RunListPage {
|
|
|
202
319
|
export interface OutputSearchQuery {
|
|
203
320
|
/** Restrict the search to these runs (the chat corpus allow-list). */
|
|
204
321
|
readonly runIds?: readonly string[];
|
|
205
|
-
/**
|
|
206
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Filename match. A string is a case-insensitive SUBSTRING match; a RegExp is
|
|
324
|
+
* tested as given. Unified with {@link OutputQuery.filename} (`string | RegExp`)
|
|
325
|
+
* so the same value works on `find()` and `search()` — feed it through
|
|
326
|
+
* {@link import("./operations.js").toFilenameMatcher} rather than assuming a string.
|
|
327
|
+
*/
|
|
328
|
+
readonly filename?: string | RegExp;
|
|
207
329
|
/** File extension, with or without a leading dot. Case-insensitive. */
|
|
208
330
|
readonly extension?: string;
|
|
209
331
|
/** Exact content type or a prefix wildcard such as `image/*`. */
|
|
@@ -223,38 +345,6 @@ export interface OutputSearchHit {
|
|
|
223
345
|
export interface OutputSearchPage {
|
|
224
346
|
readonly hits: readonly OutputSearchHit[];
|
|
225
347
|
}
|
|
226
|
-
/**
|
|
227
|
-
* A run event as recorded by the dashboard. Includes the `type` field
|
|
228
|
-
* that the `is*Event` type guards narrow on plus any provider payload.
|
|
229
|
-
*
|
|
230
|
-
* The unified-stream discriminators (`channel`, `source`, `sourceSeq`,
|
|
231
|
-
* `emittedAt`, `receivedAt`, `level`) carry through from the coordinator
|
|
232
|
-
* envelope (see `event-envelope.ts` —
|
|
233
|
-
* {@link import("./event-envelope.js").AexEvent}) so SDK/CLI consumers can
|
|
234
|
-
* split/filter the one stream by channel or source. All are OPTIONAL: archived
|
|
235
|
-
* events from before the unification (and any producer that omits an ordering
|
|
236
|
-
* attribute) lack them, an absent `channel` means `"event"` (see `channelOf`),
|
|
237
|
-
* and `level` is present only on `log`-channel records.
|
|
238
|
-
*/
|
|
239
|
-
export interface RunEvent {
|
|
240
|
-
readonly id: string;
|
|
241
|
-
readonly type: string;
|
|
242
|
-
readonly runId?: string;
|
|
243
|
-
readonly recordedAt?: string;
|
|
244
|
-
/** Which sub-stream this record rides — `"event"` (typed) or `"log"`. Absent ⇒ `"event"`. */
|
|
245
|
-
readonly channel?: import("./event-envelope.js").AexEventChannel;
|
|
246
|
-
/** Coarse origin classifier. See {@link AexEventSource}. */
|
|
247
|
-
readonly source?: import("./event-envelope.js").AexEventSource;
|
|
248
|
-
/** Per-source monotonic counter assigned at the source (carried, not re-ordered). */
|
|
249
|
-
readonly sourceSeq?: number;
|
|
250
|
-
/** Source wall-clock ms at emit (carried for a best-effort client time view). */
|
|
251
|
-
readonly emittedAt?: number;
|
|
252
|
-
/** The DO's authoritative receive-time (wall-clock ms) stamped at ingest, companion to `seq`. */
|
|
253
|
-
readonly receivedAt?: number;
|
|
254
|
-
/** Log severity, first-class on a `channel: "log"` record ("info" | "warn" | "error"). */
|
|
255
|
-
readonly level?: import("./event-envelope.js").AexLogLevel;
|
|
256
|
-
readonly [key: string]: unknown;
|
|
257
|
-
}
|
|
258
348
|
/** Status of a per-run webhook delivery. Terminal: delivered/exhausted/invalid. */
|
|
259
349
|
export type RunWebhookDeliveryStatus = "pending" | "delivering" | "retrying" | "delivered" | "exhausted" | "invalid";
|
|
260
350
|
/**
|
|
@@ -1,2 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Project a {@link UsageSummary} from the settle-written
|
|
3
|
+
* {@link RunCostProviderUsage} entries — the SINGLE server source of token
|
|
4
|
+
* usage ({@link Run.costTelemetry}`.providerUsage`). Sums each field across all
|
|
5
|
+
* provider entries; a field is present only when at least one entry carried it.
|
|
6
|
+
* This retires the dead `session.usage` / `aex.usage`-event usage path: the SDK
|
|
7
|
+
* derives usage from cost telemetry, never re-reads a dual-written top-level
|
|
8
|
+
* `usage`. Pure.
|
|
9
|
+
*/
|
|
10
|
+
export function usageFromProviderUsage(providerUsage) {
|
|
11
|
+
if (!providerUsage || providerUsage.length === 0)
|
|
12
|
+
return {};
|
|
13
|
+
let inputTokens;
|
|
14
|
+
let outputTokens;
|
|
15
|
+
let cacheReadInputTokens;
|
|
16
|
+
let cacheCreationInputTokens;
|
|
17
|
+
let totalTokens;
|
|
18
|
+
const add = (acc, value) => value === undefined ? acc : (acc ?? 0) + value;
|
|
19
|
+
for (const entry of providerUsage) {
|
|
20
|
+
inputTokens = add(inputTokens, entry.inputTokens);
|
|
21
|
+
outputTokens = add(outputTokens, entry.outputTokens);
|
|
22
|
+
cacheReadInputTokens = add(cacheReadInputTokens, entry.cacheReadInputTokens);
|
|
23
|
+
cacheCreationInputTokens = add(cacheCreationInputTokens, entry.cacheCreationInputTokens);
|
|
24
|
+
totalTokens = add(totalTokens, entry.totalTokens);
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
...(inputTokens !== undefined ? { inputTokens } : {}),
|
|
28
|
+
...(outputTokens !== undefined ? { outputTokens } : {}),
|
|
29
|
+
...(cacheReadInputTokens !== undefined ? { cacheReadInputTokens } : {}),
|
|
30
|
+
...(cacheCreationInputTokens !== undefined ? { cacheCreationInputTokens } : {}),
|
|
31
|
+
...(totalTokens !== undefined ? { totalTokens } : {})
|
|
32
|
+
};
|
|
33
|
+
}
|
|
2
34
|
//# sourceMappingURL=runtime-types.js.map
|