experimental-ash 0.31.0 → 0.33.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/CHANGELOG.md +27 -0
- package/dist/docs/public/auth-and-route-protection.md +95 -17
- package/dist/docs/public/channels/README.md +9 -3
- package/dist/src/channel/send.js +1 -1
- package/dist/src/cli/dev/repl.js +2 -2
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/compiler/normalize-helpers.js +1 -1
- package/dist/src/context/serialize.d.ts +1 -1
- package/dist/src/context/serialize.js +1 -1
- package/dist/src/discover/skills.js +1 -1
- package/dist/src/execution/create-session-step.js +1 -1
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
- package/dist/src/execution/durable-session-store.d.ts +1 -1
- package/dist/src/execution/next-driver-action.d.ts +1 -0
- package/dist/src/execution/node-step.js +1 -1
- package/dist/src/execution/session-callback-step.js +1 -1
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-steps.d.ts +4 -10
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/attachment-staging.js +1 -1
- package/dist/src/harness/authorization.d.ts +81 -0
- package/dist/src/harness/authorization.js +1 -0
- package/dist/src/harness/code-mode-approval.js +1 -1
- package/dist/src/harness/code-mode-connection-auth-state.d.ts +15 -0
- package/dist/src/harness/code-mode-connection-auth-state.js +1 -0
- package/dist/src/{execution/code-mode-authorization-lifecycle.d.ts → harness/code-mode-lifecycle.d.ts} +7 -6
- package/dist/src/harness/code-mode-lifecycle.js +1 -0
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/runtime-actions.js +1 -1
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/logging.d.ts +10 -0
- package/dist/src/internal/logging.js +1 -1
- package/dist/src/internal/nitro/host/build-application.js +1 -1
- package/dist/src/internal/nitro/host/code-mode-runtime-dependency-plugin.d.ts +1 -0
- package/dist/src/internal/nitro/host/code-mode-runtime-dependency-plugin.js +1 -0
- package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
- package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
- package/dist/src/internal/nitro/host/start-development-server.js +1 -1
- package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
- package/dist/src/internal/nitro/routes/info.d.ts +4 -2
- package/dist/src/internal/nitro/routes/info.js +1 -1
- package/dist/src/internal/workflow-bundle/builder-support.js +1 -1
- package/dist/src/internal/workflow-bundle/builder.js +2 -2
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-ffi-types@0.32.0/node_modules/@jitl/quickjs-ffi-types/dist/index.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-debug-asyncify@0.32.0/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/emscripten-module.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-debug-asyncify@0.32.0/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/ffi.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-debug-asyncify@0.32.0/node_modules/@jitl/quickjs-wasmfile-debug-asyncify/dist/index.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-debug-sync@0.32.0/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/emscripten-module.js +4 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-debug-sync@0.32.0/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/ffi.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-debug-sync@0.32.0/node_modules/@jitl/quickjs-wasmfile-debug-sync/dist/index.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-release-asyncify@0.32.0/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/emscripten-module.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-release-asyncify@0.32.0/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/ffi.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-release-asyncify@0.32.0/node_modules/@jitl/quickjs-wasmfile-release-asyncify/dist/index.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-release-sync@0.32.0/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-release-sync@0.32.0/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/ffi.js +1 -0
- package/dist/src/node_modules/.pnpm/@jitl_quickjs-wasmfile-release-sync@0.32.0/node_modules/@jitl/quickjs-wasmfile-release-sync/dist/index.js +1 -0
- package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/code-mode-tool.js +1 -0
- package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime/manager.js +1 -0
- package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime/worker-source.js +1153 -0
- package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/serialization.js +1 -0
- package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/tool-invocation.js +1 -1
- package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/chunk-TAV5CUKK.js +1 -0
- package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/chunk-V2S4ZYJR.js +6 -0
- package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/index.js +1 -0
- package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/module-ES6BEMUI.js +1 -0
- package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/module-asyncify-2EFITU5U.js +1 -0
- package/dist/src/node_modules/.pnpm/quickjs-emscripten@0.32.0/node_modules/quickjs-emscripten/dist/chunk-OHAYRCBA.js +1 -0
- package/dist/src/node_modules/.pnpm/quickjs-emscripten@0.32.0/node_modules/quickjs-emscripten/dist/index.js +1 -0
- package/dist/src/protocol/message.d.ts +18 -45
- package/dist/src/protocol/message.js +1 -1
- package/dist/src/protocol/routes.d.ts +5 -3
- package/dist/src/public/channels/ash.d.ts +9 -1
- package/dist/src/public/channels/ash.js +1 -1
- package/dist/src/public/channels/auth.d.ts +68 -3
- package/dist/src/public/channels/auth.js +1 -1
- package/dist/src/public/channels/discord/discordChannel.d.ts +2 -3
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/slack/api.js +1 -1
- package/dist/src/public/channels/slack/connections.d.ts +3 -3
- package/dist/src/public/channels/slack/defaults.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +5 -6
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/defaults.js +1 -1
- package/dist/src/public/channels/teams/teamsChannel.d.ts +2 -3
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +2 -3
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +2 -3
- package/dist/src/public/connections/errors.d.ts +1 -1
- package/dist/src/public/connections/index.js +1 -1
- package/dist/src/public/definitions/connections/mcp.d.ts +3 -3
- package/dist/src/public/definitions/defineChannel.d.ts +2 -3
- package/dist/src/public/definitions/defineChannel.js +1 -1
- package/dist/src/runtime/connections/callback-route.js +1 -1
- package/dist/src/runtime/connections/mcp-client.d.ts +1 -1
- package/dist/src/runtime/connections/mcp-client.js +1 -1
- package/dist/src/runtime/connections/principal.d.ts +2 -9
- package/dist/src/runtime/connections/principal.js +1 -1
- package/dist/src/runtime/connections/types.d.ts +6 -6
- package/dist/src/runtime/framework-channels/index.d.ts +8 -0
- package/dist/src/runtime/framework-channels/index.js +1 -1
- package/dist/src/runtime/framework-tools/code-mode-connection-auth.d.ts +0 -5
- package/dist/src/runtime/framework-tools/code-mode-connection-auth.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search.d.ts +2 -3
- package/dist/src/runtime/framework-tools/connection-search.js +1 -1
- package/dist/src/runtime/framework-tools/connection-tools.d.ts +4 -3
- package/dist/src/runtime/framework-tools/connection-tools.js +1 -1
- package/dist/src/runtime/prompt/connections.js +1 -1
- package/dist/src/runtime/resolve-agent-graph.js +1 -1
- package/dist/src/services/dev-client/request-headers.d.ts +3 -2
- package/package.json +4 -3
- package/dist/src/execution/authorization-challenge-defaults.d.ts +0 -43
- package/dist/src/execution/authorization-challenge-defaults.js +0 -1
- package/dist/src/execution/await-authorization-orchestrator.d.ts +0 -28
- package/dist/src/execution/await-authorization-orchestrator.js +0 -1
- package/dist/src/execution/await-authorization-splice.d.ts +0 -23
- package/dist/src/execution/await-authorization-splice.js +0 -1
- package/dist/src/execution/code-mode-authorization-lifecycle.js +0 -1
- package/dist/src/execution/connection-auth-steps.d.ts +0 -123
- package/dist/src/execution/connection-auth-steps.js +0 -1
- package/dist/src/internal/nitro/host/code-mode-worker-asset.d.ts +0 -18
- package/dist/src/internal/nitro/host/code-mode-worker-asset.js +0 -1
- package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/code-mode-tool.js +0 -1
- package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/runtime/manager.js +0 -1
- package/dist/src/node_modules/.pnpm/experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_/node_modules/experimental-ai-sdk-code-mode/dist/serialization.js +0 -1
- package/dist/src/runtime/connections/principal-context.d.ts +0 -101
- package/dist/src/runtime/connections/principal-context.js +0 -1
- package/dist/src/runtime/framework-tools/pending-connection-tool-calls.d.ts +0 -78
- package/dist/src/runtime/framework-tools/pending-connection-tool-calls.js +0 -1
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/approval-continuation.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/approval-response.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/continuation-capability.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/errors.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/fetch-policy.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/host-interrupt.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/index.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/interrupt-continuation.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/options.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/run-code-mode.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/runtime/max-workers.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/runtime-assets.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/source-cache.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/telemetry.js +0 -0
- /package/dist/src/node_modules/.pnpm/{experimental-ai-sdk-code-mode@1.0.7_ai@7.0.0-canary.154_zod@4.4.3_ → experimental-ai-sdk-code-mode@1.0.9_ai@7.0.0-canary.154_zod@4.4.3_}/node_modules/experimental-ai-sdk-code-mode/dist/tool-prompt.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# experimental-ash
|
|
2
2
|
|
|
3
|
+
## 0.33.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1e81358: Upgrade code mode to 1.0.9, rely on its inline worker loader instead of materializing worker assets, and preserve nested tool action events when code-mode continuations replay.
|
|
8
|
+
|
|
9
|
+
## 0.33.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- fde16ec: Typed authorization API with channel events: replace the generic `suspend()` / `getSuspension()` primitive with a purpose-built `requestAuthorization` / `getAuthorizationResult` / `getHookUrl` API. The harness now emits typed `authorization.required` and `authorization.completed` stream events that channels can handle directly (sign-in buttons, adaptive cards). A single per-session auth hook with a deterministic token replaces the old per-challenge orchestrator. Custom authored tools can use the same API for OAuth flows.
|
|
14
|
+
|
|
15
|
+
Connection authorization now works in code mode. When a connection tool or `connection_search` needs OAuth inside the code-mode sandbox, the harness parks the session, emits `authorization.required`, and resumes via the SDK's `continueCodeModeInterrupt` after the callback — matching the direct-mode behavior.
|
|
16
|
+
|
|
17
|
+
- cb16d9a: Log failed tool calls, code-mode failures, and channel/runtime errors with full stack traces. Throwing tools (previously caught by the AI SDK and reduced to a message) now log the live error and cause chain; channel dispatch catches handler throws and logs rejected background tasks instead of dropping them; and the runtime logs `run`/`deliver` failures with full detail. Adds `ASH_LOG_LEVEL` (`debug`|`info`|`warn`|`error`, defaulting to `info` in production) to control verbosity.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- dc97980: Limit `ash dev` lockfile watcher paths to the app root when no workspace marker is found, avoiding Windows fs watcher crashes from watching filesystem-root ancestors.
|
|
22
|
+
- cb16d9a: The workflow bundler now fails the build with a clear, attributed error when a Node.js built-in reaches the durable workflow driver body (which runs in a VM with no `require`). Previously such an import — typically pulled in transitively by a plain helper imported into a `"use workflow"` body — only surfaced at workflow run time as a cryptic `ReferenceError: require is not defined`. The error now names the offending builtin and the importing module and points to the fix (move the Node API behind a `"use step"`).
|
|
23
|
+
|
|
24
|
+
## 0.32.0
|
|
25
|
+
|
|
26
|
+
### Minor Changes
|
|
27
|
+
|
|
28
|
+
- a274bcd: `ashChannel({ auth })` now accepts an ordered `AuthFn[]` (or a single `AuthFn`) walked by a new public `routeAuth(request, auth)` helper: first `SessionAuthContext` wins, `null` / `undefined` skips, exhaustion (including `[]`) returns 401. Adds a new `localDev()` helper that authenticates requests addressed to a loopback hostname so `[localDev(), vercelOidc()]` is the canonical dev-and-prod chain — and the new framework default for the ash channel and `/ash/v1/info`. **Breaking:** `none()` now returns a synthetic `SessionAuthContext` with `principalType: "anonymous"` instead of `undefined`; routes detecting anonymous traffic should check `principalType === "anonymous"` rather than `session.auth.current === null`.
|
|
29
|
+
|
|
3
30
|
## 0.31.0
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
|
@@ -15,35 +15,79 @@ These settings apply to:
|
|
|
15
15
|
- `POST /ash/v1/session/:sessionId`
|
|
16
16
|
- `GET /ash/v1/session/:sessionId/stream`
|
|
17
17
|
|
|
18
|
-
## The
|
|
18
|
+
## The Default
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
`pnpm create experimental-ash-agent` scaffolds `agent/channels/ash.ts` with the same default chain
|
|
21
|
+
the framework applies when no file exists:
|
|
21
22
|
|
|
22
23
|
```ts
|
|
23
24
|
// agent/channels/ash.ts
|
|
24
25
|
import { ashChannel } from "experimental-ash/channels/ash";
|
|
25
|
-
import { vercelOidc } from "experimental-ash/channels/auth";
|
|
26
|
+
import { localDev, vercelOidc } from "experimental-ash/channels/auth";
|
|
26
27
|
|
|
27
28
|
export default ashChannel({
|
|
28
|
-
auth: vercelOidc(),
|
|
29
|
+
auth: [localDev(), vercelOidc()],
|
|
29
30
|
});
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
over framework defaults by name.
|
|
33
|
+
Deleting the file restores the framework default — the array above is the framework default, just
|
|
34
|
+
written out so you can see and edit it.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
## Walking The Auth Array
|
|
37
|
+
|
|
38
|
+
`auth` accepts either a single `AuthFn` or an array of them walked in order. Each entry returns:
|
|
39
|
+
|
|
40
|
+
- a `SessionAuthContext` — accept the request and halt the walk
|
|
41
|
+
- `null` or `undefined` — skip to the next entry
|
|
42
|
+
|
|
43
|
+
If every entry skips, the request is rejected with `401`. An empty array `auth: []` therefore
|
|
44
|
+
rejects every request.
|
|
45
|
+
|
|
46
|
+
To accept anonymous traffic, include `none()` as the final entry:
|
|
37
47
|
|
|
38
48
|
```ts
|
|
39
49
|
import { ashChannel } from "experimental-ash/channels/ash";
|
|
40
50
|
import { none } from "experimental-ash/channels/auth";
|
|
41
51
|
|
|
42
52
|
export default ashChannel({
|
|
43
|
-
auth: none(),
|
|
53
|
+
auth: [none()],
|
|
44
54
|
});
|
|
45
55
|
```
|
|
46
56
|
|
|
57
|
+
`none()` returns a synthetic `SessionAuthContext` with `principalType: "anonymous"`, which
|
|
58
|
+
terminates the walk.
|
|
59
|
+
|
|
60
|
+
## Layering Providers
|
|
61
|
+
|
|
62
|
+
The array shape makes it easy to layer providers without rewriting a resolver. Insert new entries
|
|
63
|
+
before the catch-all helpers:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { ashChannel } from "experimental-ash/channels/ash";
|
|
67
|
+
import { type AuthFn, localDev, vercelOidc } from "experimental-ash/channels/auth";
|
|
68
|
+
import { getAuthJsSession } from "@/lib/auth";
|
|
69
|
+
|
|
70
|
+
function authjsSession(): AuthFn<Request> {
|
|
71
|
+
return async (request) => {
|
|
72
|
+
const session = await getAuthJsSession(request);
|
|
73
|
+
if (!session) return null;
|
|
74
|
+
return {
|
|
75
|
+
attributes: { providerId: session.providerId },
|
|
76
|
+
authenticator: "authjs",
|
|
77
|
+
principalId: session.profile.sub,
|
|
78
|
+
principalType: "user",
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default ashChannel({
|
|
84
|
+
auth: [authjsSession(), localDev(), vercelOidc()],
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Each entry that doesn't recognize the caller returns `null` and the walk falls through to the
|
|
89
|
+
next.
|
|
90
|
+
|
|
47
91
|
## Network Policy
|
|
48
92
|
|
|
49
93
|
Ash exposes IP allow-list helpers in `experimental-ash/channels/auth`.
|
|
@@ -57,6 +101,7 @@ If a request fails the network policy, Ash rejects it before auth or runtime exe
|
|
|
57
101
|
|
|
58
102
|
Ash currently ships these channel-auth helpers:
|
|
59
103
|
|
|
104
|
+
- `localDev`
|
|
60
105
|
- `none`
|
|
61
106
|
- `httpBasic`
|
|
62
107
|
- `jwtHmac`
|
|
@@ -64,17 +109,46 @@ Ash currently ships these channel-auth helpers:
|
|
|
64
109
|
- `oidc`
|
|
65
110
|
- `vercelOidc`
|
|
66
111
|
|
|
67
|
-
You can compose them with the low-level verifier helpers when you need custom request handling.
|
|
112
|
+
You can compose them with the low-level verifier helpers when you need custom request handling. For
|
|
113
|
+
custom channels built on `defineChannel`, call `routeAuth(request, auth)` from
|
|
114
|
+
`experimental-ash/channels/auth` to share the same walk semantics.
|
|
68
115
|
|
|
69
116
|
## Strategy Notes
|
|
70
117
|
|
|
118
|
+
### `localDev`
|
|
119
|
+
|
|
120
|
+
Authenticates with a synthetic `local-dev` principal **when the inbound request is addressed to
|
|
121
|
+
a loopback hostname**. The check is on the request URL, not on environment variables. A hostname
|
|
122
|
+
counts as loopback when it is:
|
|
123
|
+
|
|
124
|
+
- `localhost` or any `*.localhost` subdomain (RFC 6761),
|
|
125
|
+
- any IPv4 address in `127.0.0.0/8`, or
|
|
126
|
+
- the IPv6 loopback `::1`.
|
|
127
|
+
|
|
128
|
+
Every other request (a deployed Vercel hostname, a `fly.dev` URL, a public IP, etc.) returns
|
|
129
|
+
`null` so the walk falls through to the next entry.
|
|
130
|
+
|
|
131
|
+
This is deliberately not an env check. Sniffing `process.env.VERCEL` is unsafe: a deployment
|
|
132
|
+
outside Vercel (Fly, Railway, self-hosted, a raw container) leaves `VERCEL` unset, which under
|
|
133
|
+
the env-based model would accept every request from the public internet. Inspecting the request
|
|
134
|
+
hostname is correct on `ash dev`, `vercel dev`, smoke tests, Vitest workers, and any deployment
|
|
135
|
+
target — Vercel or otherwise.
|
|
136
|
+
|
|
137
|
+
Caveat: `localDev()` trusts whatever hostname the request advertises. If you publish an origin
|
|
138
|
+
that lets attacker-controlled `Host` headers through (no CDN, no normalizing reverse proxy in
|
|
139
|
+
front), an attacker can spoof `Host: localhost` and reach `localDev()`. Layer a real
|
|
140
|
+
authenticator on such deployments and do not rely on `localDev()` alone.
|
|
141
|
+
|
|
71
142
|
### `vercelOidc`
|
|
72
143
|
|
|
73
|
-
Use this for the common Vercel deployment path.
|
|
144
|
+
Use this for the common Vercel deployment path. Verifies a bearer JWT against the Vercel OIDC
|
|
145
|
+
issuer; tokens minted for the current `VERCEL_PROJECT_ID` are always accepted (so internal
|
|
146
|
+
subagent / runtime callers authenticate without configuration).
|
|
74
147
|
|
|
75
148
|
### `none`
|
|
76
149
|
|
|
77
|
-
Use
|
|
150
|
+
Returns a synthetic anonymous `SessionAuthContext`. Use as the final entry in `auth` to accept
|
|
151
|
+
unauthenticated traffic explicitly.
|
|
78
152
|
|
|
79
153
|
### `httpBasic`
|
|
80
154
|
|
|
@@ -108,7 +182,9 @@ Important behavior:
|
|
|
108
182
|
- `auth.current` is the caller for the active inbound turn
|
|
109
183
|
- `auth.initiator` is the caller that started the durable session
|
|
110
184
|
- follow-up messages update `auth.current` without changing `auth.initiator`
|
|
111
|
-
-
|
|
185
|
+
- both are `null` only on internal runtime paths (e.g. subagents) that never went through an
|
|
186
|
+
authored channel route — HTTP traffic always populates `auth.current` because the walker either
|
|
187
|
+
accepts with a `SessionAuthContext` or returns `401`
|
|
112
188
|
|
|
113
189
|
Auth on follow-up messages (`deliver()`) is honored by the workflow runtime. When a different
|
|
114
190
|
user sends a follow-up to the same session, `session.auth.current` reflects the new caller on
|
|
@@ -131,10 +207,12 @@ import { ashChannel } from "experimental-ash/channels/ash";
|
|
|
131
207
|
import { httpBasic } from "experimental-ash/channels/auth";
|
|
132
208
|
|
|
133
209
|
export default ashChannel({
|
|
134
|
-
auth:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
210
|
+
auth: [
|
|
211
|
+
httpBasic({
|
|
212
|
+
username: "ops",
|
|
213
|
+
password: process.env.ROUTE_AUTH_BASIC_PASSWORD,
|
|
214
|
+
}),
|
|
215
|
+
],
|
|
138
216
|
});
|
|
139
217
|
```
|
|
140
218
|
|
|
@@ -97,18 +97,24 @@ The framework ships its canonical session protocol on `experimental-ash/channels
|
|
|
97
97
|
This is the channel the Ash dev client and any deployed-agent SDK talk to — it mounts the routes
|
|
98
98
|
under `/ash/v1/session*` documented in [Ash External Agent Protocol](../../external-agent-protocol.md).
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
`auth` accepts either a single `AuthFn` or an ordered array walked in order: the first entry
|
|
101
|
+
returning a `SessionAuthContext` wins, `null` / `undefined` skips, exhaustion (including `[]`)
|
|
102
|
+
returns `401`. Use `experimental-ash/channels/auth` for the common helpers such as `localDev()`,
|
|
101
103
|
`vercelOidc()`, `httpBasic()`, and `none()`.
|
|
102
104
|
|
|
103
105
|
```ts
|
|
104
106
|
import { ashChannel } from "experimental-ash/channels/ash";
|
|
105
|
-
import { vercelOidc } from "experimental-ash/channels/auth";
|
|
107
|
+
import { localDev, vercelOidc } from "experimental-ash/channels/auth";
|
|
106
108
|
|
|
107
109
|
export default ashChannel({
|
|
108
|
-
auth: vercelOidc(),
|
|
110
|
+
auth: [localDev(), vercelOidc()],
|
|
109
111
|
});
|
|
110
112
|
```
|
|
111
113
|
|
|
114
|
+
`pnpm create experimental-ash-agent` scaffolds exactly this file at `agent/channels/ash.ts` —
|
|
115
|
+
deleting it restores the same default. See [Auth and Route Protection](../auth-and-route-protection.md)
|
|
116
|
+
for the full walking semantics and helper reference.
|
|
117
|
+
|
|
112
118
|
## Slack Channels
|
|
113
119
|
|
|
114
120
|
Slack channels are authored with a single `slackChannel()` factory. Pass no config for the
|
package/dist/src/channel/send.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createSession}from"#channel/session.js";import{
|
|
1
|
+
import{createSession}from"#channel/session.js";import{createLogger}from"#internal/logging.js";import{isRuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{serializeUrlFilePart}from"#internal/attachments/url-refs.js";const log=createLogger(`channel.send`);function createSendFn(t,r,i){return async(a,o)=>{let s=o.auth,c=o.callback,l=o.mode??`conversation`,u=o.state,d=o.continuationToken,f=`${i}:${d}`,{message:p,inputResponses:m,modelContext:h}=normalizeSendInput(a),g=serializeUrlFilePartsInMessage(p);try{let{sessionId:n}=await t.deliver({auth:s,continuationToken:f,payload:{inputResponses:m,message:g,modelContext:h}});return createSession(n,d,t)}catch(e){isRuntimeNoActiveSessionError(e)||log.warn(`deliver failed, falling back to starting a new session`,{continuationToken:f})}if(m&&m.length>0)throw Error(`Cannot deliver inputResponses — the target session was not found via continuation token.`);let _=u?{...r,state:{...r.state,...u}}:r;return createSession((await t.run({adapter:_,auth:s,capabilities:l===`conversation`?{requestInput:!0}:void 0,callback:c,continuationToken:f,input:{message:g??``,modelContext:h},mode:l})).sessionId,d,t)}}function serializeUrlFilePartsInMessage(e){if(e===void 0||typeof e==`string`)return e;let t=!1,n=e.map(e=>e.type===`file`&&e.data instanceof URL&&e.data.protocol!==`data:`?(t=!0,{...e,data:serializeUrlFilePart(e.data)}):e);return t?n:e}function normalizeSendInput(e){return typeof e==`string`||Array.isArray(e)?{message:e}:e}export{createSendFn};
|
package/dist/src/cli/dev/repl.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`)}function normalizeStepMessage(e){let t=e.trim();return t.length>0?t:null}function getRenderTag(e){return e.options?.isSubagent===!0?`subagent`:e.fallback}function getRenderTone(e){return e.options?.isSubagent===!0?`subagent`:e.fallback}function prefixSourceLabel(e){return e.options?.sourceLabel===void 0?e.message:`${e.options.sourceLabel}${e.separator??` `}${e.message}`}function formatContentEvent(e,t,n){switch(t.type){case`message.appended`:return{finalized:!1,kind:`message`,line:renderCliSpeakerLine(e,{message:t.data.messageSoFar,speaker:n?.sourceLabel??`agent`,tone:getRenderTone({fallback:`accent`,options:n})})};case`message.completed`:if(t.data.message===null)return;if(t.data.finishReason===`tool-calls`){let r=normalizeStepMessage(t.data.message);return r===null?void 0:{finalized:!0,kind:`message`,line:renderCliTaggedLine(e,{message:prefixSourceLabel({message:r,options:n,separator:`: `}),tag:getRenderTag({fallback:`step`,options:n}),tone:getRenderTone({fallback:`accent`,options:n})})}}return{finalized:!0,kind:`message`,line:renderCliSpeakerLine(e,{message:t.data.message,speaker:n?.sourceLabel??`agent`,tone:getRenderTone({fallback:`accent`,options:n})})};case`reasoning.appended`:return{finalized:!1,kind:`reasoning`,line:renderCliTaggedLine(e,{message:prefixSourceLabel({message:t.data.reasoningSoFar,options:n,separator:`: `}),tag:getRenderTag({fallback:`reasoning`,options:n}),tone:getRenderTone({fallback:`info`,options:n})})};case`reasoning.completed`:return{finalized:!0,kind:`reasoning`,line:renderCliTaggedLine(e,{message:prefixSourceLabel({message:t.data.reasoning,options:n,separator:`: `}),tag:getRenderTag({fallback:`reasoning`,options:n}),tone:getRenderTone({fallback:`info`,options:n})})};default:return}}function formatEvent(e,t,n){let r=formatContentEvent(e,t,n);if(r!==void 0)return r.line;switch(t.type){case`message.received`:return;case`actions.requested`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.actions.length} action${t.data.actions.length===1?``:`s`})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`input.requested`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.requests.length} request${t.data.requests.length===1?``:`s`})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});case`action.result`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${formatActionResultLabel(t.data.result)})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`session.waiting`:case`session.completed`:return;case`connection.authorization_required`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.connectionName}: ${t.data.description}${formatAuthorizationChallengeSuffix(t.data.authorization)})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`warning`,options:n})});case`compaction.requested`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`compacting conversation history`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`compaction.completed`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`conversation history compacted`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`step.failed`:case`turn.failed`:case`session.failed`:return;case`subagent.called`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.name} -> ${t.data.childSessionId})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});case`subagent.started`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.subagentName})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});case`subagent.event`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.subagentName}: ${formatNestedSubagentEventLabel(t.data.event)})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`subagent.completed`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.subagentName})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});default:return}}function getEventDisplayBlockKind(e){switch(e.type){case`message.appended`:case`message.completed`:case`reasoning.appended`:case`reasoning.completed`:return`content`;default:return`meta`}}function createTurnDisplayState(){return{activeLiveContentKind:null,lastPrintedBlockKind:null}}function printDisplayLine(e){let t=e.state;return t.activeLiveContentKind!==null&&(e.terminal.commitLive(),t={activeLiveContentKind:null,lastPrintedBlockKind:`content`}),t.lastPrintedBlockKind!==null&&t.lastPrintedBlockKind!==e.kind&&e.terminal.print(``),e.terminal.print(e.line),{activeLiveContentKind:null,lastPrintedBlockKind:e.kind}}function renderTurnEvent(e){let t=formatContentEvent(e.theme,e.event,e.options);if(t!==void 0){let n=e.state;return n.activeLiveContentKind!==null&&n.activeLiveContentKind!==t.kind&&(e.terminal.commitLive(),n={activeLiveContentKind:null,lastPrintedBlockKind:`content`}),n.lastPrintedBlockKind!==null&&n.lastPrintedBlockKind!==`content`&&e.terminal.print(``),e.terminal.updateLive(t.line),t.finalized?(e.terminal.commitLive(),{activeLiveContentKind:null,lastPrintedBlockKind:`content`}):{activeLiveContentKind:t.kind,lastPrintedBlockKind:`content`}}let n=formatEvent(e.theme,e.event,e.options);return n===void 0?e.state:printDisplayLine({kind:getEventDisplayBlockKind(e.event),line:n,state:e.state,terminal:e.terminal})}function isAbortLikeError(e){return(e instanceof DOMException||e instanceof Error)&&e.name===`AbortError`}function shouldDrainSubagentStreamsOnBoundary(e){return e.length===0}var ReplSubagentStreamManager=class{#e=new Map;#t;#n;#r;#i;constructor(e){this.#t=e.displayStateRef,this.#n=e.serverUrl,this.#r=e.terminal,this.#i=e.theme}subscribe(e){if(this.#e.has(e.sessionId))return;let t=new AbortController,n=this.#a({controller:t,sessionId:e.sessionId,subagentName:e.subagentName}).finally(()=>{this.#e.delete(e.sessionId)});this.#e.set(e.sessionId,{controller:t,done:n,label:e.subagentName})}async waitForIdle(){for(;this.#e.size>0;)await Promise.all([...this.#e.values()].map(e=>e.done))}async close(){let e=[...this.#e.values()];for(let t of e)t.controller.abort();await Promise.allSettled(e.map(e=>e.done))}async#a(t){let n=resolveDevelopmentServerResourceUrl({resource:createAshMessageStreamRoutePath(t.sessionId),serverUrl:this.#n});try{for await(let e of openStreamIterable({host:this.#n,maxReconnectAttempts:3,resolveHeaders:async()=>await createDevelopmentRequestHeadersAsync({resourceUrl:n}),sessionId:t.sessionId,signal:t.controller.signal,startIndex:0}))if(this.#t.current=renderTurnEvent({event:e,options:{isSubagent:!0,sourceLabel:t.subagentName},state:this.#t.current,terminal:this.#r,theme:this.#i}),e.type===`subagent.called`&&this.subscribe({sessionId:e.data.childSessionId,subagentName:e.data.name}),isCurrentTurnBoundaryEvent(e))return}catch(n){if(isAbortLikeError(n))return;let r=toErrorMessage(n);this.#t.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(this.#i,{message:`${t.subagentName} stream failed: ${r}`,tag:`subagent`,tone:`danger`}),state:this.#t.current,terminal:this.#r})}finally{t.controller.abort()}}};function formatActionResultLabel(e){switch(e.kind){case`load-skill-result`:return e.kind;case`subagent-result`:return`${e.kind}:${e.subagentName}`;case`tool-result`:return`${e.kind}:${e.toolName}`}}function formatAuthorizationChallengeSuffix(e){if(e===void 0)return``;let t=[];return e.url!==void 0&&t.push(e.url),e.userCode!==void 0&&t.push(`code ${e.userCode}`),e.instructions!==void 0&&t.push(e.instructions),t.length===0?``:` — ${t.join(`, `)}`}function formatNestedSubagentEventLabel(e){switch(e.type){case`actions.requested`:{let t=e.data.actions,n=t.map(e=>e.kind===`tool-call`?e.toolName:e.kind).join(`,`);return`${e.type} (${t.length} action${t.length===1?``:`s`}${n.length>0?`: ${n}`:``})`}case`action.result`:return`${e.type} (${formatActionResultLabel(e.data.result)})`;case`input.requested`:return`${e.type} (${e.data.requests.length} request${e.data.requests.length===1?``:`s`})`;default:return e.type}}function formatDispatch(e,t){return t.continuationToken?renderCliTaggedLine(e,{message:`resuming session ${t.continuationToken}`,tag:`session`,tone:`info`}):renderCliTaggedLine(e,{message:`starting a new session`,tag:`session`,tone:`info`})}function formatTurnDispatch(e,t){return t.turn.inputResponses!==void 0&&t.turn.message===void 0?renderCliTaggedLine(e,{message:`responding to pending input request${t.turn.inputResponses.length===1?``:`s`}`,tag:`session`,tone:`info`}):formatDispatch(e,t.session)}function formatSessionBoundary(e,t){let n=extractCurrentTurnBoundaryEvent(t),r=extractPendingRuntimeInputRequests(t);switch(n?.type){case`session.waiting`:return[renderCliTaggedLine(e,{message:r.length>0?`waiting for input approval/answer or the next message`:`waiting for the next message`,tag:`session`,tone:`success`})];case`session.completed`:return[renderCliTaggedLine(e,{message:`session completed; the next input starts a new session`,tag:`session`,tone:`success`})];case`session.failed`:{let t=n.data.details&&typeof n.data.details.name==`string`?n.data.details.name:void 0;return[renderCliTaggedLine(e,{message:t?`session failed (${t}): ${n.data.message}`:`session failed: ${n.data.message}`,tag:`session`,tone:`danger`}),renderCliTaggedLine(e,{message:`cleared; the next input starts a new session`,tag:`session`,tone:`warning`})]}default:return[]}}async function waitForInputLine(e,t,n={}){return await new Promise(r=>{let i=e.input,cleanup=()=>{e.off(`close`,handleClose),e.off(`line`,handleLine),i.off(`keypress`,handleKeypress)},handleClose=()=>{cleanup(),r(void 0)},handleLine=e=>{cleanup(),r(e)},handleKeypress=(t,i)=>{n.allowEscape!==!0||i.name!==`escape`||(cleanup(),e.write(null,{ctrl:!0,name:`u`}),r(ESCAPED_RUNTIME_INPUT_PROMPT))};e.setPrompt(t),e.once(`close`,handleClose),e.once(`line`,handleLine),n.allowEscape&&(emitKeypressEvents(i,e),i.on(`keypress`,handleKeypress)),e.prompt()})}async function runTurn(e){let t=e.turn,n={current:createTurnDisplayState()},r=new ReplSubagentStreamManager({displayStateRef:n,serverUrl:e.serverUrl,terminal:e.terminal,theme:e.theme}),ask=async t=>{e.terminal.startPrompt(e.rl,t);let n=await waitForInputLine(e.rl,t,{allowEscape:!0});return e.terminal.stopPrompt(),n};try{for(;;){let a=e.client.getSession();n.current=printDisplayLine({kind:`meta`,line:formatTurnDispatch(e.theme,{session:a,turn:t}),state:n.current,terminal:e.terminal});let o=await e.client.send({inputResponses:t.inputResponses,message:t.message,onEvent(t){n.current=renderTurnEvent({event:t,state:n.current,terminal:e.terminal,theme:e.theme}),t.type===`subagent.called`&&r.subscribe({sessionId:t.data.childSessionId,subagentName:t.data.name})},onResponseStart(t){t.sessionId&&a.sessionId!==t.sessionId&&(n.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(e.theme,{message:t.sessionId,tag:`session`,tone:`accent`}),state:n.current,terminal:e.terminal}),n.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(e.theme,{message:resolveDevelopmentServerResourceUrl({resource:createAshMessageStreamRoutePath(t.sessionId),serverUrl:e.serverUrl}).toString(),tag:`stream`,tone:`info`}),state:n.current,terminal:e.terminal}))}}),s=extractPendingRuntimeInputRequests(o.events);if(shouldDrainSubagentStreamsOnBoundary(s)){await r.waitForIdle();for(let t of formatSessionBoundary(e.theme,o.events))n.current=printDisplayLine({kind:`meta`,line:t,state:n.current,terminal:e.terminal});return`continue`}let c=await promptForRuntimeInputRequests({ask,print(t){n.current=printDisplayLine({kind:`meta`,line:t,state:n.current,terminal:e.terminal})},requests:s,theme:e.theme});if(c.kind===`aborted`)return`exit`;if(c.kind===`deferred`)return n.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(e.theme,{message:`left pending input requests unresolved; send a new message to ignore them`,tag:`session`,tone:`warning`}),state:n.current,terminal:e.terminal}),`continue`;t={inputResponses:c.inputResponses}}}finally{await r.close()}}async function runDevelopmentRepl(t){let n=createDevelopmentTerminal(),r=createCliTheme({color:!0}),i=createInterface({input:process.stdin,output:n.output,terminal:!0});i.on(`SIGINT`,()=>{i.close()});let o=createDevClient({serverUrl:t.serverUrl});try{for(n.print(renderIntro(r,t)),n.print(``);;){n.print(``),n.startPrompt(i,`you> `);let a=await waitForInputLine(i,`you> `);if(n.stopPrompt(),a===void 0)return;let s=parseDevReplInput(a);switch(s.kind!==`empty`&&s.kind!==`exit`&&n.print(``),s.kind){case`empty`:continue;case`help`:n.print(renderIntro(r,t)),n.print(``);continue;case`exit`:return;case`new`:await o.clear(),n.print(renderCliTaggedLine(r,{message:`cleared; the next input starts a new session`,tag:`session`,tone:`warning`})),n.print(``);continue;case`message`:try{if(await runTurn({client:o,rl:i,serverUrl:t.serverUrl,terminal:n,theme:r,turn:{message:s.message}})===`exit`)return}catch(i){isVercelAuthChallenge(i)?n.printError(renderCliTaggedLine(r,{message:formatVercelAuthChallengeMessage({serverUrl:t.serverUrl}),tag:`auth`,tone:`warning`})):n.printError(renderCliTaggedLine(r,{message:toErrorMessage(i),tag:`error`,tone:`danger`}))}n.print(``)}}}finally{await o.close(),i.close(),n.dispose()}}export{createTurnDisplayState,formatContentEvent,formatEvent,renderTurnEvent,runDevelopmentRepl,shouldDrainSubagentStreamsOnBoundary};
|
|
1
|
+
import{ASH_CONTINUE_SESSION_ROUTE_PATTERN,ASH_CREATE_SESSION_ROUTE_PATH,ASH_MESSAGE_STREAM_ROUTE_PATTERN,createAshMessageStreamRoutePath}from"#protocol/routes.js";import{createCliTheme,renderCliBanner,renderCliSection,renderCliSpeakerLine,renderCliTaggedLine}from"#cli/ui/output.js";import{createInterface,emitKeypressEvents}from"node:readline";import{openStreamIterable}from"#client/open-stream.js";import{isCurrentTurnBoundaryEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{createDevelopmentRequestHeadersAsync}from"#services/dev-client/request-headers.js";import{extractCurrentTurnBoundaryEvent}from"#services/dev-client/stream.js";import{resolveDevelopmentServerResourceUrl}from"#services/dev-client/url.js";import{formatVercelAuthChallengeMessage,isVercelAuthChallenge}from"#services/dev-client/vercel-auth-error.js";import{createDevClient}from"#services/dev-client.js";import{parseDevReplInput}from"#cli/dev/input.js";import{ESCAPED_RUNTIME_INPUT_PROMPT,extractPendingRuntimeInputRequests,promptForRuntimeInputRequests}from"#cli/dev/input-requests.js";import{createDevelopmentTerminal}from"#cli/dev/terminal.js";function renderConnectionRows(r){let i=[{label:`Server`,tone:`info`,value:r.serverUrl},{label:`Create`,tone:`info`,value:`POST ${ASH_CREATE_SESSION_ROUTE_PATH}`},{label:`Continue`,tone:`info`,value:`POST ${ASH_CONTINUE_SESSION_ROUTE_PATTERN}`},{label:`Stream`,tone:`info`,value:`GET ${ASH_MESSAGE_STREAM_ROUTE_PATTERN}`}];return i.push({label:`Session`,value:`Follow-up messages reuse the active continuation token.`}),i}function renderCommandRows(){return[{label:`/help`,value:`Print the connection contract and available commands.`},{label:`/new`,value:`Clear the current durable session cursor.`},{label:`/exit`,value:`Exit the REPL.`}]}function renderIntro(e,t){return[renderCliBanner(e,{subtitle:`Interactive development REPL for the active Ash server.`,title:`Ash Dev`}),``,renderCliSection(e,{rows:renderConnectionRows(t),title:`Connection`}),``,renderCliSection(e,{rows:renderCommandRows(),title:`Commands`})].join(`
|
|
2
|
+
`)}function normalizeStepMessage(e){let t=e.trim();return t.length>0?t:null}function getRenderTag(e){return e.options?.isSubagent===!0?`subagent`:e.fallback}function getRenderTone(e){return e.options?.isSubagent===!0?`subagent`:e.fallback}function prefixSourceLabel(e){return e.options?.sourceLabel===void 0?e.message:`${e.options.sourceLabel}${e.separator??` `}${e.message}`}function formatContentEvent(e,t,n){switch(t.type){case`message.appended`:return{finalized:!1,kind:`message`,line:renderCliSpeakerLine(e,{message:t.data.messageSoFar,speaker:n?.sourceLabel??`agent`,tone:getRenderTone({fallback:`accent`,options:n})})};case`message.completed`:if(t.data.message===null)return;if(t.data.finishReason===`tool-calls`){let r=normalizeStepMessage(t.data.message);return r===null?void 0:{finalized:!0,kind:`message`,line:renderCliTaggedLine(e,{message:prefixSourceLabel({message:r,options:n,separator:`: `}),tag:getRenderTag({fallback:`step`,options:n}),tone:getRenderTone({fallback:`accent`,options:n})})}}return{finalized:!0,kind:`message`,line:renderCliSpeakerLine(e,{message:t.data.message,speaker:n?.sourceLabel??`agent`,tone:getRenderTone({fallback:`accent`,options:n})})};case`reasoning.appended`:return{finalized:!1,kind:`reasoning`,line:renderCliTaggedLine(e,{message:prefixSourceLabel({message:t.data.reasoningSoFar,options:n,separator:`: `}),tag:getRenderTag({fallback:`reasoning`,options:n}),tone:getRenderTone({fallback:`info`,options:n})})};case`reasoning.completed`:return{finalized:!0,kind:`reasoning`,line:renderCliTaggedLine(e,{message:prefixSourceLabel({message:t.data.reasoning,options:n,separator:`: `}),tag:getRenderTag({fallback:`reasoning`,options:n}),tone:getRenderTone({fallback:`info`,options:n})})};default:return}}function formatEvent(e,t,n){let r=formatContentEvent(e,t,n);if(r!==void 0)return r.line;switch(t.type){case`message.received`:return;case`actions.requested`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.actions.length} action${t.data.actions.length===1?``:`s`})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`input.requested`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.requests.length} request${t.data.requests.length===1?``:`s`})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});case`action.result`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${formatActionResultLabel(t.data.result)})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`session.waiting`:case`session.completed`:return;case`authorization.required`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.name}: ${t.data.description}${formatAuthorizationChallengeSuffix(t.data.authorization)})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`warning`,options:n})});case`compaction.requested`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`compacting conversation history`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`compaction.completed`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`conversation history compacted`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`step.failed`:case`turn.failed`:case`session.failed`:return;case`subagent.called`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.name} -> ${t.data.childSessionId})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});case`subagent.started`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.subagentName})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});case`subagent.event`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.subagentName}: ${formatNestedSubagentEventLabel(t.data.event)})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`muted`,options:n})});case`subagent.completed`:return renderCliTaggedLine(e,{message:prefixSourceLabel({message:`${t.type} (${t.data.subagentName})`,options:n}),tag:getRenderTag({fallback:`event`,options:n}),tone:getRenderTone({fallback:`info`,options:n})});default:return}}function getEventDisplayBlockKind(e){switch(e.type){case`message.appended`:case`message.completed`:case`reasoning.appended`:case`reasoning.completed`:return`content`;default:return`meta`}}function createTurnDisplayState(){return{activeLiveContentKind:null,lastPrintedBlockKind:null}}function printDisplayLine(e){let t=e.state;return t.activeLiveContentKind!==null&&(e.terminal.commitLive(),t={activeLiveContentKind:null,lastPrintedBlockKind:`content`}),t.lastPrintedBlockKind!==null&&t.lastPrintedBlockKind!==e.kind&&e.terminal.print(``),e.terminal.print(e.line),{activeLiveContentKind:null,lastPrintedBlockKind:e.kind}}function renderTurnEvent(e){let t=formatContentEvent(e.theme,e.event,e.options);if(t!==void 0){let n=e.state;return n.activeLiveContentKind!==null&&n.activeLiveContentKind!==t.kind&&(e.terminal.commitLive(),n={activeLiveContentKind:null,lastPrintedBlockKind:`content`}),n.lastPrintedBlockKind!==null&&n.lastPrintedBlockKind!==`content`&&e.terminal.print(``),e.terminal.updateLive(t.line),t.finalized?(e.terminal.commitLive(),{activeLiveContentKind:null,lastPrintedBlockKind:`content`}):{activeLiveContentKind:t.kind,lastPrintedBlockKind:`content`}}let n=formatEvent(e.theme,e.event,e.options);return n===void 0?e.state:printDisplayLine({kind:getEventDisplayBlockKind(e.event),line:n,state:e.state,terminal:e.terminal})}function isAbortLikeError(e){return(e instanceof DOMException||e instanceof Error)&&e.name===`AbortError`}function shouldDrainSubagentStreamsOnBoundary(e){return e.length===0}var ReplSubagentStreamManager=class{#e=new Map;#t;#n;#r;#i;constructor(e){this.#t=e.displayStateRef,this.#n=e.serverUrl,this.#r=e.terminal,this.#i=e.theme}subscribe(e){if(this.#e.has(e.sessionId))return;let t=new AbortController,n=this.#a({controller:t,sessionId:e.sessionId,subagentName:e.subagentName}).finally(()=>{this.#e.delete(e.sessionId)});this.#e.set(e.sessionId,{controller:t,done:n,label:e.subagentName})}async waitForIdle(){for(;this.#e.size>0;)await Promise.all([...this.#e.values()].map(e=>e.done))}async close(){let e=[...this.#e.values()];for(let t of e)t.controller.abort();await Promise.allSettled(e.map(e=>e.done))}async#a(e){let t=resolveDevelopmentServerResourceUrl({resource:createAshMessageStreamRoutePath(e.sessionId),serverUrl:this.#n});try{for await(let n of openStreamIterable({host:this.#n,maxReconnectAttempts:3,resolveHeaders:async()=>await createDevelopmentRequestHeadersAsync({resourceUrl:t}),sessionId:e.sessionId,signal:e.controller.signal,startIndex:0}))if(this.#t.current=renderTurnEvent({event:n,options:{isSubagent:!0,sourceLabel:e.subagentName},state:this.#t.current,terminal:this.#r,theme:this.#i}),n.type===`subagent.called`&&this.subscribe({sessionId:n.data.childSessionId,subagentName:n.data.name}),isCurrentTurnBoundaryEvent(n))return}catch(t){if(isAbortLikeError(t))return;let n=toErrorMessage(t);this.#t.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(this.#i,{message:`${e.subagentName} stream failed: ${n}`,tag:`subagent`,tone:`danger`}),state:this.#t.current,terminal:this.#r})}finally{e.controller.abort()}}};function formatActionResultLabel(e){switch(e.kind){case`load-skill-result`:return e.kind;case`subagent-result`:return`${e.kind}:${e.subagentName}`;case`tool-result`:return`${e.kind}:${e.toolName}`}}function formatAuthorizationChallengeSuffix(e){if(e===void 0)return``;let t=[];return e.url!==void 0&&t.push(e.url),e.userCode!==void 0&&t.push(`code ${e.userCode}`),e.instructions!==void 0&&t.push(e.instructions),t.length===0?``:` — ${t.join(`, `)}`}function formatNestedSubagentEventLabel(e){switch(e.type){case`actions.requested`:{let t=e.data.actions,n=t.map(e=>e.kind===`tool-call`?e.toolName:e.kind).join(`,`);return`${e.type} (${t.length} action${t.length===1?``:`s`}${n.length>0?`: ${n}`:``})`}case`action.result`:return`${e.type} (${formatActionResultLabel(e.data.result)})`;case`input.requested`:return`${e.type} (${e.data.requests.length} request${e.data.requests.length===1?``:`s`})`;default:return e.type}}function formatDispatch(e,t){return t.continuationToken?renderCliTaggedLine(e,{message:`resuming session ${t.continuationToken}`,tag:`session`,tone:`info`}):renderCliTaggedLine(e,{message:`starting a new session`,tag:`session`,tone:`info`})}function formatTurnDispatch(e,t){return t.turn.inputResponses!==void 0&&t.turn.message===void 0?renderCliTaggedLine(e,{message:`responding to pending input request${t.turn.inputResponses.length===1?``:`s`}`,tag:`session`,tone:`info`}):formatDispatch(e,t.session)}function formatSessionBoundary(e,t){let n=extractCurrentTurnBoundaryEvent(t),r=extractPendingRuntimeInputRequests(t);switch(n?.type){case`session.waiting`:return[renderCliTaggedLine(e,{message:r.length>0?`waiting for input approval/answer or the next message`:`waiting for the next message`,tag:`session`,tone:`success`})];case`session.completed`:return[renderCliTaggedLine(e,{message:`session completed; the next input starts a new session`,tag:`session`,tone:`success`})];case`session.failed`:{let t=n.data.details&&typeof n.data.details.name==`string`?n.data.details.name:void 0;return[renderCliTaggedLine(e,{message:t?`session failed (${t}): ${n.data.message}`:`session failed: ${n.data.message}`,tag:`session`,tone:`danger`}),renderCliTaggedLine(e,{message:`cleared; the next input starts a new session`,tag:`session`,tone:`warning`})]}default:return[]}}async function waitForInputLine(e,t,n={}){return await new Promise(r=>{let i=e.input,cleanup=()=>{e.off(`close`,handleClose),e.off(`line`,handleLine),i.off(`keypress`,handleKeypress)},handleClose=()=>{cleanup(),r(void 0)},handleLine=e=>{cleanup(),r(e)},handleKeypress=(t,i)=>{n.allowEscape!==!0||i.name!==`escape`||(cleanup(),e.write(null,{ctrl:!0,name:`u`}),r(ESCAPED_RUNTIME_INPUT_PROMPT))};e.setPrompt(t),e.once(`close`,handleClose),e.once(`line`,handleLine),n.allowEscape&&(emitKeypressEvents(i,e),i.on(`keypress`,handleKeypress)),e.prompt()})}async function runTurn(e){let t=e.turn,n={current:createTurnDisplayState()},i=new ReplSubagentStreamManager({displayStateRef:n,serverUrl:e.serverUrl,terminal:e.terminal,theme:e.theme}),ask=async t=>{e.terminal.startPrompt(e.rl,t);let n=await waitForInputLine(e.rl,t,{allowEscape:!0});return e.terminal.stopPrompt(),n};try{for(;;){let a=e.client.getSession();n.current=printDisplayLine({kind:`meta`,line:formatTurnDispatch(e.theme,{session:a,turn:t}),state:n.current,terminal:e.terminal});let o=await e.client.send({inputResponses:t.inputResponses,message:t.message,onEvent(t){n.current=renderTurnEvent({event:t,state:n.current,terminal:e.terminal,theme:e.theme}),t.type===`subagent.called`&&i.subscribe({sessionId:t.data.childSessionId,subagentName:t.data.name})},onResponseStart(t){t.sessionId&&a.sessionId!==t.sessionId&&(n.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(e.theme,{message:t.sessionId,tag:`session`,tone:`accent`}),state:n.current,terminal:e.terminal}),n.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(e.theme,{message:resolveDevelopmentServerResourceUrl({resource:createAshMessageStreamRoutePath(t.sessionId),serverUrl:e.serverUrl}).toString(),tag:`stream`,tone:`info`}),state:n.current,terminal:e.terminal}))}}),s=extractPendingRuntimeInputRequests(o.events);if(shouldDrainSubagentStreamsOnBoundary(s)){await i.waitForIdle();for(let t of formatSessionBoundary(e.theme,o.events))n.current=printDisplayLine({kind:`meta`,line:t,state:n.current,terminal:e.terminal});return`continue`}let c=await promptForRuntimeInputRequests({ask,print(t){n.current=printDisplayLine({kind:`meta`,line:t,state:n.current,terminal:e.terminal})},requests:s,theme:e.theme});if(c.kind===`aborted`)return`exit`;if(c.kind===`deferred`)return n.current=printDisplayLine({kind:`meta`,line:renderCliTaggedLine(e.theme,{message:`left pending input requests unresolved; send a new message to ignore them`,tag:`session`,tone:`warning`}),state:n.current,terminal:e.terminal}),`continue`;t={inputResponses:c.inputResponses}}}finally{await i.close()}}async function runDevelopmentRepl(e){let t=createDevelopmentTerminal(),n=createCliTheme({color:!0}),r=createInterface({input:process.stdin,output:t.output,terminal:!0});r.on(`SIGINT`,()=>{r.close()});let a=createDevClient({serverUrl:e.serverUrl});try{for(t.print(renderIntro(n,e)),t.print(``);;){t.print(``),t.startPrompt(r,`you> `);let i=await waitForInputLine(r,`you> `);if(t.stopPrompt(),i===void 0)return;let o=parseDevReplInput(i);switch(o.kind!==`empty`&&o.kind!==`exit`&&t.print(``),o.kind){case`empty`:continue;case`help`:t.print(renderIntro(n,e)),t.print(``);continue;case`exit`:return;case`new`:await a.clear(),t.print(renderCliTaggedLine(n,{message:`cleared; the next input starts a new session`,tag:`session`,tone:`warning`})),t.print(``);continue;case`message`:try{if(await runTurn({client:a,rl:r,serverUrl:e.serverUrl,terminal:t,theme:n,turn:{message:o.message}})===`exit`)return}catch(r){isVercelAuthChallenge(r)?t.printError(renderCliTaggedLine(n,{message:formatVercelAuthChallengeMessage({serverUrl:e.serverUrl}),tag:`auth`,tone:`warning`})):t.printError(renderCliTaggedLine(n,{message:toErrorMessage(r),tag:`error`,tone:`danger`}))}t.print(``)}}}finally{await a.close(),r.close(),t.dispose()}}export{createTurnDisplayState,formatContentEvent,formatEvent,renderTurnEvent,runDevelopmentRepl,shouldDrainSubagentStreamsOnBoundary};
|
|
@@ -6,7 +6,7 @@ export { ClientSession } from "#client/session.js";
|
|
|
6
6
|
export type { ClientAuth, ClientOptions, HeadersValue, HealthResult, MessageResult, OpenStreamOptions, SendMessageOptions, SendTurnInput, SessionState, TokenValue, } from "#client/types.js";
|
|
7
7
|
export type { AshAgentReducer, AshAgentReducerEvent, ClientInputRespondedEvent, ClientMessageFailedEvent, ClientMessageSubmittedEvent, } from "#client/reducer.js";
|
|
8
8
|
export type { AshMessageData, AshDynamicToolPart, AshMessageInputRequest, AshMessage, AshMessageMetadata, AshMessagePart, AshMessageToolMetadata, } from "#client/message-reducer.js";
|
|
9
|
-
export type { ActionResultStreamEvent, ActionsRequestedStreamEvent, AssistantStepFinishReason, CompactionCompletedStreamEvent, CompactionRequestedStreamEvent,
|
|
9
|
+
export type { ActionResultStreamEvent, ActionsRequestedStreamEvent, AssistantStepFinishReason, CompactionCompletedStreamEvent, CompactionRequestedStreamEvent, AuthorizationCompletedStreamEvent, ConnectionAuthorizationOutcome, AuthorizationRequiredStreamEvent, HandleMessageStreamEvent, InputRequestedStreamEvent, MessageAppendedStreamEvent, MessageCompletedStreamEvent, MessageReceivedStreamEvent, ReasoningAppendedStreamEvent, ReasoningCompletedStreamEvent, SessionCompletedStreamEvent, SessionFailedStreamEvent, SessionStartedStreamEvent, SessionWaitingStreamEvent, StepCompletedStreamEvent, StepFailedStreamEvent, StepStartedStreamEvent, SubagentCalledStreamEvent, SubagentChildEventStreamEvent, SubagentCompletedStreamEvent, SubagentStartedStreamEvent, TurnCompletedStreamEvent, TurnFailedStreamEvent, TurnStartedStreamEvent, } from "#protocol/message.js";
|
|
10
10
|
export { isCurrentTurnBoundaryEvent } from "#protocol/message.js";
|
|
11
11
|
export type { InputOption, InputRequest, InputResponse } from "#runtime/input/types.js";
|
|
12
12
|
export { inputOptionSchema, inputRequestSchema, inputResponseSchema, isInputRequest, isInputResponse, } from "#runtime/input/types.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getAuthoredModuleExport,materializeAuthoredModuleExport}from"#internal/authored-module.js";import{toErrorMessage}from"#shared/errors.js";import{
|
|
1
|
+
import{getAuthoredModuleExport,materializeAuthoredModuleExport}from"#internal/authored-module.js";import{join}from"node:path";import{toErrorMessage}from"#shared/errors.js";import{loadAuthoredModuleNamespace}from"#internal/authored-module-loader.js";async function loadModuleBackedDefinition(t){let n=getAuthoredModuleExport(await loadAuthoredModuleNamespace(join(t.agentRoot,t.source.logicalPath)),t.source);try{return await materializeAuthoredModuleExport(n)}catch(e){throw Error(`Failed to execute the ${t.kind} export "${t.source.exportName??`default`}" from "${t.source.logicalPath}": ${toErrorMessage(e)}`)}}export{loadModuleBackedDefinition};
|
|
@@ -10,6 +10,6 @@ export declare function serializeContext(ctx: AshContext): Record<string, unknow
|
|
|
10
10
|
* Deserializes a plain JSON record into a fresh context container.
|
|
11
11
|
*
|
|
12
12
|
* Each entry is matched to a registered {@link ContextKey} by name.
|
|
13
|
-
* Unknown entries (no registered key) are
|
|
13
|
+
* Unknown entries (no registered key) are dropped with a warning.
|
|
14
14
|
*/
|
|
15
15
|
export declare function deserializeContext(data: Record<string, unknown>): Promise<ContextContainer>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ContextContainer}from"#context/container.js";import{resolveKey}from"#context/key.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";function serializeContext(e){let t={};for(let[n,r]of e.entries())t[n.name]=n.codec?n.codec.serialize(r):r
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{ContextContainer}from"#context/container.js";import{resolveKey}from"#context/key.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";const log=createLogger(`context.serialize`);function serializeContext(e){let t={};for(let[n,r]of e.entries())try{t[n.name]=n.codec?n.codec.serialize(r):r}catch(e){throw logError(log,`failed to serialize context key`,e,{key:n.name}),e}return t}async function deserializeContext(e){let i=new ContextContainer,a=e[BundleKey.name];if(a!==void 0){let e=BundleKey.codec;if(e===void 0)throw Error(`Context key "ash.bundle" is missing a codec.`);i.set(BundleKey,await e.deserialize(a,i))}for(let[t,a]of Object.entries(e)){if(a===void 0||t===BundleKey.name)continue;let e=resolveKey(t);if(e===void 0){log.warn(`dropping unknown context key during deserialization`,{key:t});continue}try{i.set(e,e.codec?await e.codec.deserialize(a,i):a)}catch(e){throw logError(log,`failed to deserialize context key`,e,{key:t}),e}}return i}export{deserializeContext,serializeContext};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{join,relative,resolve}from"node:path";import{toErrorMessage}from"#shared/errors.js";import{createDiscoverErrorDiagnostic}from"#discover/diagnostics.js";import{classifySkillPackageEntry,classifySkillsDirectoryEntry,getDirectoryEntryType,getSupportedModuleBaseName,normalizeLogicalPath}from"#discover/filesystem.js";import{createDiskProjectSource}from"#discover/project-source.js";import{createModuleSourceRef,createPathDerivedSourceId,createSkillPackageSourceRef}from"#discover/manifest.js";import{readSortedDirectoryEntries}from"#discover/grammar.js";import{lowerSkillMarkdown}from"#internal/helpers/markdown.js";const DISCOVER_SKILLS_DIRECTORY_INVALID=`discover/skills-directory-invalid`,DISCOVER_SKILL_COLLISION=`discover/skill-collision`,DISCOVER_SKILL_ENTRY_NOT_DIRECTORY=`discover/skill-entry-not-directory`,DISCOVER_SKILL_FRONTMATTER_INVALID=`discover/skill-frontmatter-invalid`,DISCOVER_SKILL_MARKDOWN_MISSING=`discover/skill-markdown-missing`;async function discoverSkills(r){let a=r.source??createDiskProjectSource(),o=resolve(r.agentRoot),c=resolve(r.skillsDirectoryPath??join(o,`skills`)),l=normalizeLogicalPath(r.skillsLogicalPath??relative(o,c)),u=await a.stat(c);if(u===`missing`)return{diagnostics:[],skills:[]};if(u!==`directory`)return{diagnostics:[createDiscoverErrorDiagnostic({code:DISCOVER_SKILLS_DIRECTORY_INVALID,message:`Expected "${c}" to be a directory of authored skills.`,sourcePath:c})],skills:[]};let d=[],f=new Set,p=new Map,m=await readSortedDirectoryEntries(a,c);for(let t of m){let n=await discoverOneSkill({entryName:t.name,entryType:getDirectoryEntryType(t),skillsDirectoryPath:c,skillsLogicalPath:l,source:a});if(d.push(...n.diagnostics),n.skill===null||n.skillId===null||f.has(n.skillId))continue;let r=p.get(n.skillId);if(r!==void 0){d.push(createDiscoverErrorDiagnostic({code:DISCOVER_SKILL_COLLISION,message:`Found conflicting authored skill sources for "${n.skillId}": "${r.logicalPath}" and "${n.logicalPath}".`,sourcePath:join(c,n.skillId)})),f.add(n.skillId),p.delete(n.skillId);continue}p.set(n.skillId,{logicalPath:n.logicalPath,skill:n.skill})}return{diagnostics:d,skills:[...p.values()].map(e=>e.skill)}}async function discoverOneSkill(t){let n=join(t.skillsDirectoryPath,t.entryName);switch(classifySkillsDirectoryEntry(t.entryName,t.entryType)){case`skill-package-directory`:return discoverPackagedSkill({logicalSkillsPath:t.skillsLogicalPath,skillId:t.entryName,skillRootPath:n,source:t.source});case`flat-skill-markdown`:return discoverFlatMarkdownSkill({logicalSkillsPath:t.skillsLogicalPath,skillFileName:t.entryName,skillFilePath:n,source:t.source});case`flat-skill-module`:return discoverFlatModuleSkill({logicalSkillsPath:t.skillsLogicalPath,skillFileName:t.entryName});default:return{diagnostics:[createDiscoverErrorDiagnostic({code:DISCOVER_SKILL_ENTRY_NOT_DIRECTORY,message:`Expected "${n}" to be a skill directory containing SKILL.md or a flat ".md", ".ts", ".cts", ".mts", ".js", ".cjs", or ".mjs" skill file.`,sourcePath:n})],logicalPath:normalizeLogicalPath(join(t.skillsLogicalPath,t.entryName)),skill:null,skillId:null}}}async function discoverPackagedSkill(t){let n=join(t.skillRootPath,`SKILL.md`),r=normalizeLogicalPath(join(t.logicalSkillsPath,t.skillId,`SKILL.md`));if(await t.source.stat(n)!==`file`)return{diagnostics:[createDiscoverErrorDiagnostic({code:DISCOVER_SKILL_MARKDOWN_MISSING,message:`Expected "${n}" to exist for the "${t.skillId}" skill.`,sourcePath:t.skillRootPath})],logicalPath:r,skill:null,skillId:null};let a;try{a=lowerSkillMarkdown(await t.source.readTextFile(n))}catch(e){return{diagnostics:[createDiscoverErrorDiagnostic({code:DISCOVER_SKILL_FRONTMATTER_INVALID,message:formatSkillDiscoveryError(n,e),sourcePath:n})],logicalPath:r,skill:null,skillId:null}}let o=await discoverSkillPackagePaths(t.source,t.skillRootPath),s={description:a.description,logicalPath:r,markdown:a.markdown,name:t.skillId,rootPath:t.skillRootPath,skillFilePath:n,skillId:t.skillId,sourceId:createPathDerivedSourceId(r)};return o.assetsPath!==void 0&&(s.assetsPath=o.assetsPath),a.license!==void 0&&(s.license=a.license),a.metadata!==void 0&&(s.metadata=a.metadata),o.referencesPath!==void 0&&(s.referencesPath=o.referencesPath),o.scriptsPath!==void 0&&(s.scriptsPath=o.scriptsPath),{diagnostics:[],logicalPath:r,skill:createSkillPackageSourceRef(s),skillId:t.skillId}}async function discoverFlatMarkdownSkill(t){let n=stripMarkdownExtension(t.skillFileName),r=normalizeLogicalPath(join(t.logicalSkillsPath,t.skillFileName)),a;try{a=lowerSkillMarkdown(await t.source.readTextFile(t.skillFilePath),{slug:n})}catch(e){return{diagnostics:[createDiscoverErrorDiagnostic({code:DISCOVER_SKILL_FRONTMATTER_INVALID,message:formatSkillDiscoveryError(t.skillFilePath,e),sourcePath:t.skillFilePath})],logicalPath:r,skill:null,skillId:null}}return{diagnostics:[],logicalPath:r,skill:{definition:a,sourceKind:`markdown`,logicalPath:r,sourceId:createPathDerivedSourceId(r)},skillId:n}}async function discoverFlatModuleSkill(t){let n=getSupportedModuleBaseName(t.skillFileName),r=normalizeLogicalPath(join(t.logicalSkillsPath,t.skillFileName));return n===null?{diagnostics:[],logicalPath:r,skill:null,skillId:null}:{diagnostics:[],logicalPath:r,skill:createModuleSourceRef({logicalPath:r}),skillId:n}}async function discoverSkillPackagePaths(t,n){let r=await t.readDirectory(n),i={};for(let t of r)if(t.isDirectory())switch(classifySkillPackageEntry(t.name,getDirectoryEntryType(t))){case`skill-assets-directory`:i.assetsPath=join(n,t.name);break;case`skill-references-directory`:i.referencesPath=join(n,t.name);break;case`skill-scripts-directory`:i.scriptsPath=join(n,t.name);break;default:break}return i}function formatSkillDiscoveryError(e,t){return`Invalid authored skill frontmatter in "${e}": ${toErrorMessage(t)}`}function stripMarkdownExtension(e){return e.endsWith(`.md`)?e.slice(0,-3):e}export{DISCOVER_SKILLS_DIRECTORY_INVALID,DISCOVER_SKILL_COLLISION,DISCOVER_SKILL_ENTRY_NOT_DIRECTORY,DISCOVER_SKILL_FRONTMATTER_INVALID,DISCOVER_SKILL_MARKDOWN_MISSING,discoverSkills};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{writeDurableSession}from"#execution/durable-session-store.js";import{createSession}from"#execution/session.js";async function createSessionStep(e){"use step";let t=await getCompiledRuntimeAgentBundle({compiledArtifactsSource:e.compiledArtifactsSource,nodeId:e.nodeId});return await writeDurableSession({session:createSession({compactionOverrides:{thresholdPercent:t.resolvedAgent.config.compaction?.thresholdPercent},continuationToken:e.continuationToken,sessionId:e.sessionId,turnAgent:t.turnAgent}),writable:e.sessionWritable})}export{createSessionStep};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{AuthKey,CapabilitiesKey,InitiatorAuthKey}from"#context/keys.js";import{createSubagentCalledEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{callAdapterEventHandler}from"#channel/adapter.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{readDurableSession,writeDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession}from"#execution/session.js";import{deserializeContext}from"#context/serialize.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{getPendingRuntimeActionBatch,recordPendingSubagentChildToken}from"#harness/runtime-actions.js";import{resolveRemoteAgentForAction,startRemoteAgentSession}from"#execution/remote-agent-dispatch.js";import{buildSubagentRunInput}from"#execution/subagent-tool.js";import{createWorkflowRuntime,workflowEntryReference}from"#execution/workflow-runtime.js";const log=createLogger(`execution.dispatch-runtime-actions`);async function dispatchRuntimeActionsStep(e){"use step";let s=await readDurableSession(e.sessionState),u=getPendingRuntimeActionBatch(s.state);if(u===void 0||u.actions.length===0)return{results:[],sessionState:e.sessionState};let d=await deserializeContext(e.serializedContext),f=d.require(BundleKey),p=hydrateDurableSession({compactionOverrides:{thresholdPercent:f.resolvedAgent.config.compaction?.thresholdPercent},durable:s,turnAgent:f.turnAgent}),m=d.require(ChannelKey),h=d.get(AuthKey)??null,g=d.get(CapabilitiesKey),_=d.get(InitiatorAuthKey)??null,v=e.parentWritable.getWriter(),y=buildAdapterContext(m,d),b=p,x=[];try{for(let n of u.actions){let r,i,s,c;switch(n.kind){case`subagent-call`:{let e=createWorkflowRuntime({compiledArtifactsSource:f.compiledArtifactsSource,nodeId:n.nodeId}),{childContinuationToken:t,runInput:a}=buildSubagentRunInput({action:n,auth:h,batchEvent:u.event,capabilities:g,initiatorAuth:_,session:p}),o=await e.run(a);b=recordPendingSubagentChildToken({callId:n.callId,childContinuationToken:t,session:b}),r=o.sessionId,i=n.name,c=n.subagentName;break}case`remote-agent-call`:{let a;try{a=resolveRemoteAgentForAction({nodeId:n.nodeId,remoteAgentName:n.remoteAgentName,registry:f.subagentRegistry.subagentsByNodeId}),r=await startRemoteAgentSession({action:n,callbackBaseUrl:e.callbackBaseUrl,remote:a,session:p})}catch(e){logError(log,`remote agent start failed`,e,{remoteAgentName:n.remoteAgentName,nodeId:n.nodeId,callId:n.callId}),x.push(createRemoteAgentStartFailureResult({action:n,error:e}));continue}i=n.name,s={url:a.url},c=n.remoteAgentName;break}default:throw Error(`Unsupported runtime action kind "${n.kind}" in workflow runtime.`)}let l=await callAdapterEventHandler(m,createSubagentCalledEvent({callId:n.callId,childSessionId:r,name:i,remote:s,sequence:u.event.sequence,sessionId:p.sessionId,toolName:c,turnId:u.event.turnId,workflowId:workflowEntryReference.workflowId}),y);await v.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(l)))}}finally{v.releaseLock()}return{results:x,sessionState:b===p?e.sessionState:await writeDurableSession({session:b,writable:e.sessionWritable})}}function createRemoteAgentStartFailureResult(e){return{callId:e.action.callId,isError:!0,kind:`subagent-result`,output:{code:`REMOTE_AGENT_START_FAILED`,message:toErrorMessage(e.error)},subagentName:e.action.remoteAgentName}}export{dispatchRuntimeActionsStep};
|
|
@@ -31,7 +31,7 @@ export declare const DURABLE_SESSION_VERSION = 1;
|
|
|
31
31
|
* `hasProxyInputRequests` (a closed-contract short-circuit that lets
|
|
32
32
|
* the driver skip a per-delivery proxy-routing step when no
|
|
33
33
|
* descendant subagent is active), and `emissionState` (so workflow-body
|
|
34
|
-
*
|
|
34
|
+
* framework steps can stamp protocol events
|
|
35
35
|
* with `{ turnId, sequence, stepIndex }` without reading the full
|
|
36
36
|
* durable session). All other control-plane state travels via
|
|
37
37
|
* {@link import("#execution/next-driver-action.js").NextDriverAction}.
|
|
@@ -22,6 +22,7 @@ export type NextDriverAction = {
|
|
|
22
22
|
readonly kind: "park";
|
|
23
23
|
readonly sessionState: DurableSessionState;
|
|
24
24
|
readonly serializedContext: Record<string, unknown>;
|
|
25
|
+
readonly authorizationNames?: readonly string[];
|
|
25
26
|
} | {
|
|
26
27
|
readonly kind: "dispatch-runtime-actions";
|
|
27
28
|
readonly pendingActionKeys: readonly string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{jsonSchema}from"ai";import{createToolLoopHarness}from"#harness/tool-loop.js";import{resolveRuntimeModelReference}from"#runtime/agent/resolve-model.js";import{findRegisteredRuntimeTool}from"#runtime/tools/registry.js";import{createToolCompactionHandler}from"#execution/tool-compaction.js";function createExecutionNodeStep(e){let t=createRuntimeModelResolver(e.compiledArtifactsSource),
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{jsonSchema}from"ai";import{createToolLoopHarness}from"#harness/tool-loop.js";import{resolveRuntimeModelReference}from"#runtime/agent/resolve-model.js";import{findRegisteredRuntimeTool}from"#runtime/tools/registry.js";import{createToolCompactionHandler}from"#execution/tool-compaction.js";const log=createLogger(`execution.node-step`);function createExecutionNodeStep(e){let t=createRuntimeModelResolver(e.compiledArtifactsSource),n=createNodeHarnessTools({node:e.node}),i=collectResolvedTools(e.node),a=createToolCompactionHandler(i),s=collectRetentionPolicies(i);return createToolLoopHarness({capabilities:e.capabilities,emit:e.emit,mode:e.mode,onCompaction:a,resolveModel:t,retentionPolicies:s,runtimeIdentity:buildRuntimeIdentity(e.node),tools:n})}function buildRuntimeIdentity(e){let n=resolveInstalledPackageInfo(),r={agentId:e.turnAgent.id,agentName:e.agent.config?.name,ashVersion:n.version,modelId:e.turnAgent.model.id},i=process.env.VERCEL_GIT_COMMIT_SHA?.trim(),a=process.env.VERCEL_GIT_COMMIT_REF?.trim(),o=process.env.VERCEL_DEPLOYMENT_CREATED_AT?.trim();return i||a||o?{...r,build:{deployedAt:o||void 0,gitBranch:a||void 0,gitSha:i||void 0}}:r}function createRuntimeModelResolver(e){return t=>resolveRuntimeModelReference(t,{compiledArtifactsSource:e})}function collectResolvedTools(e){return[...e.toolRegistry.toolsByName.values()].map(e=>e.definition)}function collectRetentionPolicies(e){let t=new Map;for(let n of e){let e=n.retentionPolicy;e===void 0||e===`auto`||t.set(n.name,e)}return t}function createNodeHarnessTools(e){let t=new Map;for(let n of e.node.turnAgent.tools){let r=resolveHarnessToolDefinition({node:e.node,tool:n});r!==null&&t.set(n.name,r)}return t}function resolveHarnessToolDefinition(e){if(e.tool.kind===`subagent`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,runtimeAction:{kind:`subagent-call`,nodeId:e.tool.nodeId,subagentName:e.tool.name}};if(e.tool.kind===`remote`)return{description:e.tool.description??``,inputSchema:jsonSchema(e.tool.inputSchema??{}),name:e.tool.name,runtimeAction:{kind:`remote-agent-call`,nodeId:e.tool.nodeId,remoteAgentName:e.tool.name,subagentName:e.tool.name}};let t=findRegisteredRuntimeTool(e.node.toolRegistry,e.tool.name);if(t===null)return log.warn(`declared tool is not registered — omitting from toolset`,{toolName:e.tool.name,nodeId:e.node.nodeId}),null;let r=t.definition;return{approvalKey:r.approvalKey,description:r.description,execute:r.execute,inputSchema:r.inputStandardSchema??jsonSchema(r.inputSchema??{}),name:r.name,needsApproval:r.needsApproval,toModelOutput:r.toModelOutput}}export{createExecutionNodeStep,createNodeHarnessTools};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{SessionCallbackKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{parseSessionCallback}from"#channel/session-callback.js";const log=createLogger(`execution.session-callback`);async function fireSessionCallbackStep(e){"use step";let n=e.serializedContext[`ash.sessionId`]??``,i=e.serializedContext[SessionCallbackKey.name];if(i!==void 0)try{let t=parseSerializedSessionCallback(i),r=e.status===`completed`?{callId:t.callId,kind:`session.completed`,output:e.output??``,sessionId:n,subagentName:t.subagentName}:{callId:t.callId,error:{code:`SESSION_FAILED`,message:toErrorMessage(e.error)},kind:`session.failed`,sessionId:n,subagentName:t.subagentName},a=await fetch(t.url,{body:JSON.stringify(r),headers:{"content-type":`application/json`},method:`POST`,signal:AbortSignal.timeout(3e4)});if(!a.ok)throw Error(`Session callback failed with HTTP ${a.status}.`)}catch(e){throw log.error(`failed to post session callback`,{error:e,sessionId:n}),e}}function parseSerializedSessionCallback(e){let t=parseSessionCallback(e);if(!t.ok)throw Error(`Serialized session callback is invalid.`,{cause:t.cause});return t.callback}export{fireSessionCallbackStep};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{normalizeSerializableError}from"#execution/workflow-errors.js";import{turnStep}from"#execution/workflow-steps.js";async function turnWorkflow(n){"use workflow";let r=n.sessionState,i=n.serializedContext,a=n.delivery,o=n.parentWritable,s=n.sessionWritable;try{for(;;){let e=await turnStep({input:a,parentWritable:o,serializedContext:i,sessionState:r,sessionWritable:s});if(r=e.sessionState,i=e.serializedContext,e.action===`done`){await notifyDriverStep({completionToken:n.completionToken,payload:{action:{kind:`done`,output:e.output??``,serializedContext:i,sessionState:r},kind:`turn-result`}});return}if(e.action===`park`){let t=e.pendingRuntimeActionKeys;if(!(t!==void 0||e.hasPendingAuthorization||e.hasPendingInputBatch&&n.capabilities?.requestInput===!0||n.mode===`conversation`))throw Error("Task mode cannot wait for follow-up input (`next: null`).");let a=t===void 0?{kind:`park`,serializedContext:i,sessionState:r,authorizationNames:e.authorizationNames}:{kind:`dispatch-runtime-actions`,pendingActionKeys:t,serializedContext:i,sessionState:r};await notifyDriverStep({completionToken:n.completionToken,payload:{action:a,kind:`turn-result`}});return}a=void 0}}catch(t){throw await notifyDriverStep({completionToken:n.completionToken,payload:{error:normalizeSerializableError(t),kind:`turn-error`}}),t}}async function notifyDriverStep(e){"use step";let{resumeHook:t}=await import(`#compiled/@workflow/core/runtime.js`);await t(e.completionToken,e.payload)}export{notifyDriverStep,turnWorkflow};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ASH_SESSION_STREAM_NAMESPACE}from"#execution/durable-session-store.js";import{accumulateRuntimeActionResults}from"#harness/runtime-actions.js";import{resolveVercelProductionCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{normalizeSerializableError,rebuildSerializableError}from"#execution/workflow-errors.js";import{dispatchTurnStep,emitTerminalSessionFailureStep,routeProxiedDeliverStep,runProxyInputRequestStep}from"#execution/workflow-steps.js";import{createHook,getWorkflowMetadata,getWritable}from"#compiled/@workflow/core/index.js";import{coalesceDeliveries}from"#harness/messages.js";import{notifyDelegatedParentStep}from"#execution/delegated-parent-notification.js";import{createDelegatedSubagentErrorResult,createDelegatedSubagentSuccessResult}from"#execution/delegated-parent-result.js";import{createSessionStep}from"#execution/create-session-step.js";import{dispatchRuntimeActionsStep}from"#execution/dispatch-runtime-actions-step.js";import{fireSessionCallbackStep}from"#execution/session-callback-step.js";async function workflowEntry(t){"use workflow";let{workflowRunId:n}=getWorkflowMetadata(),i=t.serializedContext[`ash.continuationToken`]||``,a=t.serializedContext[`ash.mode`],s=t.serializedContext[`ash.capabilities`],c=t.serializedContext[`ash.bundle`];t.serializedContext[`ash.sessionId`]=n;let l=getWritable(),d=getWritable({namespace:ASH_SESSION_STREAM_NAMESPACE});try{let e=await createSessionStep({compiledArtifactsSource:c.source,continuationToken:i,nodeId:c.nodeId,sessionId:n,sessionWritable:d});return await runDriverLoop({capabilities:s,driverWritable:l,initialInput:{kind:`deliver`,payloads:[{message:t.input.message,modelContext:t.input.modelContext}]},mode:a,serializedContext:t.serializedContext,sessionState:e,sessionWritable:d})}catch(e){throw await emitTerminalSessionFailureStep({error:normalizeSerializableError(e),parentWritable:l,serializedContext:t.serializedContext}),await fireSessionCallbackStep({error:normalizeSerializableError(e),serializedContext:t.serializedContext,status:`failed`}),await notifyDelegatedParentStep({result:createDelegatedSubagentErrorResult(t.serializedContext,e),serializedContext:t.serializedContext}),e}}async function runDriverLoop(e){let t=createHook({token:`${e.sessionState.sessionId}:auth`}),r=t[Symbol.asyncIterator](),i=0,a=await dispatchAndAwaitTurn({capabilities:e.capabilities,delivery:e.initialInput,mode:e.mode,parentWritable:e.driverWritable,serializedContext:e.serializedContext,sessionState:e.sessionState,sessionWritable:e.sessionWritable,turnGeneration:++i});if(a.kind===`done`)return await closeHookIterator(r),await disposeHook(t),await finalizeDone({action:a,driverWritable:e.driverWritable});if(!a.sessionState.continuationToken)throw Error("Cannot park: no continuation token available. The channel must post the first message during the initial turn (anchoring the session) or `send()` must be called with an explicit continuationToken.");let o=a.sessionState.continuationToken,s=createHook({token:o}),c=s[Symbol.asyncIterator](),u=null,d=[],getNextPromise=()=>(u??=c.next(),u),consumeNext=()=>{u=null},rekeyHook=async e=>{e===o||!e||(await closeHookIterator(c),await disposeHook(s),o=e,s=createHook({token:o}),c=s[Symbol.asyncIterator](),u=null)};try{for(;;)switch(a.kind){case`done`:return await finalizeDone({action:a,driverWritable:e.driverWritable});case`dispatch-runtime-actions`:{let t=await dispatchRuntimeActionsStep({callbackBaseUrl:resolveVercelProductionCallbackBaseUrl()??getWorkflowMetadata().url,parentWritable:e.driverWritable,serializedContext:a.serializedContext,sessionState:a.sessionState,sessionWritable:e.sessionWritable}),r=await waitForPendingRuntimeActionResults({bufferedDeliveries:d,consumeNext,getNextPromise,initialResults:t.results,parentWritable:e.driverWritable,pendingActionKeys:a.pendingActionKeys,rekeyHook,serializedContext:a.serializedContext,sessionState:t.sessionState,sessionWritable:e.sessionWritable});if(r===null)return{output:``};a=await dispatchAndAwaitTurn({capabilities:e.capabilities,delivery:{kind:`runtime-action-result`,results:r.results},mode:e.mode,parentWritable:e.driverWritable,serializedContext:r.serializedContext,sessionState:r.sessionState,sessionWritable:e.sessionWritable,turnGeneration:++i}),await rekeyHook(a.sessionState.continuationToken);break}case`park`:{if(a.authorizationNames&&a.authorizationNames.length>0){let t=a.authorizationNames.length,n=[];for(;n.length<t;){let e=await r.next();if(e.done)break;e.value.kind===`deliver`&&n.push(...e.value.payloads)}a=await dispatchAndAwaitTurn({capabilities:e.capabilities,delivery:{kind:`deliver`,payloads:n},mode:e.mode,parentWritable:e.driverWritable,serializedContext:a.serializedContext,sessionState:a.sessionState,sessionWritable:e.sessionWritable,turnGeneration:++i}),await rekeyHook(a.sessionState.continuationToken);break}let t=await waitForNextDeliver({bufferedDeliveries:d,consumeNext,getNextPromise});if(t===null)return{output:``};let n=await routeDeliverForChildren({auth:t.auth,parentWritable:e.driverWritable,payloads:t.payloads,sessionState:a.sessionState});if(n===void 0)continue;a=await dispatchAndAwaitTurn({capabilities:e.capabilities,delivery:{auth:t.auth,kind:`deliver`,payloads:[n]},mode:e.mode,parentWritable:e.driverWritable,serializedContext:a.serializedContext,sessionState:a.sessionState,sessionWritable:e.sessionWritable,turnGeneration:++i}),await rekeyHook(a.sessionState.continuationToken);break}}}finally{await closeHookIterator(c),await disposeHook(s),await closeHookIterator(r),await disposeHook(t)}}async function finalizeDone(e){return await fireSessionCallbackStep({output:e.action.output,serializedContext:e.action.serializedContext,status:`completed`}),await notifyDelegatedParentStep({result:createDelegatedSubagentSuccessResult(e.action.serializedContext,e.action.output),serializedContext:e.action.serializedContext}),{output:e.action.output}}async function dispatchAndAwaitTurn(e){let t=`ash://turn/${e.sessionState.sessionId}/${e.turnGeneration}`,n=createHook({token:t});try{await dispatchTurnStep({capabilities:e.capabilities,completionToken:t,delivery:e.delivery,mode:e.mode,parentWritable:e.parentWritable,serializedContext:e.serializedContext,sessionState:e.sessionState,sessionWritable:e.sessionWritable});let r=await awaitHookPayload(n);if(r.kind===`turn-error`)throw rebuildSerializableError(r.error);return r.action}finally{await disposeHook(n)}}async function awaitHookPayload(e){for await(let t of e)return t;throw Error(`Turn completion hook closed before delivering a result.`)}async function waitForPendingRuntimeActionResults(e){let n=e.sessionState,r=e.serializedContext,i=await accumulateRuntimeActionResults({bufferedDeliveries:e.bufferedDeliveries,async getNext(){for(;;){let t=await e.getNextPromise();if(e.consumeNext(),t.done)return null;let i=t.value;if(i.kind===`deliver`){let t=await routeDeliverForChildren({auth:i.auth,parentWritable:e.parentWritable,payloads:i.payloads,sessionState:n});if(t===void 0)continue;return{kind:`deliver`,value:{...i,payloads:[t]}}}if(i.kind===`runtime-action-result`)return{kind:`runtime-action-result`,results:i.results};let a=await runProxyInputRequestStep({hookPayload:i,parentWritable:e.parentWritable,serializedContext:r,sessionState:n,sessionWritable:e.sessionWritable});n=a.sessionState,r=a.serializedContext,await e.rekeyHook(n.continuationToken)}},initialResults:e.initialResults,pendingActionKeys:e.pendingActionKeys});return i===null?null:{results:i,serializedContext:r,sessionState:n}}async function routeDeliverForChildren(e){let t=coalescePayloads(e.payloads);return e.sessionState.hasProxyInputRequests?(await routeProxiedDeliverStep({auth:e.auth,parentWritable:e.parentWritable,payload:t,sessionState:e.sessionState})).remainder:t}async function waitForNextDeliver(e){if(e.bufferedDeliveries.length>0)return coalesceDeliveries(e.bufferedDeliveries.splice(0));for(;;){let t=await e.getNextPromise();if(e.consumeNext(),t.done)return null;if(t.value.kind!==`deliver`)continue;let n=t.value;for(;;){let t=await takeReadyPayload(e.getNextPromise());if(t===NO_READY_MESSAGE||(e.consumeNext(),t.done))break;t.value.kind===`deliver`&&(n=coalesceDeliveries([n,t.value]))}return n}}function coalescePayloads(e){if(e.length===0)return{};if(e.length===1)return e[0]??{};let t={},n=[];for(let r of e){for(let[e,n]of Object.entries(r))e!==`inputResponses`&&n!==void 0&&(t[e]=n);r.inputResponses!==void 0&&n.push(...r.inputResponses)}return n.length>0&&(t.inputResponses=n),t}const NO_READY_MESSAGE=Symbol(`no-ready-message`);async function takeReadyPayload(e){return await Promise.resolve(),await Promise.race([e,Promise.resolve(NO_READY_MESSAGE)])}async function closeHookIterator(e){typeof e.return==`function`&&await e.return(void 0)}async function disposeHook(e){let t=e.dispose;if(typeof t==`function`){await t.call(e);return}let n=e[Symbol.dispose];typeof n==`function`&&await n.call(e)}export{workflowEntry};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{RuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{RuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{serializeContext}from"#context/serialize.js";import{getHookByToken,getRun,resumeHook,start}from"#compiled/@workflow/core/runtime.js";import{HookNotFoundError}from"#compiled/@workflow/errors/index.js";import{buildRunContext}from"#execution/runtime-context.js";const WORKFLOW_ENTRY_NAME=`workflowEntry`,TURN_WORKFLOW_NAME=`turnWorkflow`,ASH_PACKAGE_INFO=resolveInstalledPackageInfo(),LATEST_DEPLOYMENT_UNSUPPORTED_MESSAGE=`deploymentId 'latest' requires a World that implements resolveLatestDeploymentId()`,STABLE_WORKFLOW_NAMES=new Set([WORKFLOW_ENTRY_NAME,TURN_WORKFLOW_NAME]),STABLE_ID_BASE=ASH_PACKAGE_INFO.name,log=createLogger(`execution.workflow-runtime`),workflowEntryReference={workflowId:`workflow//${STABLE_ID_BASE}//${WORKFLOW_ENTRY_NAME}`},turnWorkflowReference={workflowId:`workflow//${STABLE_ID_BASE}//${TURN_WORKFLOW_NAME}`};function createWorkflowRuntime(e){return{async run(n){let r=serializeContext(buildRunContext({bundle:await getCompiledRuntimeAgentBundle({compiledArtifactsSource:e.compiledArtifactsSource,nodeId:e.nodeId}),run:n})),o;try{o=await startWorkflowPreferLatest(workflowEntryReference,[{input:n.input,serializedContext:r}])}catch(e){throw logError(log,`failed to start workflow run`,e,{continuationToken:n.continuationToken}),e}return{continuationToken:n.continuationToken??o.runId,events:parseNdjsonStream(getRun(o.runId).getReadable()),sessionId:o.runId}},async deliver(e){let r={auth:e.auth,kind:`deliver`,payloads:[e.payload]};try{let t=normalizeWorkflowHook(await getHookByToken(e.continuationToken));return await resumeHook(e.continuationToken,r),{sessionId:t.runId}}catch(r){throw HookNotFoundError.is(r)?new RuntimeNoActiveSessionError(e.continuationToken):(logError(log,`failed to deliver to active session`,r,{continuationToken:e.continuationToken}),r)}},async getEventStream(e,t){return parseNdjsonStream(getRun(e).getReadable({startIndex:t?.startIndex}))}}}async function startWorkflowPreferLatest(e,t){try{return await start(e,t,{deploymentId:`latest`})}catch(n){if(!isLatestDeploymentUnsupportedError(n))throw n;return await start(e,t)}}function isLatestDeploymentUnsupportedError(e){return e instanceof Error&&e.message.includes(`deploymentId 'latest' requires a World that implements resolveLatestDeploymentId()`)}function normalizeWorkflowHook(e){if(typeof e!=`object`||!e||!(`runId`in e))throw Error(`Workflow hook did not include a run id.`);let t=e.runId;if(typeof t!=`string`||t.length===0)throw Error(`Workflow hook did not include a run id.`);return{runId:t}}function parseNdjsonStream(e){let t=new TextDecoder,n=``;return new ReadableStream({async start(r){let i=e.getReader();try{for(;;){let{value:e,done:a}=await i.read();if(a){let e=n.trim();e.length>0&&r.enqueue(JSON.parse(e)),r.close();return}n+=t.decode(e,{stream:!0});for(let e=n.indexOf(`
|
|
2
2
|
`);e!==-1;e=n.indexOf(`
|
|
3
3
|
`)){let t=n.slice(0,e).trim();n=n.slice(e+1),t.length>0&&r.enqueue(JSON.parse(t))}}}catch(e){r.error(e)}finally{i.releaseLock()}}})}export{LATEST_DEPLOYMENT_UNSUPPORTED_MESSAGE,STABLE_WORKFLOW_NAMES,createWorkflowRuntime,startWorkflowPreferLatest,turnWorkflowReference,workflowEntryReference};
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { DeliverPayload, HookPayload, SessionAuthContext, SubagentInputRequestHookPayload } from "#channel/types.js";
|
|
2
2
|
import { deserializeContext } from "#context/serialize.js";
|
|
3
3
|
import type { HarnessSession } from "#harness/types.js";
|
|
4
|
-
import { type PendingConnectionToolCall } from "#runtime/framework-tools/pending-connection-tool-calls.js";
|
|
5
|
-
import { type PendingCodeModeConnectionAuthorization } from "#runtime/framework-tools/code-mode-connection-auth.js";
|
|
6
4
|
import { type DurableSessionSnapshot, type DurableSessionState } from "#execution/durable-session-store.js";
|
|
7
5
|
import { type TurnWorkflowInput } from "#execution/turn-workflow.js";
|
|
8
6
|
/**
|
|
9
7
|
* Result of one durable harness step, consumed by the turn workflow.
|
|
10
8
|
*
|
|
11
|
-
* `park` carries `hasPendingInputBatch` and
|
|
12
|
-
* so the turn workflow can pick the right
|
|
9
|
+
* `park` carries `hasPendingInputBatch`, `hasPendingAuthorization`, and
|
|
10
|
+
* `pendingRuntimeActionKeys` so the turn workflow can pick the right
|
|
13
11
|
* {@link import("#execution/next-driver-action.js").NextDriverAction}
|
|
14
12
|
* arm without re-reading the session.
|
|
15
13
|
*/
|
|
@@ -20,16 +18,12 @@ export type DurableStepResult = {
|
|
|
20
18
|
readonly sessionState: DurableSessionState;
|
|
21
19
|
} | {
|
|
22
20
|
readonly action: "park";
|
|
21
|
+
readonly authorizationNames?: readonly string[];
|
|
22
|
+
readonly hasPendingAuthorization: boolean;
|
|
23
23
|
readonly hasPendingInputBatch: boolean;
|
|
24
24
|
readonly pendingRuntimeActionKeys?: readonly string[];
|
|
25
25
|
readonly serializedContext: Record<string, unknown>;
|
|
26
26
|
readonly sessionState: DurableSessionState;
|
|
27
|
-
} | {
|
|
28
|
-
readonly action: "await-authorization";
|
|
29
|
-
readonly pendingCodeModeConnectionAuthorization?: PendingCodeModeConnectionAuthorization;
|
|
30
|
-
readonly pendingToolCalls: readonly PendingConnectionToolCall[];
|
|
31
|
-
readonly serializedContext: Record<string, unknown>;
|
|
32
|
-
readonly sessionState: DurableSessionState;
|
|
33
27
|
};
|
|
34
28
|
/** Input for one atomic harness step inside a durable `"use step"`. */
|
|
35
29
|
export interface TurnStepInput {
|