@aexhq/sdk 0.46.5-canary → 0.51.0
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/LICENSE +201 -0
- package/NOTICE +38 -0
- package/README.md +54 -29
- package/dist/client/aex.d.ts +33 -0
- package/dist/client/aex.js +108 -0
- package/dist/client/aex.js.map +1 -0
- package/dist/client/credentials.d.ts +25 -0
- package/dist/client/credentials.js +97 -0
- package/dist/client/credentials.js.map +1 -0
- package/dist/client/routing.d.ts +7 -0
- package/dist/client/routing.js +29 -0
- package/dist/client/routing.js.map +1 -0
- package/dist/downloads/download.d.ts +18 -0
- package/dist/downloads/download.js +67 -0
- package/dist/downloads/download.js.map +1 -0
- package/dist/generated/errors.d.ts +12 -0
- package/dist/generated/errors.js +82 -0
- package/dist/generated/errors.js.map +1 -0
- package/dist/generated/models.d.ts +1392 -0
- package/dist/generated/models.js +62 -0
- package/dist/generated/models.js.map +1 -0
- package/dist/generated/resources.d.ts +710 -0
- package/dist/generated/resources.js +582 -0
- package/dist/generated/resources.js.map +1 -0
- package/dist/generated/routes.d.ts +44 -0
- package/dist/generated/routes.js +1905 -0
- package/dist/generated/routes.js.map +1 -0
- package/dist/index.d.ts +21 -50
- package/dist/index.js +14 -62
- package/dist/index.js.map +1 -1
- package/dist/node/session-file-limits.d.ts +4 -0
- package/dist/node/session-file-limits.js +7 -0
- package/dist/node/session-file-limits.js.map +1 -0
- package/dist/node/session-files.d.ts +80 -0
- package/dist/node/session-files.js +254 -0
- package/dist/node/session-files.js.map +1 -0
- package/dist/observations/stream.d.ts +1 -0
- package/dist/observations/stream.js +18 -0
- package/dist/observations/stream.js.map +1 -0
- package/dist/transport/errors.d.ts +60 -0
- package/dist/transport/errors.js +107 -0
- package/dist/transport/errors.js.map +1 -0
- package/dist/transport/pagination.d.ts +8 -0
- package/dist/transport/pagination.js +34 -0
- package/dist/transport/pagination.js.map +1 -0
- package/dist/transport/retry.d.ts +21 -0
- package/dist/transport/retry.js +37 -0
- package/dist/transport/retry.js.map +1 -0
- package/dist/transport/transport.d.ts +25 -0
- package/dist/transport/transport.js +33 -0
- package/dist/transport/transport.js.map +1 -0
- package/package.json +67 -30
- package/dist/_contracts/account-operations.d.ts +0 -101
- package/dist/_contracts/account-operations.js +0 -242
- package/dist/_contracts/account-types.d.ts +0 -461
- package/dist/_contracts/account-types.js +0 -1
- package/dist/_contracts/api-key.d.ts +0 -61
- package/dist/_contracts/api-key.js +0 -101
- package/dist/_contracts/api-routes.d.ts +0 -20
- package/dist/_contracts/api-routes.js +0 -109
- package/dist/_contracts/archive-limits.d.ts +0 -3
- package/dist/_contracts/archive-limits.js +0 -23
- package/dist/_contracts/asset-authoring.d.ts +0 -22
- package/dist/_contracts/asset-authoring.js +0 -106
- package/dist/_contracts/asset-bundle.d.ts +0 -64
- package/dist/_contracts/asset-bundle.js +0 -263
- package/dist/_contracts/asset-upload-helper.d.ts +0 -31
- package/dist/_contracts/asset-upload-helper.js +0 -84
- package/dist/_contracts/billing-admission.d.ts +0 -29
- package/dist/_contracts/billing-admission.js +0 -28
- package/dist/_contracts/bundle-manifest.d.ts +0 -89
- package/dist/_contracts/bundle-manifest.js +0 -158
- package/dist/_contracts/canonical-sha256.d.ts +0 -8
- package/dist/_contracts/canonical-sha256.js +0 -8
- package/dist/_contracts/connection-ticket.d.ts +0 -22
- package/dist/_contracts/connection-ticket.js +0 -54
- package/dist/_contracts/continuation-event.d.ts +0 -31
- package/dist/_contracts/continuation-event.js +0 -6
- package/dist/_contracts/contract-parse-error.d.ts +0 -12
- package/dist/_contracts/contract-parse-error.js +0 -51
- package/dist/_contracts/error-codes.d.ts +0 -26
- package/dist/_contracts/error-codes.js +0 -116
- package/dist/_contracts/error-factory.d.ts +0 -32
- package/dist/_contracts/error-factory.js +0 -174
- package/dist/_contracts/event-envelope.d.ts +0 -471
- package/dist/_contracts/event-envelope.js +0 -501
- package/dist/_contracts/event-stream-client.d.ts +0 -122
- package/dist/_contracts/event-stream-client.js +0 -445
- package/dist/_contracts/event-view.d.ts +0 -44
- package/dist/_contracts/event-view.js +0 -69
- package/dist/_contracts/failure-class.d.ts +0 -29
- package/dist/_contracts/failure-class.js +0 -73
- package/dist/_contracts/http.d.ts +0 -135
- package/dist/_contracts/http.js +0 -434
- package/dist/_contracts/ids.d.ts +0 -66
- package/dist/_contracts/ids.js +0 -119
- package/dist/_contracts/index.d.ts +0 -42
- package/dist/_contracts/index.js +0 -52
- package/dist/_contracts/internal.d.ts +0 -55
- package/dist/_contracts/internal.js +0 -113
- package/dist/_contracts/models.d.ts +0 -30
- package/dist/_contracts/models.js +0 -28
- package/dist/_contracts/operation-core.d.ts +0 -36
- package/dist/_contracts/operation-core.js +0 -70
- package/dist/_contracts/operations.d.ts +0 -218
- package/dist/_contracts/operations.js +0 -1496
- package/dist/_contracts/otlp-projection.d.ts +0 -78
- package/dist/_contracts/otlp-projection.js +0 -171
- package/dist/_contracts/post-hook.d.ts +0 -31
- package/dist/_contracts/post-hook.js +0 -61
- package/dist/_contracts/provider-fault.d.ts +0 -34
- package/dist/_contracts/provider-fault.js +0 -68
- package/dist/_contracts/retry-core.d.ts +0 -29
- package/dist/_contracts/retry-core.js +0 -79
- package/dist/_contracts/runner-event.d.ts +0 -117
- package/dist/_contracts/runner-event.js +0 -172
- package/dist/_contracts/runtime-kind.d.ts +0 -60
- package/dist/_contracts/runtime-kind.js +0 -70
- package/dist/_contracts/runtime-manifest.d.ts +0 -121
- package/dist/_contracts/runtime-manifest.js +0 -83
- package/dist/_contracts/runtime-security-profile.d.ts +0 -26
- package/dist/_contracts/runtime-security-profile.js +0 -73
- package/dist/_contracts/runtime-sizes.d.ts +0 -104
- package/dist/_contracts/runtime-sizes.js +0 -111
- package/dist/_contracts/runtime-types.d.ts +0 -618
- package/dist/_contracts/runtime-types.js +0 -58
- package/dist/_contracts/schemas/asset-bundle.d.ts +0 -70
- package/dist/_contracts/schemas/asset-bundle.js +0 -107
- package/dist/_contracts/schemas/asset-ref.d.ts +0 -61
- package/dist/_contracts/schemas/asset-ref.js +0 -118
- package/dist/_contracts/schemas/bundle-manifest.d.ts +0 -66
- package/dist/_contracts/schemas/bundle-manifest.js +0 -77
- package/dist/_contracts/schemas/index.d.ts +0 -32
- package/dist/_contracts/schemas/index.js +0 -30
- package/dist/_contracts/schemas/mcp-server.d.ts +0 -99
- package/dist/_contracts/schemas/mcp-server.js +0 -209
- package/dist/_contracts/schemas/models.d.ts +0 -29
- package/dist/_contracts/schemas/models.js +0 -51
- package/dist/_contracts/schemas/numeric.d.ts +0 -18
- package/dist/_contracts/schemas/numeric.js +0 -28
- package/dist/_contracts/schemas/post-hook.d.ts +0 -45
- package/dist/_contracts/schemas/post-hook.js +0 -68
- package/dist/_contracts/schemas/response-assets.d.ts +0 -75
- package/dist/_contracts/schemas/response-assets.js +0 -81
- package/dist/_contracts/schemas/response-billing.d.ts +0 -208
- package/dist/_contracts/schemas/response-billing.js +0 -139
- package/dist/_contracts/schemas/response-common.d.ts +0 -132
- package/dist/_contracts/schemas/response-common.js +0 -162
- package/dist/_contracts/schemas/response-identity.d.ts +0 -648
- package/dist/_contracts/schemas/response-identity.js +0 -131
- package/dist/_contracts/schemas/response-mcp-servers.d.ts +0 -51
- package/dist/_contracts/schemas/response-mcp-servers.js +0 -32
- package/dist/_contracts/schemas/response-secrets.d.ts +0 -50
- package/dist/_contracts/schemas/response-secrets.js +0 -32
- package/dist/_contracts/schemas/response-sessions-internal.d.ts +0 -200
- package/dist/_contracts/schemas/response-sessions-internal.js +0 -142
- package/dist/_contracts/schemas/response-sessions.d.ts +0 -1598
- package/dist/_contracts/schemas/response-sessions.js +0 -377
- package/dist/_contracts/schemas/response-webhooks.d.ts +0 -76
- package/dist/_contracts/schemas/response-webhooks.js +0 -42
- package/dist/_contracts/schemas/response-workspace.d.ts +0 -225
- package/dist/_contracts/schemas/response-workspace.js +0 -99
- package/dist/_contracts/schemas/runtime-kind.d.ts +0 -31
- package/dist/_contracts/schemas/runtime-kind.js +0 -29
- package/dist/_contracts/schemas/runtime-security-profile.d.ts +0 -28
- package/dist/_contracts/schemas/runtime-security-profile.js +0 -26
- package/dist/_contracts/schemas/runtime-sizes.d.ts +0 -70
- package/dist/_contracts/schemas/runtime-sizes.js +0 -127
- package/dist/_contracts/schemas/session-limits.d.ts +0 -34
- package/dist/_contracts/schemas/session-limits.js +0 -39
- package/dist/_contracts/schemas/session-machine.d.ts +0 -23
- package/dist/_contracts/schemas/session-machine.js +0 -24
- package/dist/_contracts/schemas/session-request-config.d.ts +0 -58
- package/dist/_contracts/schemas/session-request-config.js +0 -134
- package/dist/_contracts/schemas/session-webhook.d.ts +0 -11
- package/dist/_contracts/schemas/session-webhook.js +0 -38
- package/dist/_contracts/schemas/side-effect-audit.d.ts +0 -98
- package/dist/_contracts/schemas/side-effect-audit.js +0 -102
- package/dist/_contracts/schemas/submission-assets.d.ts +0 -117
- package/dist/_contracts/schemas/submission-assets.js +0 -147
- package/dist/_contracts/schemas/submission-body.d.ts +0 -251
- package/dist/_contracts/schemas/submission-body.js +0 -378
- package/dist/_contracts/schemas/submission-environment.d.ts +0 -79
- package/dist/_contracts/schemas/submission-environment.js +0 -179
- package/dist/_contracts/schemas/submission-request.d.ts +0 -158
- package/dist/_contracts/schemas/submission-request.js +0 -49
- package/dist/_contracts/schemas/submission-secrets.d.ts +0 -47
- package/dist/_contracts/schemas/submission-secrets.js +0 -108
- package/dist/_contracts/schemas/wire.d.ts +0 -118
- package/dist/_contracts/schemas/wire.js +0 -171
- package/dist/_contracts/schemas/workspace-resources.d.ts +0 -50
- package/dist/_contracts/schemas/workspace-resources.js +0 -87
- package/dist/_contracts/sdk-errors.d.ts +0 -212
- package/dist/_contracts/sdk-errors.js +0 -313
- package/dist/_contracts/sdk-secrets.d.ts +0 -67
- package/dist/_contracts/sdk-secrets.js +0 -427
- package/dist/_contracts/session-archive.d.ts +0 -16
- package/dist/_contracts/session-archive.js +0 -92
- package/dist/_contracts/session-artifacts.d.ts +0 -189
- package/dist/_contracts/session-artifacts.js +0 -264
- package/dist/_contracts/session-config.d.ts +0 -373
- package/dist/_contracts/session-config.js +0 -562
- package/dist/_contracts/session-cost-types.d.ts +0 -211
- package/dist/_contracts/session-cost-types.js +0 -69
- package/dist/_contracts/session-cost.d.ts +0 -8
- package/dist/_contracts/session-cost.js +0 -582
- package/dist/_contracts/session-custody.d.ts +0 -165
- package/dist/_contracts/session-custody.js +0 -345
- package/dist/_contracts/session-file-query.d.ts +0 -14
- package/dist/_contracts/session-file-query.js +0 -178
- package/dist/_contracts/session-record.d.ts +0 -112
- package/dist/_contracts/session-record.js +0 -165
- package/dist/_contracts/session-retention.d.ts +0 -201
- package/dist/_contracts/session-retention.js +0 -450
- package/dist/_contracts/side-effect-audit.d.ts +0 -126
- package/dist/_contracts/side-effect-audit.js +0 -520
- package/dist/_contracts/sse.d.ts +0 -74
- package/dist/_contracts/sse.js +0 -227
- package/dist/_contracts/stable.d.ts +0 -45
- package/dist/_contracts/stable.js +0 -62
- package/dist/_contracts/status.d.ts +0 -25
- package/dist/_contracts/status.js +0 -57
- package/dist/_contracts/submission-limits.d.ts +0 -61
- package/dist/_contracts/submission-limits.js +0 -60
- package/dist/_contracts/submission.d.ts +0 -547
- package/dist/_contracts/submission.js +0 -812
- package/dist/_contracts/suggest.d.ts +0 -15
- package/dist/_contracts/suggest.js +0 -53
- package/dist/_contracts/testing/response-bindings.d.ts +0 -45
- package/dist/_contracts/testing/response-bindings.js +0 -256
- package/dist/_contracts/testing/wire-conformance-entry.d.ts +0 -10
- package/dist/_contracts/testing/wire-conformance-entry.js +0 -8
- package/dist/_contracts/testing/wire-conformance.d.ts +0 -169
- package/dist/_contracts/testing/wire-conformance.js +0 -276
- package/dist/_contracts/turn-trace.d.ts +0 -28
- package/dist/_contracts/turn-trace.js +0 -1
- package/dist/_contracts/unknown-field-error.d.ts +0 -13
- package/dist/_contracts/unknown-field-error.js +0 -21
- package/dist/_contracts/value-guards.d.ts +0 -20
- package/dist/_contracts/value-guards.js +0 -34
- package/dist/_contracts/webhook-verify.d.ts +0 -34
- package/dist/_contracts/webhook-verify.js +0 -93
- package/dist/_contracts/wire-observer.d.ts +0 -49
- package/dist/_contracts/wire-observer.js +0 -34
- package/dist/_contracts/workflow-status.d.ts +0 -7
- package/dist/_contracts/workflow-status.js +0 -43
- package/dist/_contracts/workspace-resources.d.ts +0 -98
- package/dist/_contracts/workspace-resources.js +0 -39
- package/dist/archive-limits.d.ts +0 -1
- package/dist/archive-limits.js +0 -2
- package/dist/archive-limits.js.map +0 -1
- package/dist/asset-upload.d.ts +0 -47
- package/dist/asset-upload.js +0 -269
- package/dist/asset-upload.js.map +0 -1
- package/dist/bundle.d.ts +0 -9
- package/dist/bundle.js +0 -20
- package/dist/bundle.js.map +0 -1
- package/dist/canonical-zip.d.ts +0 -68
- package/dist/canonical-zip.js +0 -355
- package/dist/canonical-zip.js.map +0 -1
- package/dist/cli.mjs +0 -12048
- package/dist/cli.mjs.sha256 +0 -1
- package/dist/client-types.d.ts +0 -192
- package/dist/client-types.js +0 -2
- package/dist/client-types.js.map +0 -1
- package/dist/client.d.ts +0 -464
- package/dist/client.js +0 -1207
- package/dist/client.js.map +0 -1
- package/dist/event-projection.d.ts +0 -22
- package/dist/event-projection.js +0 -380
- package/dist/event-projection.js.map +0 -1
- package/dist/fetch-archive.d.ts +0 -16
- package/dist/fetch-archive.js +0 -252
- package/dist/fetch-archive.js.map +0 -1
- package/dist/file.d.ts +0 -96
- package/dist/file.js +0 -272
- package/dist/file.js.map +0 -1
- package/dist/instructions.d.ts +0 -20
- package/dist/instructions.js +0 -40
- package/dist/instructions.js.map +0 -1
- package/dist/legacy-session-provider-fault.d.ts +0 -7
- package/dist/legacy-session-provider-fault.js +0 -38
- package/dist/legacy-session-provider-fault.js.map +0 -1
- package/dist/mcp-server.d.ts +0 -84
- package/dist/mcp-server.js +0 -117
- package/dist/mcp-server.js.map +0 -1
- package/dist/node-fs.d.ts +0 -29
- package/dist/node-fs.js +0 -19
- package/dist/node-fs.js.map +0 -1
- package/dist/node-walk.d.ts +0 -69
- package/dist/node-walk.js +0 -151
- package/dist/node-walk.js.map +0 -1
- package/dist/path-basename.d.ts +0 -5
- package/dist/path-basename.js +0 -9
- package/dist/path-basename.js.map +0 -1
- package/dist/retry.d.ts +0 -70
- package/dist/retry.js +0 -155
- package/dist/retry.js.map +0 -1
- package/dist/secret.d.ts +0 -65
- package/dist/secret.js +0 -110
- package/dist/secret.js.map +0 -1
- package/dist/session-validate.d.ts +0 -100
- package/dist/session-validate.js +0 -303
- package/dist/session-validate.js.map +0 -1
- package/dist/skill.d.ts +0 -99
- package/dist/skill.js +0 -169
- package/dist/skill.js.map +0 -1
- package/dist/submission-wire.d.ts +0 -13
- package/dist/submission-wire.js +0 -69
- package/dist/submission-wire.js.map +0 -1
- package/dist/tool.d.ts +0 -41
- package/dist/tool.js +0 -76
- package/dist/tool.js.map +0 -1
- package/dist/version.d.ts +0 -9
- package/dist/version.js +0 -10
- package/dist/version.js.map +0 -1
- package/docs/authentication.md +0 -125
- package/docs/billing.md +0 -164
- package/docs/cleanup.md +0 -27
- package/docs/concepts/agent-tools.md +0 -47
- package/docs/concepts/composition.md +0 -60
- package/docs/concepts/providers-and-runtimes.md +0 -121
- package/docs/concepts/sessions.md +0 -51
- package/docs/concepts/subagents.md +0 -35
- package/docs/credentials.md +0 -116
- package/docs/defaults.md +0 -51
- package/docs/errors.md +0 -258
- package/docs/events.md +0 -143
- package/docs/files.md +0 -130
- package/docs/limits-and-quotas.md +0 -114
- package/docs/limits.md +0 -51
- package/docs/mcp.md +0 -47
- package/docs/networking.md +0 -114
- package/docs/provider-runtime-capabilities.md +0 -32
- package/docs/public-surface.json +0 -73
- package/docs/quickstart.md +0 -135
- package/docs/release.md +0 -44
- package/docs/retries.md +0 -108
- package/docs/secrets.md +0 -141
- package/docs/session-config.md +0 -51
- package/docs/session-record.md +0 -58
- package/docs/skills.md +0 -65
- package/docs/telemetry.md +0 -66
- package/docs/testing.md +0 -35
- package/docs/vision-skills.md +0 -94
- package/docs/webhooks.md +0 -143
package/dist/client.js
DELETED
|
@@ -1,1207 +0,0 @@
|
|
|
1
|
-
import { CredentialValidationError, HttpClient, PLANE_BASE_URLS, SessionStateError, SecretString, asAexEventView, asAexStreamEventView, isReplayableEvent, tryParseApiKey, parseModelSlug, resolveBuiltinToolNames, streamCoordinatorEvents, parseSessionLimits, parseApprovalGate, parseResponseFormat, parseRuntimeSize, parseRuntimeKind, parseSessionTimeout, parseSessionWebhook } from "./_contracts/index.js";
|
|
2
|
-
import { hasRunTerminalType, operations } from "./_contracts/internal.js";
|
|
3
|
-
import { uploadAsset, uploadAssetMultipart } from "./asset-upload.js";
|
|
4
|
-
import { AexRateLimitError, isThrottleFault, resolveRetryConfig } from "./retry.js";
|
|
5
|
-
import { legacySessionProviderFault } from "./legacy-session-provider-fault.js";
|
|
6
|
-
import { splitSecretEnv } from "./secret.js";
|
|
7
|
-
import { assertRunCheckpoint, assertSessionCommittedAfterRun, buildTurnResult, isSessionRunTerminalEvent, messageFromWire, PROGRESSING_SESSION_STATUSES, projectAssistantMessages, terminalCheckpointId, terminalSessionStatusFromEvents, turnTraceFromEvents } from "./event-projection.js";
|
|
8
|
-
import { assertStartSessionOptions, assertSupportedSessionFields, assertSupportedSessionSendOptions, configError, normaliseSessionInput, validatedSessionConfig } from "./session-validate.js";
|
|
9
|
-
import { fileCaptureForWire, mergeMcpServers, sessionEnvironmentForWire, sessionRetentionForWire } from "./submission-wire.js";
|
|
10
|
-
export class SessionRunStream {
|
|
11
|
-
#stream;
|
|
12
|
-
#events = [];
|
|
13
|
-
#waiters = new Set();
|
|
14
|
-
#outcome;
|
|
15
|
-
#pump;
|
|
16
|
-
#hasIterator = false;
|
|
17
|
-
constructor(stream) {
|
|
18
|
-
this.#stream = stream;
|
|
19
|
-
}
|
|
20
|
-
#start() {
|
|
21
|
-
this.#pump ??= this.#drain();
|
|
22
|
-
return this.#pump;
|
|
23
|
-
}
|
|
24
|
-
async #drain() {
|
|
25
|
-
const generator = this.#stream();
|
|
26
|
-
try {
|
|
27
|
-
let next = await generator.next();
|
|
28
|
-
while (!next.done) {
|
|
29
|
-
this.#events.push(next.value);
|
|
30
|
-
this.#notify();
|
|
31
|
-
next = await generator.next();
|
|
32
|
-
}
|
|
33
|
-
this.#outcome = { ok: true, value: next.value };
|
|
34
|
-
this.#notify();
|
|
35
|
-
return next.value;
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
this.#outcome = { ok: false, error };
|
|
39
|
-
this.#notify();
|
|
40
|
-
throw error;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
#notify() {
|
|
44
|
-
for (const resolve of this.#waiters)
|
|
45
|
-
resolve();
|
|
46
|
-
this.#waiters.clear();
|
|
47
|
-
}
|
|
48
|
-
[Symbol.asyncIterator]() {
|
|
49
|
-
let cursor = this.#hasIterator ? this.#events.length : 0;
|
|
50
|
-
this.#hasIterator = true;
|
|
51
|
-
void this.#start().catch(() => { });
|
|
52
|
-
// Deliberately do NOT forward `return()`: `break`-ing out of a
|
|
53
|
-
// `for await` loop must not close the in-flight turn — `finished()` can
|
|
54
|
-
// still drain it to completion afterwards.
|
|
55
|
-
return {
|
|
56
|
-
next: async () => {
|
|
57
|
-
while (true) {
|
|
58
|
-
if (cursor < this.#events.length) {
|
|
59
|
-
return { done: false, value: this.#events[cursor++] };
|
|
60
|
-
}
|
|
61
|
-
if (this.#outcome !== undefined) {
|
|
62
|
-
if (!this.#outcome.ok)
|
|
63
|
-
throw this.#outcome.error;
|
|
64
|
-
return { done: true, value: undefined };
|
|
65
|
-
}
|
|
66
|
-
await new Promise((resolve) => this.#waiters.add(resolve));
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
return: async () => ({ done: true, value: undefined })
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
finished() {
|
|
73
|
-
return this.#start();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const internalSessionSenders = new WeakMap();
|
|
77
|
-
function sendSessionInternal(session, input, options = {}) {
|
|
78
|
-
const sender = internalSessionSenders.get(session);
|
|
79
|
-
if (sender === undefined) {
|
|
80
|
-
throw new Error("Aex: invalid session handle");
|
|
81
|
-
}
|
|
82
|
-
return sender(normaliseSessionInput(input, "session.messages.send", "input"), options);
|
|
83
|
-
}
|
|
84
|
-
export class SessionHandle {
|
|
85
|
-
#http;
|
|
86
|
-
#fetch;
|
|
87
|
-
#session;
|
|
88
|
-
messages;
|
|
89
|
-
events;
|
|
90
|
-
otel;
|
|
91
|
-
files;
|
|
92
|
-
webhooks;
|
|
93
|
-
/** The last message sent on this handle, for `session.messages.replayLast()`. */
|
|
94
|
-
#lastSend;
|
|
95
|
-
constructor(http, session, fetch) {
|
|
96
|
-
this.#http = http;
|
|
97
|
-
this.#session = session;
|
|
98
|
-
this.#fetch = fetch;
|
|
99
|
-
const id = session.id;
|
|
100
|
-
this.messages = sessionMessages(http, id, (input, options = {}) => {
|
|
101
|
-
assertSupportedSessionSendOptions(options, "session.messages.send");
|
|
102
|
-
return sendSessionInternal(this, input, options);
|
|
103
|
-
}, (options = {}) => {
|
|
104
|
-
assertSupportedSessionSendOptions(options, "session.messages.replayLast");
|
|
105
|
-
const last = this.#lastSend;
|
|
106
|
-
if (last === undefined) {
|
|
107
|
-
throw new SessionStateError("session.messages.replayLast: no message has been sent on this session yet");
|
|
108
|
-
}
|
|
109
|
-
return sendSessionInternal(this, last.input, {
|
|
110
|
-
...options,
|
|
111
|
-
idempotencyKey: options.idempotencyKey ?? last.idempotencyKey
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
this.events = sessionEvents(http, id);
|
|
115
|
-
this.otel = sessionOtel(http, id);
|
|
116
|
-
this.files = sessionFiles(http, id, fetch);
|
|
117
|
-
this.webhooks = {
|
|
118
|
-
list: () => operations.getSessionWebhookDeliveries(http, id),
|
|
119
|
-
redeliver: (deliveryId) => operations.redeliverSessionWebhook(http, id, deliveryId)
|
|
120
|
-
};
|
|
121
|
-
internalSessionSenders.set(this, (input, options = {}) => new SessionRunStream(() => this.#send(input, options)));
|
|
122
|
-
}
|
|
123
|
-
get id() {
|
|
124
|
-
return this.#session.id;
|
|
125
|
-
}
|
|
126
|
-
get record() {
|
|
127
|
-
return this.#session;
|
|
128
|
-
}
|
|
129
|
-
async *#send(input, options) {
|
|
130
|
-
const idempotencyKey = operations.resolveIdempotencyKey(options.idempotencyKey);
|
|
131
|
-
this.#lastSend = { input, idempotencyKey };
|
|
132
|
-
const accepted = await operations.sendSessionMessage(this.#http, this.id, { input }, { idempotencyKey });
|
|
133
|
-
this.#session = accepted.session;
|
|
134
|
-
const run = accepted.run;
|
|
135
|
-
const eventCursor = accepted.eventCursor ?? run.eventCursor ?? 0;
|
|
136
|
-
const streamEvents = [];
|
|
137
|
-
for await (const event of streamSessionRunEvents(this.#http, this.id, run, {
|
|
138
|
-
...options,
|
|
139
|
-
from: eventCursor
|
|
140
|
-
})) {
|
|
141
|
-
streamEvents.push(event);
|
|
142
|
-
yield event;
|
|
143
|
-
}
|
|
144
|
-
const events = streamEvents.filter(isDurableEventView);
|
|
145
|
-
// RUN_FINISHED/RUN_ERROR is the consistency barrier: all reads below must
|
|
146
|
-
// already observe the same committed run and checkpoint.
|
|
147
|
-
const read = terminalSessionStatusFromEvents(events, run.runId);
|
|
148
|
-
this.#session = await operations.getSession(this.#http, this.id);
|
|
149
|
-
assertSessionCommittedAfterRun(this.#session, run, read);
|
|
150
|
-
const checkpointId = terminalCheckpointId(events, run.runId);
|
|
151
|
-
const snapshot = checkpointId === undefined
|
|
152
|
-
? undefined
|
|
153
|
-
: await operations.listSessionFiles(this.#http, this.id, { checkpointId });
|
|
154
|
-
if (snapshot !== undefined) {
|
|
155
|
-
assertRunCheckpoint(events, run.runId, snapshot.revision);
|
|
156
|
-
}
|
|
157
|
-
const messages = projectAssistantMessages(events);
|
|
158
|
-
return buildTurnResult(this.id, this.#session, run, events, snapshot?.files ?? [], snapshot?.revision, messages, read);
|
|
159
|
-
}
|
|
160
|
-
async suspend() {
|
|
161
|
-
const accepted = await operations.suspendSession(this.#http, this.id);
|
|
162
|
-
this.#session = accepted.session;
|
|
163
|
-
return accepted;
|
|
164
|
-
}
|
|
165
|
-
async cancel() {
|
|
166
|
-
const accepted = await operations.cancelSession(this.#http, this.id);
|
|
167
|
-
this.#session = accepted.session;
|
|
168
|
-
return accepted;
|
|
169
|
-
}
|
|
170
|
-
async resume() {
|
|
171
|
-
const accepted = await operations.resumeSession(this.#http, this.id);
|
|
172
|
-
this.#session = accepted.session;
|
|
173
|
-
return accepted;
|
|
174
|
-
}
|
|
175
|
-
async delete() {
|
|
176
|
-
const accepted = await operations.deleteSession(this.#http, this.id);
|
|
177
|
-
if (accepted && typeof accepted === "object" && "session" in accepted) {
|
|
178
|
-
this.#session = accepted.session;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Request the HITL write-gate: park this session `awaiting_approval` before
|
|
183
|
-
* its next gated action. Imperative counterpart to the declarative
|
|
184
|
-
* `approvalGate` submission option. Resume with {@link approve} / reject with
|
|
185
|
-
* {@link deny}.
|
|
186
|
-
*/
|
|
187
|
-
async requestApproval() {
|
|
188
|
-
const accepted = await operations.requestApproval(this.#http, this.id);
|
|
189
|
-
this.#session = accepted.session;
|
|
190
|
-
return accepted;
|
|
191
|
-
}
|
|
192
|
-
/** Approve an `awaiting_approval` session so the held turn resumes (→ running). */
|
|
193
|
-
async approve() {
|
|
194
|
-
const accepted = await operations.approveSession(this.#http, this.id);
|
|
195
|
-
this.#session = accepted.session;
|
|
196
|
-
return accepted;
|
|
197
|
-
}
|
|
198
|
-
/** Deny an `awaiting_approval` session; its run is cancelled and the session returns to `idle`. */
|
|
199
|
-
async deny() {
|
|
200
|
-
const accepted = await operations.denySession(this.#http, this.id);
|
|
201
|
-
this.#session = accepted.session;
|
|
202
|
-
return accepted;
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Enumerate this session's subagent CHILD sessions (`GET /api/sessions/:id/children`). Each is
|
|
206
|
-
* a read-only {@link ChildSessionHandle} with observable events, checkpointed
|
|
207
|
-
* files, descendants, lineage, and lifecycle state.
|
|
208
|
-
*/
|
|
209
|
-
async children() {
|
|
210
|
-
const refs = await operations.listSessionChildren(this.#http, this.id);
|
|
211
|
-
return refs.map((ref) => new ChildSessionHandle(this.#http, ref, this.#fetch));
|
|
212
|
-
}
|
|
213
|
-
/** Re-read the session record from the server and store it as the current record. */
|
|
214
|
-
async refresh() {
|
|
215
|
-
this.#session = await operations.getSession(this.#http, this.id);
|
|
216
|
-
return this.#session;
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Download EVERYTHING public about this session as one zip, assembled
|
|
220
|
-
* client-side from the public read endpoints. Organised into `metadata/`,
|
|
221
|
-
* `events/`, and `files/` folders, plus a `manifest.json`. Pass `to` to
|
|
222
|
-
* also write the bytes to a file path while still returning them.
|
|
223
|
-
*/
|
|
224
|
-
async download(options) {
|
|
225
|
-
return writeOptionalFile(await operations.download(this.#http, this.id), options?.to);
|
|
226
|
-
}
|
|
227
|
-
/** Download only the session record (the `metadata` namespace) as a zip. */
|
|
228
|
-
async downloadMetadata(options) {
|
|
229
|
-
return writeOptionalFile(await operations.downloadMetadata(this.#http, this.id), options?.to);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
export class SessionClient {
|
|
233
|
-
#http;
|
|
234
|
-
#fetch;
|
|
235
|
-
#buildCreateRequest;
|
|
236
|
-
constructor(http, buildCreateRequest, fetch) {
|
|
237
|
-
this.#http = http;
|
|
238
|
-
this.#buildCreateRequest = buildCreateRequest;
|
|
239
|
-
this.#fetch = fetch;
|
|
240
|
-
}
|
|
241
|
-
async create(options) {
|
|
242
|
-
const request = await this.#buildCreateRequest(options);
|
|
243
|
-
const session = await operations.createSession(this.#http, request, { idempotencyKey: operations.resolveIdempotencyKey(options.idempotencyKey) });
|
|
244
|
-
return new SessionHandle(this.#http, session, this.#fetch);
|
|
245
|
-
}
|
|
246
|
-
async open(sessionId) {
|
|
247
|
-
return new SessionHandle(this.#http, await operations.getSession(this.#http, sessionId), this.#fetch);
|
|
248
|
-
}
|
|
249
|
-
get(sessionId) {
|
|
250
|
-
return operations.getSession(this.#http, sessionId);
|
|
251
|
-
}
|
|
252
|
-
async delete(sessionId) {
|
|
253
|
-
await operations.deleteSession(this.#http, sessionId);
|
|
254
|
-
}
|
|
255
|
-
list(query) {
|
|
256
|
-
return operations.listSessions(this.#http, query);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Read-only observation handle for a lineage-discoverable subagent child.
|
|
261
|
-
* Child ids address events, checkpointed files, and descendants. They are not
|
|
262
|
-
* top-level resumable sessions, so this handle deliberately has no get, send,
|
|
263
|
-
* cancel, suspend, resume, or delete controls.
|
|
264
|
-
*/
|
|
265
|
-
export class ChildSessionHandle {
|
|
266
|
-
#http;
|
|
267
|
-
#fetch;
|
|
268
|
-
#ref;
|
|
269
|
-
events;
|
|
270
|
-
files;
|
|
271
|
-
constructor(http, ref, fetch) {
|
|
272
|
-
this.#http = http;
|
|
273
|
-
this.#ref = ref;
|
|
274
|
-
this.#fetch = fetch;
|
|
275
|
-
this.events = childSessionEventsAccessor(http, ref);
|
|
276
|
-
this.files = sessionFiles(http, ref.id, fetch);
|
|
277
|
-
}
|
|
278
|
-
get id() {
|
|
279
|
-
return this.#ref.id;
|
|
280
|
-
}
|
|
281
|
-
get parentSessionId() {
|
|
282
|
-
return this.#ref.parentSessionId;
|
|
283
|
-
}
|
|
284
|
-
get depth() {
|
|
285
|
-
return this.#ref.depth;
|
|
286
|
-
}
|
|
287
|
-
/** The lifecycle status captured by the parent lineage read. */
|
|
288
|
-
get status() {
|
|
289
|
-
return this.#ref.status;
|
|
290
|
-
}
|
|
291
|
-
get ref() {
|
|
292
|
-
return this.#ref;
|
|
293
|
-
}
|
|
294
|
-
/** This child's own subagent children (recursive lineage). */
|
|
295
|
-
async children() {
|
|
296
|
-
const refs = await operations.listSessionChildren(this.#http, this.id);
|
|
297
|
-
return refs.map((ref) => new ChildSessionHandle(this.#http, ref, this.#fetch));
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
/** Session-record events accessor (list + polling stream) used by {@link ChildSessionHandle}. */
|
|
301
|
-
function childSessionEventsAccessor(http, ref) {
|
|
302
|
-
return {
|
|
303
|
-
iterate: (options) => iterateSessionEventViews(http, ref.id, options ?? {}),
|
|
304
|
-
list: async () => (await operations.listSessionEvents(http, ref.id)).map(asAexEventView),
|
|
305
|
-
stream: (options) => streamChildSessionEventsPolling(http, ref, options ?? {})
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
async function* iterateSessionEventViews(http, id, options) {
|
|
309
|
-
for await (const event of operations.iterateSessionEvents(http, id, options)) {
|
|
310
|
-
yield asAexEventView(event);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
/** One snapshot-polling engine shared by root and child session event streams. */
|
|
314
|
-
async function* pollSessionEventViews(http, id, options, resolvePolling) {
|
|
315
|
-
const from = validateStreamEventsFrom(options.from);
|
|
316
|
-
if (options.signal?.aborted)
|
|
317
|
-
return;
|
|
318
|
-
const seenIds = new Set();
|
|
319
|
-
const intervalMs = options.intervalMs ?? 1_000;
|
|
320
|
-
const signal = options.signal;
|
|
321
|
-
const { boundedRunlessSnapshot, isTerminal } = await resolvePolling(from);
|
|
322
|
-
while (!signal?.aborted) {
|
|
323
|
-
const events = await operations.listSessionEvents(http, id);
|
|
324
|
-
let terminalSeen = false;
|
|
325
|
-
for (const event of events) {
|
|
326
|
-
if (event.sequence >= from && !seenIds.has(event.id)) {
|
|
327
|
-
seenIds.add(event.id);
|
|
328
|
-
yield asAexEventView(event);
|
|
329
|
-
}
|
|
330
|
-
if (isTerminal(event))
|
|
331
|
-
terminalSeen = true;
|
|
332
|
-
}
|
|
333
|
-
if (terminalSeen || boundedRunlessSnapshot)
|
|
334
|
-
return;
|
|
335
|
-
// `sleep` rejects on abort — treat that as a graceful stop.
|
|
336
|
-
try {
|
|
337
|
-
await sleep(intervalMs, signal);
|
|
338
|
-
}
|
|
339
|
-
catch {
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Poll a child session's events until a committed RUN terminal is visible, the
|
|
346
|
-
* signal aborts, or the caller breaks the iterator.
|
|
347
|
-
*/
|
|
348
|
-
function streamChildSessionEventsPolling(http, ref, options) {
|
|
349
|
-
return pollSessionEventViews(http, ref.id, options, (from) => {
|
|
350
|
-
const progressing = PROGRESSING_SESSION_STATUSES.has(ref.status);
|
|
351
|
-
const targetRunId = progressing ? undefined : ref.lastRun?.runId;
|
|
352
|
-
const priorRunId = progressing ? ref.lastRun?.runId : undefined;
|
|
353
|
-
return {
|
|
354
|
-
boundedRunlessSnapshot: !progressing && targetRunId === undefined,
|
|
355
|
-
isTerminal: targetRunId !== undefined
|
|
356
|
-
? (event) => isSessionRunTerminalEvent(event, targetRunId)
|
|
357
|
-
: (event) => hasRunTerminalType(event) && event.sequence >= from && event.runId !== priorRunId
|
|
358
|
-
};
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
async function* streamSessionRunEvents(http, sessionId, run, options) {
|
|
362
|
-
const first = await operations.getSessionCoordinatorTicket(http, sessionId);
|
|
363
|
-
for await (const event of streamCoordinatorEvents({
|
|
364
|
-
wsUrl: first.wsUrl,
|
|
365
|
-
from: options.from ?? 0,
|
|
366
|
-
fetchTicket: async () => (await operations.getSessionCoordinatorTicket(http, sessionId)).ticket,
|
|
367
|
-
isTerminal: (event) => isSessionRunTerminalEvent(event, run.runId),
|
|
368
|
-
...(options.signal ? { signal: options.signal } : {}),
|
|
369
|
-
...(options.webSocketFactory ? { webSocketFactory: options.webSocketFactory } : {}),
|
|
370
|
-
...(options.idleTimeoutMs !== undefined ? { idleTimeoutMs: options.idleTimeoutMs } : {}),
|
|
371
|
-
...(options.pingIntervalMs !== undefined ? { pingIntervalMs: options.pingIntervalMs } : {})
|
|
372
|
-
})) {
|
|
373
|
-
if (event.runId !== run.runId)
|
|
374
|
-
continue;
|
|
375
|
-
yield asAexStreamEventView(event);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function isDurableEventView(event) {
|
|
379
|
-
return isReplayableEvent(event);
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Poll the session's event snapshots until a durable run terminal, the signal
|
|
383
|
-
* aborts, or the caller breaks the iterator, deduping by event id. Yields the
|
|
384
|
-
* one canonical guard-bearing {@link AexEventView} (same shape as every other
|
|
385
|
-
* event surface). Module-level so `SessionHandle.events` can hand it to its
|
|
386
|
-
* accessor object literal.
|
|
387
|
-
*/
|
|
388
|
-
function streamSessionEventsPolling(http, id, options) {
|
|
389
|
-
return pollSessionEventViews(http, id, options, async (from) => {
|
|
390
|
-
const initial = await operations.getSession(http, id);
|
|
391
|
-
const targetRunId = initial.currentRun?.runId ?? initial.lastRun?.runId;
|
|
392
|
-
return {
|
|
393
|
-
boundedRunlessSnapshot: targetRunId === undefined && !PROGRESSING_SESSION_STATUSES.has(initial.status),
|
|
394
|
-
isTerminal: targetRunId === undefined
|
|
395
|
-
? (event) => hasRunTerminalType(event) && event.sequence >= from
|
|
396
|
-
: (event) => isSessionRunTerminalEvent(event, targetRunId)
|
|
397
|
-
};
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Stream the unified {@link AexEvent} envelope live over the session's
|
|
402
|
-
* coordinator WebSocket. The ticket is re-minted on each (re)connect so a long
|
|
403
|
-
* session never outlives it. Module-level so `SessionHandle.events` can hand
|
|
404
|
-
* it to its accessor object literal.
|
|
405
|
-
*/
|
|
406
|
-
async function* streamSessionEnvelopes(http, id, options) {
|
|
407
|
-
const first = await operations.getSessionCoordinatorTicket(http, id);
|
|
408
|
-
for await (const event of streamCoordinatorEvents({
|
|
409
|
-
wsUrl: first.wsUrl,
|
|
410
|
-
from: options.from ?? 0,
|
|
411
|
-
fetchTicket: async () => (await operations.getSessionCoordinatorTicket(http, id)).ticket,
|
|
412
|
-
isTerminal: hasRunTerminalType,
|
|
413
|
-
...(options.signal ? { signal: options.signal } : {}),
|
|
414
|
-
...(options.idleTimeoutMs !== undefined ? { idleTimeoutMs: options.idleTimeoutMs } : {}),
|
|
415
|
-
...(options.pingIntervalMs !== undefined ? { pingIntervalMs: options.pingIntervalMs } : {}),
|
|
416
|
-
...(options.eventQuietRecheckMs !== undefined ? { eventQuietRecheckMs: options.eventQuietRecheckMs } : {})
|
|
417
|
-
})) {
|
|
418
|
-
yield asAexStreamEventView(event);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Download captured files. No selector → the full files namespace as a
|
|
423
|
-
* zip; a selector → one file's raw bytes. Module-level so
|
|
424
|
-
* `SessionHandle.files` can hand it to its accessor object literal.
|
|
425
|
-
*/
|
|
426
|
-
async function downloadSessionFile(http, id, selector, options) {
|
|
427
|
-
// One selector-resolution path: contracts resolve every selector against the
|
|
428
|
-
// authoritative checkpoint, then download and verify the committed bytes.
|
|
429
|
-
const transferOptions = {
|
|
430
|
-
...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
431
|
-
...(options?.checkpointId !== undefined ? { checkpointId: options.checkpointId } : {})
|
|
432
|
-
};
|
|
433
|
-
const bytes = selector === undefined
|
|
434
|
-
? await operations.downloadSessionFiles(http, id, transferOptions)
|
|
435
|
-
: (await operations.downloadSessionFile(http, id, selector, transferOptions)).bytes;
|
|
436
|
-
return writeOptionalFile(bytes, options?.to);
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Build the files accessor for a session id. Shared by
|
|
440
|
-
* `SessionHandle.files` and child-session handles, so both expose the identical
|
|
441
|
-
* rich {@link SessionFiles} surface.
|
|
442
|
-
*/
|
|
443
|
-
function sessionFiles(http, id, fetchLike) {
|
|
444
|
-
const list = (query) => operations.listSessionFiles(http, id, query);
|
|
445
|
-
return {
|
|
446
|
-
list,
|
|
447
|
-
last: async () => (await list()).files.at(-1),
|
|
448
|
-
first: async () => (await list()).files[0],
|
|
449
|
-
read: (selector, options) => operations.readSessionFileText(http, id, selector, options),
|
|
450
|
-
find: (query) => operations.findSessionFiles(http, id, query),
|
|
451
|
-
findOne: (query) => operations.findSessionFile(http, id, query),
|
|
452
|
-
link: (selectorOrQuery, options) => operations.sessionFileLink(http, id, selectorOrQuery, options),
|
|
453
|
-
fetch: async (selectorOrQuery, options) => {
|
|
454
|
-
const link = await operations.sessionFileLink(http, id, selectorOrQuery, options);
|
|
455
|
-
return (fetchLike ?? globalThis.fetch)(link.url);
|
|
456
|
-
},
|
|
457
|
-
download: (selector, options) => downloadSessionFile(http, id, selector, options)
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
function sessionMessages(http, id, send, replayLast) {
|
|
461
|
-
const list = () => listAllSessionMessages(http, id);
|
|
462
|
-
return {
|
|
463
|
-
send,
|
|
464
|
-
replayLast,
|
|
465
|
-
list,
|
|
466
|
-
last: async () => (await list()).at(-1),
|
|
467
|
-
first: async () => (await list())[0]
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
const LIST_MESSAGES_PAGE_BUDGET = 1000;
|
|
471
|
-
async function listAllSessionMessages(http, id) {
|
|
472
|
-
const messages = [];
|
|
473
|
-
const seenCursors = new Set();
|
|
474
|
-
let cursor;
|
|
475
|
-
for (let pageIndex = 0; pageIndex < LIST_MESSAGES_PAGE_BUDGET; pageIndex += 1) {
|
|
476
|
-
const page = await operations.listSessionMessages(http, id, cursor === undefined ? undefined : { cursor });
|
|
477
|
-
messages.push(...page.messages.map(messageFromWire));
|
|
478
|
-
if (page.nextCursor === undefined)
|
|
479
|
-
return messages;
|
|
480
|
-
if (typeof page.nextCursor !== "string" || page.nextCursor.length === 0) {
|
|
481
|
-
throw new SessionStateError("session messages response contains an invalid nextCursor", { sessionId: id });
|
|
482
|
-
}
|
|
483
|
-
if (seenCursors.has(page.nextCursor)) {
|
|
484
|
-
throw new SessionStateError("session messages pagination repeated a cursor", {
|
|
485
|
-
sessionId: id,
|
|
486
|
-
cursor: page.nextCursor
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
seenCursors.add(page.nextCursor);
|
|
490
|
-
cursor = page.nextCursor;
|
|
491
|
-
}
|
|
492
|
-
throw new SessionStateError("session messages pagination exceeded its page budget", {
|
|
493
|
-
sessionId: id,
|
|
494
|
-
pageBudget: LIST_MESSAGES_PAGE_BUDGET
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
function sessionEvents(http, id) {
|
|
498
|
-
const iterate = (options) => iterateSessionEventViews(http, id, options ?? {});
|
|
499
|
-
const list = async () => (await operations.listSessionEvents(http, id)).map(asAexEventView);
|
|
500
|
-
return {
|
|
501
|
-
iterate,
|
|
502
|
-
list,
|
|
503
|
-
last: async () => {
|
|
504
|
-
let last;
|
|
505
|
-
for await (const event of iterate())
|
|
506
|
-
last = event;
|
|
507
|
-
return last;
|
|
508
|
-
},
|
|
509
|
-
first: async () => {
|
|
510
|
-
for await (const event of iterate())
|
|
511
|
-
return event;
|
|
512
|
-
return undefined;
|
|
513
|
-
},
|
|
514
|
-
stream: (options) => streamSessionEventsPolling(http, id, options ?? {}),
|
|
515
|
-
streamEnvelopes: (options) => streamSessionEnvelopes(http, id, options ?? {}),
|
|
516
|
-
archiveLink: (options) => operations.eventArchiveLink(http, id, options),
|
|
517
|
-
download: async (options) => writeOptionalFile(await operations.downloadEvents(http, id), options?.to)
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
function sessionOtel(http, id) {
|
|
521
|
-
return {
|
|
522
|
-
traces: () => operations.iterateSessionOtlpPages(http, id, "traces"),
|
|
523
|
-
logs: () => operations.iterateSessionOtlpPages(http, id, "logs")
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
function validateStreamEventsFrom(value) {
|
|
527
|
-
if (value === undefined)
|
|
528
|
-
return 0;
|
|
529
|
-
if (!Number.isSafeInteger(value) || value < 0) {
|
|
530
|
-
throw configError("session.events.stream", "from", "from must be a non-negative safe integer");
|
|
531
|
-
}
|
|
532
|
-
return value;
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Workspace secret management exposed under `client.workspace.secrets`.
|
|
536
|
-
*
|
|
537
|
-
* Lifecycle parity with assets: a `Secret.value(...)` is per-session and
|
|
538
|
-
* gone at terminal; `set` persists a named, searchable workspace secret you can `get` (metadata),
|
|
539
|
-
* `rotate`, `list`, and `delete`. The identity is the `name`; the value rotates
|
|
540
|
-
* under that stable name.
|
|
541
|
-
*
|
|
542
|
-
* Values are write-only through the public SDK: `set`/`rotate` send the value in
|
|
543
|
-
* the request BODY (never the URL); `get`/`list` return metadata only.
|
|
544
|
-
*/
|
|
545
|
-
export class SecretsClient {
|
|
546
|
-
#http;
|
|
547
|
-
constructor(http) {
|
|
548
|
-
this.#http = http;
|
|
549
|
-
}
|
|
550
|
-
/** Create a named workspace secret. Accepts a raw string or a `SecretString`. */
|
|
551
|
-
set(args) {
|
|
552
|
-
return operations.createSecret(this.#http, { name: args.name, value: unwrapSecretValue(args.value) });
|
|
553
|
-
}
|
|
554
|
-
/** List workspace secret metadata (searchable by name). Never returns values. */
|
|
555
|
-
list() {
|
|
556
|
-
return operations.listSecrets(this.#http);
|
|
557
|
-
}
|
|
558
|
-
/** Metadata for one workspace secret by name. Never returns the value. */
|
|
559
|
-
get(name) {
|
|
560
|
-
return operations.getSecret(this.#http, name);
|
|
561
|
-
}
|
|
562
|
-
/** Replace the value of an existing workspace secret; bumps its version. */
|
|
563
|
-
rotate(args) {
|
|
564
|
-
return operations.rotateSecret(this.#http, { name: args.name, value: unwrapSecretValue(args.value) });
|
|
565
|
-
}
|
|
566
|
-
delete(name) {
|
|
567
|
-
return operations.deleteSecret(this.#http, name);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
/** Accept a raw string or a `SecretString`; return the raw value for the wire. */
|
|
571
|
-
function unwrapSecretValue(value) {
|
|
572
|
-
const raw = value instanceof SecretString ? value.unwrap() : value;
|
|
573
|
-
if (typeof raw !== "string" || !raw) {
|
|
574
|
-
throw new Error("secrets: value must be a non-empty string");
|
|
575
|
-
}
|
|
576
|
-
return raw;
|
|
577
|
-
}
|
|
578
|
-
function wrapNewWorkspace(wire) {
|
|
579
|
-
return {
|
|
580
|
-
workspaceId: wire.workspaceId,
|
|
581
|
-
apiKey: new SecretString(wire.apiKey, "workspace api key"),
|
|
582
|
-
...(typeof wire.slug === "string" ? { slug: wire.slug } : {}),
|
|
583
|
-
...(typeof wire.orgId === "string" ? { orgId: wire.orgId } : {})
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
function wrapNewApiKey(wire) {
|
|
587
|
-
return {
|
|
588
|
-
id: wire.id,
|
|
589
|
-
apiKey: new SecretString(wire.apiKey, "api key"),
|
|
590
|
-
...(typeof wire.name === "string" ? { name: wire.name } : {}),
|
|
591
|
-
...(typeof wire.kind === "string" ? { kind: wire.kind } : {}),
|
|
592
|
-
...(typeof wire.workspaceId === "string" ? { workspaceId: wire.workspaceId } : {}),
|
|
593
|
-
...(Array.isArray(wire.scopes) ? { scopes: wire.scopes } : {})
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* Control-plane management of the ORGS the account principal belongs to. Reached
|
|
598
|
-
* with an account credential (PAT / device session), NOT a data-plane workspace
|
|
599
|
-
* key. An org owns workspaces and is the billing / roles / cap boundary.
|
|
600
|
-
*
|
|
601
|
-
* Naming: `client.orgs` (plural) is a collection across the account, mirroring
|
|
602
|
-
* `client.sessions` / `client.workspaces`.
|
|
603
|
-
*/
|
|
604
|
-
export class OrgsClient {
|
|
605
|
-
#http;
|
|
606
|
-
constructor(http) {
|
|
607
|
-
this.#http = http;
|
|
608
|
-
}
|
|
609
|
-
/** Create an org; the caller becomes its admin. */
|
|
610
|
-
create(args) {
|
|
611
|
-
return operations.createOrg(this.#http, args);
|
|
612
|
-
}
|
|
613
|
-
/** List the orgs the caller belongs to. */
|
|
614
|
-
list() {
|
|
615
|
-
return operations.listOrgs(this.#http);
|
|
616
|
-
}
|
|
617
|
-
/** List an org's members (pending invites appear with `status: "pending"`). */
|
|
618
|
-
members(orgId) {
|
|
619
|
-
return operations.listOrgMembers(this.#http, orgId);
|
|
620
|
-
}
|
|
621
|
-
/** Invite an email to the org at a role (`member` by default). */
|
|
622
|
-
invite(orgId, args) {
|
|
623
|
-
return operations.createOrgInvite(this.#http, orgId, args);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Control-plane management of WORKSPACES across the account's orgs. This is the
|
|
628
|
-
* PLURAL collection (`client.workspaces`) — distinct from the SINGULAR
|
|
629
|
-
* `client.workspace`, which is the data-plane context bound to the current key
|
|
630
|
-
* (its files/skills/tools/instructions/secrets). Creating a workspace returns
|
|
631
|
-
* its first workspace-scoped key ONCE.
|
|
632
|
-
*/
|
|
633
|
-
export class WorkspacesClient {
|
|
634
|
-
#http;
|
|
635
|
-
constructor(http) {
|
|
636
|
-
this.#http = http;
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Create a workspace under an org and reveal its first workspace-scoped API
|
|
640
|
-
* key ONCE (wrapped in a redacted {@link SecretString}). The free tier caps at
|
|
641
|
-
* 3 workspaces per org.
|
|
642
|
-
*/
|
|
643
|
-
async create(args) {
|
|
644
|
-
return wrapNewWorkspace(await operations.createWorkspace(this.#http, args));
|
|
645
|
-
}
|
|
646
|
-
/** List the workspaces the caller can manage across their orgs. */
|
|
647
|
-
list() {
|
|
648
|
-
return operations.listWorkspaces(this.#http);
|
|
649
|
-
}
|
|
650
|
-
/** Delete a workspace by id. Idempotent. */
|
|
651
|
-
delete(workspaceId) {
|
|
652
|
-
return operations.deleteWorkspace(this.#http, workspaceId);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Control-plane management of API KEYS — both data-plane workspace keys and
|
|
657
|
-
* account PATs. Creating a key reveals its value ONCE (wrapped in a redacted
|
|
658
|
-
* {@link SecretString}); list/delete operate on metadata only.
|
|
659
|
-
*/
|
|
660
|
-
export class KeysClient {
|
|
661
|
-
#http;
|
|
662
|
-
constructor(http) {
|
|
663
|
-
this.#http = http;
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Mint an API key and reveal its value ONCE. Pass `workspaceId` for a
|
|
667
|
-
* data-plane workspace key, or `account: true` for an account PAT. A PAT
|
|
668
|
-
* cannot mint another PAT (anti-escalation, enforced server-side).
|
|
669
|
-
*/
|
|
670
|
-
async create(args = {}) {
|
|
671
|
-
return wrapNewApiKey(await operations.createApiKey(this.#http, args));
|
|
672
|
-
}
|
|
673
|
-
/** List API keys (metadata only; never values). */
|
|
674
|
-
list() {
|
|
675
|
-
return operations.listApiKeys(this.#http);
|
|
676
|
-
}
|
|
677
|
-
/** Revoke/delete an API key by id. Idempotent. */
|
|
678
|
-
delete(keyId) {
|
|
679
|
-
return operations.deleteApiKey(this.#http, keyId);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
|
-
* Unified user-facing client for aex. The same class powers the published
|
|
684
|
-
* `@aexhq/sdk` SDK and, under the hood, the bundled `aex` CLI. All remote
|
|
685
|
-
* operations use the hosted aex API and operate on durable session records.
|
|
686
|
-
*
|
|
687
|
-
* The SDK never asks the caller for a workspace id — workspace identity
|
|
688
|
-
* is derived server-side from the API key on every request. Use
|
|
689
|
-
* `client.whoami()` if you want to introspect which workspace the
|
|
690
|
-
* token resolves to.
|
|
691
|
-
*/
|
|
692
|
-
export class Aex {
|
|
693
|
-
#http;
|
|
694
|
-
#debug;
|
|
695
|
-
/** The same fetch the HttpClient uses, threaded into direct asset uploads. */
|
|
696
|
-
#fetch;
|
|
697
|
-
#assetRetry;
|
|
698
|
-
/**
|
|
699
|
-
* The SINGULAR data-plane context bound to the current key: this workspace's
|
|
700
|
-
* files, skills, tools, instructions, and secrets. (Contrast {@link workspaces},
|
|
701
|
-
* the plural control-plane collection.)
|
|
702
|
-
*/
|
|
703
|
-
workspace;
|
|
704
|
-
sessions;
|
|
705
|
-
/** Control-plane: the orgs the account principal belongs to (roles/members/invites). */
|
|
706
|
-
orgs;
|
|
707
|
-
/**
|
|
708
|
-
* Control-plane: manage WORKSPACES across your orgs (create/list/delete). The
|
|
709
|
-
* PLURAL collection — not to be confused with {@link workspace} (singular), the
|
|
710
|
-
* data-plane context of the current key.
|
|
711
|
-
*/
|
|
712
|
-
workspaces;
|
|
713
|
-
/** Control-plane: manage API keys — workspace keys and account PATs (create/list/delete). */
|
|
714
|
-
keys;
|
|
715
|
-
constructor(options, overrides = {}) {
|
|
716
|
-
const resolved = typeof options === "string" ? { ...overrides, apiKey: options } : options;
|
|
717
|
-
const apiKey = resolved.apiKey;
|
|
718
|
-
if (!apiKey) {
|
|
719
|
-
// Typed so a caller catching AexError (the SDK's error base) catches a
|
|
720
|
-
// missing credential too, instead of a bare Error slipping the taxonomy.
|
|
721
|
-
throw new CredentialValidationError("Aex: apiKey is required");
|
|
722
|
-
}
|
|
723
|
-
// Self-describing key ⇒ plane-aware routing, checked ZERO-network in the
|
|
724
|
-
// constructor: derive the baseUrl from the key's plane when omitted, and
|
|
725
|
-
// fail fast on a plane/baseUrl mismatch instead of a bare 401 after a full
|
|
726
|
-
// round-trip. A non-self-describing key (no `aex_` shape) skips this and keeps
|
|
727
|
-
// the HttpClient default.
|
|
728
|
-
const baseUrl = resolveBaseUrlForKey(apiKey, resolved.baseUrl);
|
|
729
|
-
// The transport applies the ONE shared retry policy — `HTTP_RETRY_POLICY`
|
|
730
|
-
// from `@aexhq/contracts`, the same object the `aex` CLI hands its own
|
|
731
|
-
// HttpClient — so an `aex` command and an SDK call react identically to a
|
|
732
|
-
// 429. The raw `#fetch` below stays unwrapped for object-storage traffic.
|
|
733
|
-
// Asset uploads apply the same resolved policy in their transfer helper;
|
|
734
|
-
// `session.files.fetch()` intentionally returns the raw one-shot response.
|
|
735
|
-
const baseFetch = resolved.fetch ?? ((input, init) => fetch(input, init));
|
|
736
|
-
this.#debug = resolved.debug
|
|
737
|
-
? typeof resolved.debug === "function"
|
|
738
|
-
? resolved.debug
|
|
739
|
-
: (line) => console.error(line)
|
|
740
|
-
: undefined;
|
|
741
|
-
this.#assetRetry = resolved.retry === false
|
|
742
|
-
? { maxAttempts: 1 }
|
|
743
|
-
: resolveRetryConfig(resolved.retry);
|
|
744
|
-
this.#http = new HttpClient({
|
|
745
|
-
...(baseUrl ? { baseUrl } : {}),
|
|
746
|
-
apiKey,
|
|
747
|
-
fetch: baseFetch,
|
|
748
|
-
retry: resolved.retry === false ? false : resolveRetryConfig(resolved.retry),
|
|
749
|
-
// Opt-in local diagnostics: emit a redacted per-request trace to
|
|
750
|
-
// stderr. Uploads nothing. A caller wanting a custom sink can pass
|
|
751
|
-
// a function instead of `true`.
|
|
752
|
-
...(this.#debug ? { debug: this.#debug } : {})
|
|
753
|
-
});
|
|
754
|
-
this.#fetch = resolved.fetch;
|
|
755
|
-
this.workspace = new WorkspaceClient(this.#http, {
|
|
756
|
-
upload: (args) => this.#uploadAsset(args),
|
|
757
|
-
uploadStream: (args) => this.#uploadAssetStream(args)
|
|
758
|
-
});
|
|
759
|
-
this.sessions = new SessionClient(this.#http, (options) => this.#buildSessionCreateRequest(options), this.#fetch);
|
|
760
|
-
this.orgs = new OrgsClient(this.#http);
|
|
761
|
-
this.workspaces = new WorkspacesClient(this.#http);
|
|
762
|
-
this.keys = new KeysClient(this.#http);
|
|
763
|
-
}
|
|
764
|
-
/**
|
|
765
|
-
* Internal: materialize raw bytes to the content-addressable asset store
|
|
766
|
-
* (`/api/assets/presign` -> PUT -> `/api/assets/finalize`). Used by the session-create
|
|
767
|
-
* prepare step to upload draft skill / tool / instructions / file bundles so
|
|
768
|
-
* the wire submission carries only plain `kind:"asset"` refs (skills resolve
|
|
769
|
-
* to name-only `kind:"skill"` refs after their bytes upload).
|
|
770
|
-
* NOT part of the public API.
|
|
771
|
-
*/
|
|
772
|
-
async #uploadAsset(args) {
|
|
773
|
-
return uploadAsset({
|
|
774
|
-
http: this.#http,
|
|
775
|
-
bytes: args.bytes,
|
|
776
|
-
hash: args.hash,
|
|
777
|
-
...(args.contentType ? { contentType: args.contentType } : {}),
|
|
778
|
-
retry: this.#assetRetry,
|
|
779
|
-
...(this.#fetch ? { fetch: this.#fetch } : {})
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* Internal: materialize a LARGE draft (a `File.fromPath` over the streaming
|
|
784
|
-
* threshold) to the content store via the two-pass streaming multipart flow —
|
|
785
|
-
* hash the deterministic canonical-zip stream, presign by hash (dedup still
|
|
786
|
-
* short-circuits), then upload it in parts. Bounded memory (one entry + one
|
|
787
|
-
* part). NOT part of the public API.
|
|
788
|
-
*/
|
|
789
|
-
async #uploadAssetStream(args) {
|
|
790
|
-
return uploadAssetMultipart({
|
|
791
|
-
http: this.#http,
|
|
792
|
-
drive: args.drive,
|
|
793
|
-
...(args.contentType ? { contentType: args.contentType } : {}),
|
|
794
|
-
retry: this.#assetRetry,
|
|
795
|
-
...(this.#fetch ? { fetch: this.#fetch } : {})
|
|
796
|
-
});
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Convenience one-shot on top of the canonical session API:
|
|
800
|
-
* open a session, send `message` as the first run, stream through its durable
|
|
801
|
-
* terminal event, then return the collected text,
|
|
802
|
-
* events, files, and session record. The returned `sessionId` is the session id,
|
|
803
|
-
* so callers can resume later with `aex.sessions.open(sessionId)`.
|
|
804
|
-
*/
|
|
805
|
-
async start(options, opts = {}) {
|
|
806
|
-
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
807
|
-
throw configError("Aex.start", "options", "options are required");
|
|
808
|
-
}
|
|
809
|
-
assertStartSessionOptions(opts, "Aex.start");
|
|
810
|
-
const scopedSignal = scopedAbortSignal(opts.timeoutMs);
|
|
811
|
-
try {
|
|
812
|
-
const { message, deleteAfter, messageIdempotencyKey, stream, ...createOptions } = options;
|
|
813
|
-
assertSupportedSessionFields(options, "Aex.start", true);
|
|
814
|
-
const input = normaliseSessionInput(message, "Aex.start", "message");
|
|
815
|
-
assertSupportedSessionSendOptions(stream, "Aex.start stream", false);
|
|
816
|
-
const sendOptions = {
|
|
817
|
-
...(stream ?? {}),
|
|
818
|
-
...(scopedSignal?.signal ? { signal: scopedSignal.signal } : {}),
|
|
819
|
-
...(opts.webSocketFactory ? { webSocketFactory: opts.webSocketFactory } : {}),
|
|
820
|
-
...(opts.idleTimeoutMs !== undefined ? { idleTimeoutMs: opts.idleTimeoutMs } : {}),
|
|
821
|
-
...(opts.pingIntervalMs !== undefined ? { pingIntervalMs: opts.pingIntervalMs } : {})
|
|
822
|
-
};
|
|
823
|
-
// Derive the message key from the create key (like the CLI) so a retried
|
|
824
|
-
// session with the same `idempotencyKey` de-duplicates BOTH the create and the
|
|
825
|
-
// billable turn server-side — never a duplicate billable session turn (sdk-dx-3).
|
|
826
|
-
const createKey = operations.resolveIdempotencyKey(createOptions.idempotencyKey);
|
|
827
|
-
const messageKey = messageIdempotencyKey !== undefined
|
|
828
|
-
? operations.resolveIdempotencyKey(messageIdempotencyKey)
|
|
829
|
-
: operations.deriveMessageIdempotencyKey(createKey);
|
|
830
|
-
const session = await this.sessions.create({ ...createOptions, idempotencyKey: createKey });
|
|
831
|
-
// One terminal boundary: RUN_FINISHED/RUN_ERROR carries the run outcome,
|
|
832
|
-
// cost, and usage. `start()` only reshapes the same finished result.
|
|
833
|
-
let turnResult;
|
|
834
|
-
try {
|
|
835
|
-
turnResult = await sendSessionInternal(session, input, { ...sendOptions, idempotencyKey: messageKey }).finished();
|
|
836
|
-
}
|
|
837
|
-
catch (err) {
|
|
838
|
-
if (scopedSignal?.signal.aborted) {
|
|
839
|
-
// The client-side wait budget expired. Throw rather than returning a
|
|
840
|
-
// misleading partial result; the session continues server-side.
|
|
841
|
-
throw new SessionStateError(`Aex.start: timed out after ${opts.timeoutMs}ms waiting for run completion in session ${session.id}; the session ` +
|
|
842
|
-
`continues server-side — cancel via session.cancel() or reopen with aex.sessions.open(${JSON.stringify(session.id)})`);
|
|
843
|
-
}
|
|
844
|
-
throw err;
|
|
845
|
-
}
|
|
846
|
-
const sessionId = turnResult.sessionId;
|
|
847
|
-
if (deleteAfter) {
|
|
848
|
-
await session.delete();
|
|
849
|
-
}
|
|
850
|
-
const sessionState = turnResult.session;
|
|
851
|
-
const trace = turnTraceFromEvents(turnResult.events);
|
|
852
|
-
const outcome = turnResult.outcome;
|
|
853
|
-
const { outcome: _untypedOutcome, ...baseResult } = turnResult;
|
|
854
|
-
const result = {
|
|
855
|
-
...baseResult,
|
|
856
|
-
trace,
|
|
857
|
-
...(outcome !== undefined ? { outcome } : {})
|
|
858
|
-
};
|
|
859
|
-
if (opts.throwOnFailure && !turnResult.ok) {
|
|
860
|
-
// A turn that failed because the upstream provider throttled us surfaces
|
|
861
|
-
// as a structured, non-leaky AexRateLimitError carrying the provider
|
|
862
|
-
// fault, so callers can branch on `isRateLimited(err)` and replay.
|
|
863
|
-
const throttle = throttleFromSession(sessionState, this.#debug);
|
|
864
|
-
if (throttle) {
|
|
865
|
-
throw new AexRateLimitError({
|
|
866
|
-
status: throttle.status ?? 429,
|
|
867
|
-
attempts: 1,
|
|
868
|
-
source: "provider",
|
|
869
|
-
providerFault: throttle,
|
|
870
|
-
...(throttle.retryAfterMs !== undefined ? { retryAfterMs: throttle.retryAfterMs } : {})
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
throw new SessionStateError(`Aex.start: session ${sessionId} ended ${turnResult.status}${turnResult.error ? `: ${turnResult.error}` : ""}`, { sessionId, status: turnResult.status });
|
|
874
|
-
}
|
|
875
|
-
return result;
|
|
876
|
-
}
|
|
877
|
-
finally {
|
|
878
|
-
scopedSignal?.clear();
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
async #buildSessionCreateRequest(options) {
|
|
882
|
-
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
883
|
-
throw configError("aex.sessions.create", "options", "options are required");
|
|
884
|
-
}
|
|
885
|
-
assertSupportedSessionFields(options, "aex.sessions.create", false);
|
|
886
|
-
// Model is REQUIRED and validated as a gateway `creator/model` slug string.
|
|
887
|
-
// This is a structural boundary gate only — a well-formed slug the gateway
|
|
888
|
-
// catalog doesn't yet serve is allowed through (the server arbitrates), so a
|
|
889
|
-
// slightly-old SDK can still run a newly-launched model.
|
|
890
|
-
if (typeof options.model !== "string" || !options.model) {
|
|
891
|
-
throw configError("aex.sessions.create", "model", "model is required");
|
|
892
|
-
}
|
|
893
|
-
const selectedModel = options.model;
|
|
894
|
-
validatedSessionConfig("aex.sessions.create", "model", "model must be a gateway model slug of the form \"creator/model\"", () => parseModelSlug(selectedModel), { kind: "scalar", rejectedValues: [selectedModel] });
|
|
895
|
-
// Fast client-side validation via the contract parsers (the SSoT). runtimeSize
|
|
896
|
-
// and timeout are STABLE closed sets whose invalid values the create endpoint
|
|
897
|
-
// otherwise SILENTLY defaults (no error ever — pre-launch edge-sweep F11/F12);
|
|
898
|
-
// reject them synchronously with a typed error instead. webhook shape is
|
|
899
|
-
// re-checked here for a fast local fail (the server enforces it too). Model is
|
|
900
|
-
// deliberately NOT hard-rejected here to preserve forward-compat with models
|
|
901
|
-
// added server-side before an SDK upgrade (an unknown model still fails on the
|
|
902
|
-
// server).
|
|
903
|
-
const runtimeSize = options.runtime?.size;
|
|
904
|
-
validatedSessionConfig("aex.sessions.create", "runtime.size", "runtime.size must be a supported size preset", () => parseRuntimeSize(runtimeSize), { kind: "scalar", rejectedValues: [runtimeSize] });
|
|
905
|
-
const runtimeKind = options.runtime?.kind;
|
|
906
|
-
validatedSessionConfig("aex.sessions.create", "runtime.kind", "runtime.kind must be one of: container, spot_container, lambda", () => parseRuntimeKind(runtimeKind), { kind: "scalar", rejectedValues: [runtimeKind] });
|
|
907
|
-
const sessionTimeout = options.overrides?.timeout;
|
|
908
|
-
validatedSessionConfig("aex.sessions.create", "overrides.timeout", "overrides.timeout must be a supported duration", () => parseSessionTimeout(sessionTimeout), { kind: "scalar", rejectedValues: [sessionTimeout] });
|
|
909
|
-
if (options.webhook !== undefined) {
|
|
910
|
-
validatedSessionConfig("aex.sessions.create", "webhook.url", "webhook.url must be a valid HTTPS URL", () => parseSessionWebhook(options.webhook), { kind: "redacted" });
|
|
911
|
-
}
|
|
912
|
-
if (options.responseFormat !== undefined) {
|
|
913
|
-
validatedSessionConfig("aex.sessions.create", "responseFormat", "responseFormat is invalid", () => parseResponseFormat(options.responseFormat), { kind: "redacted" });
|
|
914
|
-
}
|
|
915
|
-
if (options.approvalGate !== undefined) {
|
|
916
|
-
validatedSessionConfig("aex.sessions.create", "approvalGate", "approvalGate is invalid", () => parseApprovalGate(options.approvalGate), { kind: "redacted" });
|
|
917
|
-
}
|
|
918
|
-
const splitEnvironmentSecrets = validatedSessionConfig("aex.sessions.create", "environment.secrets", "environment.secrets is invalid", () => splitSecretEnv(options.environment?.secrets), { kind: "redacted" });
|
|
919
|
-
const secretEnvDeclarations = splitEnvironmentSecrets.declarations;
|
|
920
|
-
const envSecretValues = splitEnvironmentSecrets.values;
|
|
921
|
-
let limits;
|
|
922
|
-
const limitsInput = {};
|
|
923
|
-
if (options.overrides?.maxSpendUsd !== undefined) {
|
|
924
|
-
const maxSpendUsd = options.overrides.maxSpendUsd;
|
|
925
|
-
validatedSessionConfig("aex.sessions.create", "overrides.maxSpendUsd", "overrides.maxSpendUsd must be valid", () => parseSessionLimits({ maxSpendUsd }), { kind: "scalar", rejectedValues: [maxSpendUsd] });
|
|
926
|
-
limitsInput.maxSpendUsd = maxSpendUsd;
|
|
927
|
-
}
|
|
928
|
-
if (options.overrides?.maxTurns !== undefined) {
|
|
929
|
-
const maxTurns = options.overrides.maxTurns;
|
|
930
|
-
validatedSessionConfig("aex.sessions.create", "overrides.maxTurns", "overrides.maxTurns must be valid", () => parseSessionLimits({ maxTurns }), { kind: "scalar", rejectedValues: [maxTurns] });
|
|
931
|
-
limitsInput.maxTurns = maxTurns;
|
|
932
|
-
}
|
|
933
|
-
limits = parseSessionLimits(Object.keys(limitsInput).length > 0 ? limitsInput : undefined);
|
|
934
|
-
const { submissionMcpServers, mergedMcpSecrets } = mergeMcpServers(options.mcpServers ?? [], []);
|
|
935
|
-
const fileCapture = validatedSessionConfig("aex.sessions.create", "fileCapture", "fileCapture is invalid", () => fileCaptureForWire(options.fileCapture), { kind: "redacted" });
|
|
936
|
-
const environment = validatedSessionConfig("aex.sessions.create", "environment", "environment is invalid", () => sessionEnvironmentForWire(options.environment), { kind: "redacted" });
|
|
937
|
-
let builtinTools = options.builtinTools ?? "default";
|
|
938
|
-
if (Array.isArray(builtinTools)) {
|
|
939
|
-
const selectedBuiltinTools = builtinTools;
|
|
940
|
-
builtinTools = validatedSessionConfig("aex.sessions.create", "builtinTools", "builtinTools contains an unsupported tool name", () => resolveBuiltinToolNames(selectedBuiltinTools), { kind: "scalar", rejectedValues: selectedBuiltinTools });
|
|
941
|
-
}
|
|
942
|
-
const submission = {
|
|
943
|
-
model: options.model,
|
|
944
|
-
...(options.system ? { system: options.system } : {}),
|
|
945
|
-
assets: {
|
|
946
|
-
files: options.assets?.files ?? [],
|
|
947
|
-
skills: options.assets?.skills ?? [],
|
|
948
|
-
tools: options.assets?.tools ?? [],
|
|
949
|
-
instructions: options.assets?.instructions ?? []
|
|
950
|
-
},
|
|
951
|
-
builtinTools,
|
|
952
|
-
mcpServers: submissionMcpServers,
|
|
953
|
-
...(Object.keys(secretEnvDeclarations).length > 0 ? { secretEnv: secretEnvDeclarations } : {}),
|
|
954
|
-
...(environment ? { environment: environment } : {}),
|
|
955
|
-
...(options.metadata ? { metadata: options.metadata } : {}),
|
|
956
|
-
...(fileCapture ? { fileCapture } : {}),
|
|
957
|
-
...(options.outputMode !== undefined ? { outputMode: options.outputMode } : {}),
|
|
958
|
-
...(options.responseFormat !== undefined ? { responseFormat: options.responseFormat } : {}),
|
|
959
|
-
...(options.approvalGate !== undefined ? { approvalGate: options.approvalGate } : {})
|
|
960
|
-
};
|
|
961
|
-
const secrets = {
|
|
962
|
-
...(mergedMcpSecrets.length > 0 ? { mcpServers: mergedMcpSecrets } : {}),
|
|
963
|
-
...(Object.keys(envSecretValues).length > 0 ? { envSecrets: envSecretValues } : {})
|
|
964
|
-
};
|
|
965
|
-
const retention = sessionRetentionForWire(options);
|
|
966
|
-
return {
|
|
967
|
-
submission,
|
|
968
|
-
...(options.runtime?.size ? { runtimeSize: options.runtime.size } : {}),
|
|
969
|
-
...(options.runtime?.kind ? { runtimeKind: options.runtime.kind } : {}),
|
|
970
|
-
...(options.overrides?.timeout ? { timeout: options.overrides.timeout } : {}),
|
|
971
|
-
...(limits ? { limits } : {}),
|
|
972
|
-
retention,
|
|
973
|
-
// Operational/delivery concern — sibling of secrets, NOT part of the
|
|
974
|
-
// hashed submission. Delivered at the committed RUN terminal.
|
|
975
|
-
...(options.webhook ? { webhook: options.webhook } : {}),
|
|
976
|
-
secrets
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
whoami() {
|
|
980
|
-
return operations.whoami(this.#http);
|
|
981
|
-
}
|
|
982
|
-
/** Read prepaid balance, current-period allowances, recharge settings, and card state. */
|
|
983
|
-
billing() {
|
|
984
|
-
return operations.getBilling(this.#http);
|
|
985
|
-
}
|
|
986
|
-
/** Buy prepaid credit through hosted checkout; first use also saves the card. */
|
|
987
|
-
billingTopup(request, options) {
|
|
988
|
-
return operations.createBillingTopupCheckout(this.#http, request, options);
|
|
989
|
-
}
|
|
990
|
-
/** Set the separately consented, off-by-default auto-recharge authority. */
|
|
991
|
-
billingAutoTopup(request) {
|
|
992
|
-
return operations.updateBillingAutoTopup(this.#http, request);
|
|
993
|
-
}
|
|
994
|
-
/** Create a hosted billing-portal session. */
|
|
995
|
-
billingPortal(request = {}, options) {
|
|
996
|
-
return operations.createBillingPortal(this.#http, request, options);
|
|
997
|
-
}
|
|
998
|
-
/** Read recent signed credit-ledger rows, newest first. */
|
|
999
|
-
billingLedger(query) {
|
|
1000
|
-
return operations.getBillingLedger(this.#http, query);
|
|
1001
|
-
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Reveal the workspace webhook signing secret (creating one on first use) —
|
|
1004
|
-
* the `whsec_<base64>` value `verifyAexWebhook` takes as `secret`. Backed by
|
|
1005
|
-
* `POST /api/webhook/signing-secret`; repeat calls return the SAME value (the
|
|
1006
|
-
* hosted API does not rotate it). Treat the reveal as sensitive: never log it.
|
|
1007
|
-
*/
|
|
1008
|
-
webhookSigningSecret() {
|
|
1009
|
-
return operations.getWebhookSigningSecret(this.#http);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Resolve the effective `baseUrl` from a self-describing API key (ZERO-network):
|
|
1014
|
-
* - non-self-describing key: return the caller's `baseUrl`
|
|
1015
|
-
* unchanged (HttpClient falls back to the prd default).
|
|
1016
|
-
* - `baseUrl` omitted: DERIVE it from the key's plane.
|
|
1017
|
-
* - `baseUrl` supplied but its plane DISAGREES with the key's plane: throw
|
|
1018
|
-
* {@link CredentialValidationError} BEFORE any request.
|
|
1019
|
-
*/
|
|
1020
|
-
function resolveBaseUrlForKey(apiKey, baseUrl) {
|
|
1021
|
-
const parsed = tryParseApiKey(apiKey);
|
|
1022
|
-
if (parsed === null)
|
|
1023
|
-
return baseUrl;
|
|
1024
|
-
const planeUrl = PLANE_BASE_URLS[parsed.plane];
|
|
1025
|
-
if (baseUrl === undefined) {
|
|
1026
|
-
return planeUrl;
|
|
1027
|
-
}
|
|
1028
|
-
const canonicalPlane = baseUrl === PLANE_BASE_URLS.dev ? "dev" : baseUrl === PLANE_BASE_URLS.prd ? "prd" : undefined;
|
|
1029
|
-
if (canonicalPlane !== undefined && canonicalPlane !== parsed.plane) {
|
|
1030
|
-
throw new CredentialValidationError(`Aex: this API key is for the ${parsed.plane} plane but baseUrl targets the ${canonicalPlane} plane (${baseUrl}) — ` +
|
|
1031
|
-
`pass the ${parsed.plane} plane baseUrl, or omit baseUrl to auto-route.`, { plane: parsed.plane, baseUrl });
|
|
1032
|
-
}
|
|
1033
|
-
return baseUrl;
|
|
1034
|
-
}
|
|
1035
|
-
function scopedAbortSignal(timeoutMs) {
|
|
1036
|
-
if (timeoutMs === undefined) {
|
|
1037
|
-
return undefined;
|
|
1038
|
-
}
|
|
1039
|
-
const controller = new AbortController();
|
|
1040
|
-
const timer = setTimeout(() => controller.abort(), Math.max(0, timeoutMs));
|
|
1041
|
-
return {
|
|
1042
|
-
signal: controller.signal,
|
|
1043
|
-
clear() {
|
|
1044
|
-
clearTimeout(timer);
|
|
1045
|
-
}
|
|
1046
|
-
};
|
|
1047
|
-
}
|
|
1048
|
-
async function writeOptionalFile(bytes, to) {
|
|
1049
|
-
if (to !== undefined) {
|
|
1050
|
-
const { writeFile } = await import("node:fs/promises");
|
|
1051
|
-
await writeFile(to, bytes);
|
|
1052
|
-
}
|
|
1053
|
-
return bytes;
|
|
1054
|
-
}
|
|
1055
|
-
function sleep(ms, signal) {
|
|
1056
|
-
return new Promise((resolve, reject) => {
|
|
1057
|
-
if (signal?.aborted) {
|
|
1058
|
-
reject(new Error("aborted"));
|
|
1059
|
-
return;
|
|
1060
|
-
}
|
|
1061
|
-
const timer = setTimeout(() => {
|
|
1062
|
-
signal?.removeEventListener("abort", onAbort);
|
|
1063
|
-
resolve();
|
|
1064
|
-
}, ms);
|
|
1065
|
-
const onAbort = () => {
|
|
1066
|
-
clearTimeout(timer);
|
|
1067
|
-
reject(new Error("aborted"));
|
|
1068
|
-
};
|
|
1069
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
/**
|
|
1073
|
-
* Extract a throttle-class {@link ProviderFault} from a failed session record.
|
|
1074
|
-
* Reads the contract-owned structured field. Only when that field is absent,
|
|
1075
|
-
* the narrow historical adapter may recognize an exact legacy terminal.
|
|
1076
|
-
*/
|
|
1077
|
-
function throttleFromSession(session, debug) {
|
|
1078
|
-
const fault = session.providerFault ?? legacySessionProviderFault(session, debug);
|
|
1079
|
-
return fault && isThrottleFault(fault) ? fault : undefined;
|
|
1080
|
-
}
|
|
1081
|
-
export class WorkspaceFilesClient {
|
|
1082
|
-
http;
|
|
1083
|
-
publisher;
|
|
1084
|
-
constructor(http, publisher) {
|
|
1085
|
-
this.http = http;
|
|
1086
|
-
this.publisher = publisher;
|
|
1087
|
-
}
|
|
1088
|
-
async publish(file) {
|
|
1089
|
-
const bundle = file._takeDraftBundle();
|
|
1090
|
-
const stream = file._takeDraftStream();
|
|
1091
|
-
if (!bundle && !stream)
|
|
1092
|
-
throw new Error("workspace.files.publish requires a draft File");
|
|
1093
|
-
const uploaded = bundle
|
|
1094
|
-
? await this.publisher.upload({ bytes: bundle.bytes, hash: bundle.contentHash, contentType: "application/zip" })
|
|
1095
|
-
: await this.publisher.uploadStream({ drive: stream.drive, contentType: "application/zip" });
|
|
1096
|
-
return operations.publishWorkspaceFile(this.http, {
|
|
1097
|
-
assetId: uploaded.assetId,
|
|
1098
|
-
contentHash: uploaded.contentHash,
|
|
1099
|
-
sizeBytes: uploaded.sizeBytes,
|
|
1100
|
-
contentType: uploaded.contentType,
|
|
1101
|
-
name: bundle?.name ?? stream.name,
|
|
1102
|
-
mountPath: bundle?.mountPath ?? stream.mountPath
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
list(query) {
|
|
1106
|
-
return operations.listWorkspaceFiles(this.http, query);
|
|
1107
|
-
}
|
|
1108
|
-
get(resourceId, version) { return operations.getWorkspaceFile(this.http, resourceId, version); }
|
|
1109
|
-
delete(resourceId) { return operations.deleteWorkspaceFile(this.http, resourceId); }
|
|
1110
|
-
}
|
|
1111
|
-
export class WorkspaceSkillsClient {
|
|
1112
|
-
http;
|
|
1113
|
-
publisher;
|
|
1114
|
-
constructor(http, publisher) {
|
|
1115
|
-
this.http = http;
|
|
1116
|
-
this.publisher = publisher;
|
|
1117
|
-
}
|
|
1118
|
-
async publish(skill) {
|
|
1119
|
-
const bundle = skill._takeDraftBundle();
|
|
1120
|
-
if (!bundle)
|
|
1121
|
-
throw new Error("workspace.skills.publish requires a draft Skill");
|
|
1122
|
-
const uploaded = await this.publisher.upload({ bytes: bundle.bytes, hash: bundle.contentHash, contentType: "application/zip" });
|
|
1123
|
-
return operations.publishWorkspaceSkill(this.http, {
|
|
1124
|
-
assetId: uploaded.assetId,
|
|
1125
|
-
contentHash: uploaded.contentHash,
|
|
1126
|
-
sizeBytes: uploaded.sizeBytes,
|
|
1127
|
-
contentType: uploaded.contentType,
|
|
1128
|
-
name: bundle.name,
|
|
1129
|
-
description: bundle.description
|
|
1130
|
-
});
|
|
1131
|
-
}
|
|
1132
|
-
list(query) {
|
|
1133
|
-
return operations.listWorkspaceSkills(this.http, query);
|
|
1134
|
-
}
|
|
1135
|
-
get(resourceId, version) { return operations.getWorkspaceSkill(this.http, resourceId, version); }
|
|
1136
|
-
delete(resourceId) { return operations.deleteWorkspaceSkill(this.http, resourceId); }
|
|
1137
|
-
}
|
|
1138
|
-
export class WorkspaceToolsClient {
|
|
1139
|
-
http;
|
|
1140
|
-
publisher;
|
|
1141
|
-
constructor(http, publisher) {
|
|
1142
|
-
this.http = http;
|
|
1143
|
-
this.publisher = publisher;
|
|
1144
|
-
}
|
|
1145
|
-
async publish(tool) {
|
|
1146
|
-
const bundle = tool._takeDraftBundle();
|
|
1147
|
-
if (!bundle)
|
|
1148
|
-
throw new Error("workspace.tools.publish requires a draft Tool");
|
|
1149
|
-
const uploaded = await this.publisher.upload({ bytes: bundle.bytes, hash: bundle.contentHash, contentType: "application/zip" });
|
|
1150
|
-
return operations.publishWorkspaceTool(this.http, {
|
|
1151
|
-
assetId: uploaded.assetId,
|
|
1152
|
-
contentHash: uploaded.contentHash,
|
|
1153
|
-
sizeBytes: uploaded.sizeBytes,
|
|
1154
|
-
contentType: uploaded.contentType,
|
|
1155
|
-
name: bundle.ref.name,
|
|
1156
|
-
description: bundle.ref.description,
|
|
1157
|
-
input_schema: bundle.ref.input_schema,
|
|
1158
|
-
entry: bundle.ref.entry
|
|
1159
|
-
});
|
|
1160
|
-
}
|
|
1161
|
-
list(query) {
|
|
1162
|
-
return operations.listWorkspaceTools(this.http, query);
|
|
1163
|
-
}
|
|
1164
|
-
get(resourceId, version) { return operations.getWorkspaceTool(this.http, resourceId, version); }
|
|
1165
|
-
delete(resourceId) { return operations.deleteWorkspaceTool(this.http, resourceId); }
|
|
1166
|
-
}
|
|
1167
|
-
export class WorkspaceInstructionsClient {
|
|
1168
|
-
http;
|
|
1169
|
-
publisher;
|
|
1170
|
-
constructor(http, publisher) {
|
|
1171
|
-
this.http = http;
|
|
1172
|
-
this.publisher = publisher;
|
|
1173
|
-
}
|
|
1174
|
-
async publish(instructions) {
|
|
1175
|
-
const bundle = instructions._takeDraftBundle();
|
|
1176
|
-
if (!bundle)
|
|
1177
|
-
throw new Error("workspace.instructions.publish requires draft instructions");
|
|
1178
|
-
const uploaded = await this.publisher.upload({ bytes: bundle.bytes, hash: bundle.contentHash, contentType: "application/zip" });
|
|
1179
|
-
return operations.publishWorkspaceInstruction(this.http, {
|
|
1180
|
-
assetId: uploaded.assetId,
|
|
1181
|
-
contentHash: uploaded.contentHash,
|
|
1182
|
-
sizeBytes: uploaded.sizeBytes,
|
|
1183
|
-
contentType: uploaded.contentType,
|
|
1184
|
-
name: bundle.name
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
list(query) {
|
|
1188
|
-
return operations.listWorkspaceInstructions(this.http, query);
|
|
1189
|
-
}
|
|
1190
|
-
get(resourceId, version) { return operations.getWorkspaceInstruction(this.http, resourceId, version); }
|
|
1191
|
-
delete(resourceId) { return operations.deleteWorkspaceInstruction(this.http, resourceId); }
|
|
1192
|
-
}
|
|
1193
|
-
export class WorkspaceClient {
|
|
1194
|
-
files;
|
|
1195
|
-
skills;
|
|
1196
|
-
tools;
|
|
1197
|
-
instructions;
|
|
1198
|
-
secrets;
|
|
1199
|
-
constructor(http, publisher) {
|
|
1200
|
-
this.files = new WorkspaceFilesClient(http, publisher);
|
|
1201
|
-
this.skills = new WorkspaceSkillsClient(http, publisher);
|
|
1202
|
-
this.tools = new WorkspaceToolsClient(http, publisher);
|
|
1203
|
-
this.instructions = new WorkspaceInstructionsClient(http, publisher);
|
|
1204
|
-
this.secrets = new SecretsClient(http);
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
//# sourceMappingURL=client.js.map
|