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.
Files changed (149) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/docs/public/auth-and-route-protection.md +95 -17
  3. package/dist/docs/public/channels/README.md +9 -3
  4. package/dist/src/channel/send.js +1 -1
  5. package/dist/src/cli/dev/repl.js +2 -2
  6. package/dist/src/client/index.d.ts +1 -1
  7. package/dist/src/compiler/normalize-helpers.js +1 -1
  8. package/dist/src/context/serialize.d.ts +1 -1
  9. package/dist/src/context/serialize.js +1 -1
  10. package/dist/src/discover/skills.js +1 -1
  11. package/dist/src/execution/create-session-step.js +1 -1
  12. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  13. package/dist/src/execution/durable-session-store.d.ts +1 -1
  14. package/dist/src/execution/next-driver-action.d.ts +1 -0
  15. package/dist/src/execution/node-step.js +1 -1
  16. package/dist/src/execution/session-callback-step.js +1 -1
  17. package/dist/src/execution/turn-workflow.js +1 -1
  18. package/dist/src/execution/workflow-entry.js +1 -1
  19. package/dist/src/execution/workflow-runtime.js +1 -1
  20. package/dist/src/execution/workflow-steps.d.ts +4 -10
  21. package/dist/src/execution/workflow-steps.js +1 -1
  22. package/dist/src/harness/attachment-staging.js +1 -1
  23. package/dist/src/harness/authorization.d.ts +81 -0
  24. package/dist/src/harness/authorization.js +1 -0
  25. package/dist/src/harness/code-mode-approval.js +1 -1
  26. package/dist/src/harness/code-mode-connection-auth-state.d.ts +15 -0
  27. package/dist/src/harness/code-mode-connection-auth-state.js +1 -0
  28. package/dist/src/{execution/code-mode-authorization-lifecycle.d.ts → harness/code-mode-lifecycle.d.ts} +7 -6
  29. package/dist/src/harness/code-mode-lifecycle.js +1 -0
  30. package/dist/src/harness/code-mode.js +1 -1
  31. package/dist/src/harness/emission.js +1 -1
  32. package/dist/src/harness/runtime-actions.js +1 -1
  33. package/dist/src/harness/step-hooks.js +1 -1
  34. package/dist/src/harness/tool-loop.js +1 -1
  35. package/dist/src/internal/application/package.js +1 -1
  36. package/dist/src/internal/logging.d.ts +10 -0
  37. package/dist/src/internal/logging.js +1 -1
  38. package/dist/src/internal/nitro/host/build-application.js +1 -1
  39. package/dist/src/internal/nitro/host/code-mode-runtime-dependency-plugin.d.ts +1 -0
  40. package/dist/src/internal/nitro/host/code-mode-runtime-dependency-plugin.js +1 -0
  41. package/dist/src/internal/nitro/host/create-application-nitro.js +1 -1
  42. package/dist/src/internal/nitro/host/dev-authored-source-watcher.js +1 -1
  43. package/dist/src/internal/nitro/host/start-development-server.js +1 -1
  44. package/dist/src/internal/nitro/routes/channel-dispatch.js +1 -1
  45. package/dist/src/internal/nitro/routes/info.d.ts +4 -2
  46. package/dist/src/internal/nitro/routes/info.js +1 -1
  47. package/dist/src/internal/workflow-bundle/builder-support.js +1 -1
  48. package/dist/src/internal/workflow-bundle/builder.js +2 -2
  49. package/dist/src/node_modules/.pnpm/@jitl_quickjs-ffi-types@0.32.0/node_modules/@jitl/quickjs-ffi-types/dist/index.js +1 -0
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/chunk-TAV5CUKK.js +1 -0
  68. package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/chunk-V2S4ZYJR.js +6 -0
  69. package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/index.js +1 -0
  70. package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/module-ES6BEMUI.js +1 -0
  71. package/dist/src/node_modules/.pnpm/quickjs-emscripten-core@0.32.0/node_modules/quickjs-emscripten-core/dist/module-asyncify-2EFITU5U.js +1 -0
  72. package/dist/src/node_modules/.pnpm/quickjs-emscripten@0.32.0/node_modules/quickjs-emscripten/dist/chunk-OHAYRCBA.js +1 -0
  73. package/dist/src/node_modules/.pnpm/quickjs-emscripten@0.32.0/node_modules/quickjs-emscripten/dist/index.js +1 -0
  74. package/dist/src/protocol/message.d.ts +18 -45
  75. package/dist/src/protocol/message.js +1 -1
  76. package/dist/src/protocol/routes.d.ts +5 -3
  77. package/dist/src/public/channels/ash.d.ts +9 -1
  78. package/dist/src/public/channels/ash.js +1 -1
  79. package/dist/src/public/channels/auth.d.ts +68 -3
  80. package/dist/src/public/channels/auth.js +1 -1
  81. package/dist/src/public/channels/discord/discordChannel.d.ts +2 -3
  82. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  83. package/dist/src/public/channels/slack/api.js +1 -1
  84. package/dist/src/public/channels/slack/connections.d.ts +3 -3
  85. package/dist/src/public/channels/slack/defaults.js +1 -1
  86. package/dist/src/public/channels/slack/slackChannel.d.ts +5 -6
  87. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  88. package/dist/src/public/channels/teams/defaults.js +1 -1
  89. package/dist/src/public/channels/teams/teamsChannel.d.ts +2 -3
  90. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  91. package/dist/src/public/channels/telegram/telegramChannel.d.ts +2 -3
  92. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  93. package/dist/src/public/channels/twilio/twilioChannel.d.ts +2 -3
  94. package/dist/src/public/connections/errors.d.ts +1 -1
  95. package/dist/src/public/connections/index.js +1 -1
  96. package/dist/src/public/definitions/connections/mcp.d.ts +3 -3
  97. package/dist/src/public/definitions/defineChannel.d.ts +2 -3
  98. package/dist/src/public/definitions/defineChannel.js +1 -1
  99. package/dist/src/runtime/connections/callback-route.js +1 -1
  100. package/dist/src/runtime/connections/mcp-client.d.ts +1 -1
  101. package/dist/src/runtime/connections/mcp-client.js +1 -1
  102. package/dist/src/runtime/connections/principal.d.ts +2 -9
  103. package/dist/src/runtime/connections/principal.js +1 -1
  104. package/dist/src/runtime/connections/types.d.ts +6 -6
  105. package/dist/src/runtime/framework-channels/index.d.ts +8 -0
  106. package/dist/src/runtime/framework-channels/index.js +1 -1
  107. package/dist/src/runtime/framework-tools/code-mode-connection-auth.d.ts +0 -5
  108. package/dist/src/runtime/framework-tools/code-mode-connection-auth.js +1 -1
  109. package/dist/src/runtime/framework-tools/connection-search.d.ts +2 -3
  110. package/dist/src/runtime/framework-tools/connection-search.js +1 -1
  111. package/dist/src/runtime/framework-tools/connection-tools.d.ts +4 -3
  112. package/dist/src/runtime/framework-tools/connection-tools.js +1 -1
  113. package/dist/src/runtime/prompt/connections.js +1 -1
  114. package/dist/src/runtime/resolve-agent-graph.js +1 -1
  115. package/dist/src/services/dev-client/request-headers.d.ts +3 -2
  116. package/package.json +4 -3
  117. package/dist/src/execution/authorization-challenge-defaults.d.ts +0 -43
  118. package/dist/src/execution/authorization-challenge-defaults.js +0 -1
  119. package/dist/src/execution/await-authorization-orchestrator.d.ts +0 -28
  120. package/dist/src/execution/await-authorization-orchestrator.js +0 -1
  121. package/dist/src/execution/await-authorization-splice.d.ts +0 -23
  122. package/dist/src/execution/await-authorization-splice.js +0 -1
  123. package/dist/src/execution/code-mode-authorization-lifecycle.js +0 -1
  124. package/dist/src/execution/connection-auth-steps.d.ts +0 -123
  125. package/dist/src/execution/connection-auth-steps.js +0 -1
  126. package/dist/src/internal/nitro/host/code-mode-worker-asset.d.ts +0 -18
  127. package/dist/src/internal/nitro/host/code-mode-worker-asset.js +0 -1
  128. 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
  129. 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
  130. 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
  131. package/dist/src/runtime/connections/principal-context.d.ts +0 -101
  132. package/dist/src/runtime/connections/principal-context.js +0 -1
  133. package/dist/src/runtime/framework-tools/pending-connection-tool-calls.d.ts +0 -78
  134. package/dist/src/runtime/framework-tools/pending-connection-tool-calls.js +0 -1
  135. /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
  136. /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
  137. /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
  138. /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
  139. /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
  140. /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
  141. /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
  142. /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
  143. /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
  144. /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
  145. /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
  146. /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
  147. /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
  148. /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
  149. /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 Main API
18
+ ## The Default
19
19
 
20
- To override the Ash channel's auth, create `agent/channels/ash.ts`:
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
- The framework ships a default channel named `"ash"` that implements the routes above. Creating
33
- `agent/channels/ash.ts` overrides it app-author channels take precedence
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
- For open routes, use `none()` explicitly:
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 this when you want an explicit open route.
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
- - unprotected agents expose both as `null`
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: httpBasic({
135
- username: "ops",
136
- password: process.env.ROUTE_AUTH_BASIC_PASSWORD,
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
- It requires an auth function. Use `experimental-ash/channels/auth` for the common helpers such as
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
@@ -1 +1 @@
1
- import{createSession}from"#channel/session.js";import{createErrorId,createLogger}from"#internal/logging.js";import{isRuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{serializeUrlFilePart}from"#internal/attachments/url-refs.js";import{toErrorMessage}from"#shared/errors.js";const log=createLogger(`channel.send`);function createSendFn(n,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:t}=await n.deliver({auth:s,continuationToken:f,payload:{inputResponses:m,message:g,modelContext:h}});return createSession(t,d,n)}catch(e){isRuntimeNoActiveSessionError(e)||log.warn(`deliver failed, falling back to starting a new session`,{continuationToken:f,errorId:createErrorId(),message:toErrorMessage(e)})}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 n.run({adapter:_,auth:s,capabilities:l===`conversation`?{requestInput:!0}:void 0,callback:c,continuationToken:f,input:{message:g??``,modelContext:h},mode:l})).sessionId,d,n)}}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};
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};
@@ -1,2 +1,2 @@
1
- import{toErrorMessage}from"#shared/errors.js";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{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(e){let i=[{label:`Server`,tone:`info`,value:e.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`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, ConnectionAuthorizationCompletedStreamEvent, ConnectionAuthorizationOutcome, ConnectionAuthorizationPendingStreamEvent, ConnectionAuthorizationRequiredStreamEvent, 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";
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{join}from"node:path";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};
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 silently dropped.
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;return t}async function deserializeContext(r){let i=new ContextContainer,a=r[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[e,a]of Object.entries(r)){if(a===void 0||e===BundleKey.name)continue;let r=resolveKey(e);r!==void 0&&i.set(r,r.codec?await r.codec.deserialize(a,i):a)}return i}export{deserializeContext,serializeContext};
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{toErrorMessage}from"#shared/errors.js";import{join,relative,resolve}from"node:path";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(e){let a=e.source??createDiskProjectSource(),o=resolve(e.agentRoot),c=resolve(e.skillsDirectoryPath??join(o,`skills`)),l=normalizeLogicalPath(e.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 e of m){let n=await discoverOneSkill({entryName:e.name,entryType:getDirectoryEntryType(e),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(e){let n=join(e.skillsDirectoryPath,e.entryName);switch(classifySkillsDirectoryEntry(e.entryName,e.entryType)){case`skill-package-directory`:return discoverPackagedSkill({logicalSkillsPath:e.skillsLogicalPath,skillId:e.entryName,skillRootPath:n,source:e.source});case`flat-skill-markdown`:return discoverFlatMarkdownSkill({logicalSkillsPath:e.skillsLogicalPath,skillFileName:e.entryName,skillFilePath:n,source:e.source});case`flat-skill-module`:return discoverFlatModuleSkill({logicalSkillsPath:e.skillsLogicalPath,skillFileName:e.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(e.skillsLogicalPath,e.entryName)),skill:null,skillId:null}}}async function discoverPackagedSkill(e){let n=join(e.skillRootPath,`SKILL.md`),r=normalizeLogicalPath(join(e.logicalSkillsPath,e.skillId,`SKILL.md`));if(await e.source.stat(n)!==`file`)return{diagnostics:[createDiscoverErrorDiagnostic({code:DISCOVER_SKILL_MARKDOWN_MISSING,message:`Expected "${n}" to exist for the "${e.skillId}" skill.`,sourcePath:e.skillRootPath})],logicalPath:r,skill:null,skillId:null};let a;try{a=lowerSkillMarkdown(await e.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(e.source,e.skillRootPath),s={description:a.description,logicalPath:r,markdown:a.markdown,name:e.skillId,rootPath:e.skillRootPath,skillFilePath:n,skillId:e.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:e.skillId}}async function discoverFlatMarkdownSkill(e){let n=stripMarkdownExtension(e.skillFileName),r=normalizeLogicalPath(join(e.logicalSkillsPath,e.skillFileName)),a;try{a=lowerSkillMarkdown(await e.source.readTextFile(e.skillFilePath),{slug:n})}catch(t){return{diagnostics:[createDiscoverErrorDiagnostic({code:DISCOVER_SKILL_FRONTMATTER_INVALID,message:formatSkillDiscoveryError(e.skillFilePath,t),sourcePath:e.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(e){let n=getSupportedModuleBaseName(e.skillFileName),r=normalizeLogicalPath(join(e.logicalSkillsPath,e.skillFileName));return n===null?{diagnostics:[],logicalPath:r,skill:null,skillId:null}:{diagnostics:[],logicalPath:r,skill:createModuleSourceRef({logicalPath:r}),skillId:n}}async function discoverSkillPackagePaths(e,n){let r=await e.readDirectory(n),i={};for(let e of r)if(e.isDirectory())switch(classifySkillPackageEntry(e.name,getDirectoryEntryType(e))){case`skill-assets-directory`:i.assetsPath=join(n,e.name);break;case`skill-references-directory`:i.referencesPath=join(n,e.name);break;case`skill-scripts-directory`:i.scriptsPath=join(n,e.name);break;default:break}return i}function formatSkillDiscoveryError(t,n){return`Invalid authored skill frontmatter in "${t}": ${toErrorMessage(n)}`}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
+ 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{writeDurableSession}from"#execution/durable-session-store.js";import{createSession}from"#execution/session.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.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
+ 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{toErrorMessage}from"#shared/errors.js";import{AuthKey,CapabilitiesKey,InitiatorAuthKey}from"#context/keys.js";import{createSubagentCalledEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{callAdapterEventHandler}from"#channel/adapter.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{getPendingRuntimeActionBatch,recordPendingSubagentChildToken}from"#harness/runtime-actions.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{deserializeContext}from"#context/serialize.js";import{readDurableSession,writeDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession}from"#execution/session.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";async function dispatchRuntimeActionsStep(e){"use step";let u=await readDurableSession(e.sessionState),d=getPendingRuntimeActionBatch(u.state);if(d===void 0||d.actions.length===0)return{results:[],sessionState:e.sessionState};let f=await deserializeContext(e.serializedContext),p=f.require(BundleKey),m=hydrateDurableSession({compactionOverrides:{thresholdPercent:p.resolvedAgent.config.compaction?.thresholdPercent},durable:u,turnAgent:p.turnAgent}),h=f.require(ChannelKey),g=f.get(AuthKey)??null,_=f.get(CapabilitiesKey),v=f.get(InitiatorAuthKey)??null,y=e.parentWritable.getWriter(),b=buildAdapterContext(h,f),x=m,S=[];try{for(let t of d.actions){let n,r,s,c;switch(t.kind){case`subagent-call`:{let e=createWorkflowRuntime({compiledArtifactsSource:p.compiledArtifactsSource,nodeId:t.nodeId}),{childContinuationToken:i,runInput:a}=buildSubagentRunInput({action:t,auth:g,batchEvent:d.event,capabilities:_,initiatorAuth:v,session:m}),o=await e.run(a);x=recordPendingSubagentChildToken({callId:t.callId,childContinuationToken:i,session:x}),n=o.sessionId,r=t.name,c=t.subagentName;break}case`remote-agent-call`:{let i;try{i=resolveRemoteAgentForAction({nodeId:t.nodeId,remoteAgentName:t.remoteAgentName,registry:p.subagentRegistry.subagentsByNodeId}),n=await startRemoteAgentSession({action:t,callbackBaseUrl:e.callbackBaseUrl,remote:i,session:m})}catch(e){S.push(createRemoteAgentStartFailureResult({action:t,error:e}));continue}r=t.name,s={url:i.url},c=t.remoteAgentName;break}default:throw Error(`Unsupported runtime action kind "${t.kind}" in workflow runtime.`)}let l=await callAdapterEventHandler(h,createSubagentCalledEvent({callId:t.callId,childSessionId:n,name:r,remote:s,sequence:d.event.sequence,sessionId:m.sessionId,toolName:c,turnId:d.event.turnId,workflowId:workflowEntryReference.workflowId}),b);await y.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(l)))}}finally{y.releaseLock()}return{results:S,sessionState:x===m?e.sessionState:await writeDurableSession({session:x,writable:e.sessionWritable})}}function createRemoteAgentStartFailureResult(t){return{callId:t.action.callId,isError:!0,kind:`subagent-result`,output:{code:`REMOTE_AGENT_START_FAILED`,message:toErrorMessage(t.error)},subagentName:t.action.remoteAgentName}}export{dispatchRuntimeActionsStep};
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
- * orchestrators eg. await-authorization — can stamp protocol events
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),r=createNodeHarnessTools({node:e.node}),i=collectResolvedTools(e.node),o=createToolCompactionHandler(i),s=collectRetentionPolicies(i);return createToolLoopHarness({capabilities:e.capabilities,emit:e.emit,mode:e.mode,onCompaction:o,resolveModel:t,retentionPolicies:s,runtimeIdentity:buildRuntimeIdentity(e.node),tools:r})}function buildRuntimeIdentity(t){let n=resolveInstalledPackageInfo(),r={agentId:t.turnAgent.id,agentName:t.agent.config?.name,ashVersion:n.version,modelId:t.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 n=findRegisteredRuntimeTool(e.node.toolRegistry,e.tool.name);if(n===null)return null;let r=n.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
+ 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{toErrorMessage}from"#shared/errors.js";import{SessionCallbackKey}from"#context/keys.js";import{parseSessionCallback}from"#channel/session-callback.js";const log=createLogger(`execution.session-callback`);async function fireSessionCallbackStep(e){"use step";let r=e.serializedContext[`ash.sessionId`]??``,a=e.serializedContext[SessionCallbackKey.name];if(a!==void 0)try{let n=parseSerializedSessionCallback(a),i=e.status===`completed`?{callId:n.callId,kind:`session.completed`,output:e.output??``,sessionId:r,subagentName:n.subagentName}:{callId:n.callId,error:{code:`SESSION_FAILED`,message:toErrorMessage(e.error)},kind:`session.failed`,sessionId:r,subagentName:n.subagentName},o=await fetch(n.url,{body:JSON.stringify(i),headers:{"content-type":`application/json`},method:`POST`,signal:AbortSignal.timeout(3e4)});if(!o.ok)throw Error(`Session callback failed with HTTP ${o.status}.`)}catch(e){throw log.error(`failed to post session callback`,{error:e,sessionId:r}),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
+ 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{awaitAuthorizationAndResolve}from"#execution/await-authorization-orchestrator.js";import{normalizeSerializableError}from"#execution/workflow-errors.js";import{turnStep}from"#execution/workflow-steps.js";async function turnWorkflow(r){"use workflow";let i=r.sessionState,a=r.serializedContext,o=r.delivery,s=r.parentWritable,c=r.sessionWritable;try{for(;;){let t=await turnStep({input:o,parentWritable:s,serializedContext:a,sessionState:i,sessionWritable:c});if(i=t.sessionState,a=t.serializedContext,t.action===`done`){await notifyDriverStep({completionToken:r.completionToken,payload:{action:{kind:`done`,output:t.output??``,serializedContext:a,sessionState:i},kind:`turn-result`}});return}if(t.action===`park`){let e=t.pendingRuntimeActionKeys;if(!(e!==void 0||t.hasPendingInputBatch&&r.capabilities?.requestInput===!0||r.mode===`conversation`))throw Error("Task mode cannot wait for follow-up input (`next: null`).");let n=e===void 0?{kind:`park`,serializedContext:a,sessionState:i}:{kind:`dispatch-runtime-actions`,pendingActionKeys:e,serializedContext:a,sessionState:i};await notifyDriverStep({completionToken:r.completionToken,payload:{action:n,kind:`turn-result`}});return}if(t.action===`await-authorization`){let n=await awaitAuthorizationAndResolve({parentWritable:s,pendingCodeModeConnectionAuthorization:t.pendingCodeModeConnectionAuthorization,pendingToolCalls:t.pendingToolCalls,serializedContext:a,sessionState:i,sessionWritable:c});i=n.sessionState,a=n.serializedContext,o=void 0;continue}o=void 0}}catch(e){throw await notifyDriverStep({completionToken:r.completionToken,payload:{error:normalizeSerializableError(e),kind:`turn-error`}}),e}}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
+ 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{createHook,getWorkflowMetadata,getWritable}from"#compiled/@workflow/core/index.js";import{resolveVercelProductionCallbackBaseUrl}from"#execution/workflow-callback-url.js";import{accumulateRuntimeActionResults}from"#harness/runtime-actions.js";import{ASH_SESSION_STREAM_NAMESPACE}from"#execution/durable-session-store.js";import{normalizeSerializableError,rebuildSerializableError}from"#execution/workflow-errors.js";import{dispatchTurnStep,emitTerminalSessionFailureStep,routeProxiedDeliverStep,runProxyInputRequestStep}from"#execution/workflow-steps.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(e){"use workflow";let{workflowRunId:r}=getWorkflowMetadata(),i=e.serializedContext[`ash.continuationToken`]||``,s=e.serializedContext[`ash.mode`],c=e.serializedContext[`ash.capabilities`],u=e.serializedContext[`ash.bundle`];e.serializedContext[`ash.sessionId`]=r;let d=getWritable(),f=getWritable({namespace:ASH_SESSION_STREAM_NAMESPACE});try{let t=await createSessionStep({compiledArtifactsSource:u.source,continuationToken:i,nodeId:u.nodeId,sessionId:r,sessionWritable:f});return await runDriverLoop({capabilities:c,driverWritable:d,initialInput:{kind:`deliver`,payloads:[{message:e.input.message,modelContext:e.input.modelContext}]},mode:s,serializedContext:e.serializedContext,sessionState:t,sessionWritable:f})}catch(t){throw await emitTerminalSessionFailureStep({error:normalizeSerializableError(t),parentWritable:d,serializedContext:e.serializedContext}),await fireSessionCallbackStep({error:normalizeSerializableError(t),serializedContext:e.serializedContext,status:`failed`}),await notifyDelegatedParentStep({result:createDelegatedSubagentErrorResult(e.serializedContext,t),serializedContext:e.serializedContext}),t}}async function runDriverLoop(n){let i=0,a=await dispatchAndAwaitTurn({capabilities:n.capabilities,delivery:n.initialInput,mode:n.mode,parentWritable:n.driverWritable,serializedContext:n.serializedContext,sessionState:n.sessionState,sessionWritable:n.sessionWritable,turnGeneration:++i});if(a.kind===`done`)return await finalizeDone({action:a,driverWritable:n.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](),l=null,u=[],getNextPromise=()=>(l??=c.next(),l),consumeNext=()=>{l=null},rekeyHook=async t=>{t===o||!t||(await closeHookIterator(c),await disposeHook(s),o=t,s=createHook({token:o}),c=s[Symbol.asyncIterator](),l=null)};try{for(;;)switch(a.kind){case`done`:return await finalizeDone({action:a,driverWritable:n.driverWritable});case`dispatch-runtime-actions`:{let e=await dispatchRuntimeActionsStep({callbackBaseUrl:resolveVercelProductionCallbackBaseUrl()??getWorkflowMetadata().url,parentWritable:n.driverWritable,serializedContext:a.serializedContext,sessionState:a.sessionState,sessionWritable:n.sessionWritable}),o=await waitForPendingRuntimeActionResults({bufferedDeliveries:u,consumeNext,getNextPromise,initialResults:e.results,parentWritable:n.driverWritable,pendingActionKeys:a.pendingActionKeys,rekeyHook,serializedContext:a.serializedContext,sessionState:e.sessionState,sessionWritable:n.sessionWritable});if(o===null)return{output:``};a=await dispatchAndAwaitTurn({capabilities:n.capabilities,delivery:{kind:`runtime-action-result`,results:o.results},mode:n.mode,parentWritable:n.driverWritable,serializedContext:o.serializedContext,sessionState:o.sessionState,sessionWritable:n.sessionWritable,turnGeneration:++i}),await rekeyHook(a.sessionState.continuationToken);break}case`park`:{let e=await waitForNextDeliver({bufferedDeliveries:u,consumeNext,getNextPromise});if(e===null)return{output:``};let t=await routeDeliverForChildren({auth:e.auth,parentWritable:n.driverWritable,payloads:e.payloads,sessionState:a.sessionState});if(t===void 0)continue;a=await dispatchAndAwaitTurn({capabilities:n.capabilities,delivery:{auth:e.auth,kind:`deliver`,payloads:[t]},mode:n.mode,parentWritable:n.driverWritable,serializedContext:a.serializedContext,sessionState:a.sessionState,sessionWritable:n.sessionWritable,turnGeneration:++i}),await rekeyHook(a.sessionState.continuationToken);break}}}finally{await closeHookIterator(c),await disposeHook(s)}}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(t){let n=`ash://turn/${t.sessionState.sessionId}/${t.turnGeneration}`,r=createHook({token:n});try{await dispatchTurnStep({capabilities:t.capabilities,completionToken:n,delivery:t.delivery,mode:t.mode,parentWritable:t.parentWritable,serializedContext:t.serializedContext,sessionState:t.sessionState,sessionWritable:t.sessionWritable});let e=await awaitHookPayload(r);if(e.kind===`turn-error`)throw rebuildSerializableError(e.error);return e.action}finally{await disposeHook(r)}}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 t=e.sessionState,n=e.serializedContext,r=await accumulateRuntimeActionResults({bufferedDeliveries:e.bufferedDeliveries,async getNext(){for(;;){let r=await e.getNextPromise();if(e.consumeNext(),r.done)return null;let i=r.value;if(i.kind===`deliver`){let n=await routeDeliverForChildren({auth:i.auth,parentWritable:e.parentWritable,payloads:i.payloads,sessionState:t});if(n===void 0)continue;return{kind:`deliver`,value:{...i,payloads:[n]}}}if(i.kind===`runtime-action-result`)return{kind:`runtime-action-result`,results:i.results};let a=await runProxyInputRequestStep({hookPayload:i,parentWritable:e.parentWritable,serializedContext:n,sessionState:t,sessionWritable:e.sessionWritable});t=a.sessionState,n=a.serializedContext,await e.rekeyHook(t.continuationToken)}},initialResults:e.initialResults,pendingActionKeys:e.pendingActionKeys});return r===null?null:{results:r,serializedContext:n,sessionState:t}}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
+ 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{serializeContext}from"#context/serialize.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.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,workflowEntryReference={workflowId:`workflow//${STABLE_ID_BASE}//${WORKFLOW_ENTRY_NAME}`},turnWorkflowReference={workflowId:`workflow//${STABLE_ID_BASE}//${TURN_WORKFLOW_NAME}`};function createWorkflowRuntime(t){return{async run(e){let i=serializeContext(buildRunContext({bundle:await getCompiledRuntimeAgentBundle({compiledArtifactsSource:t.compiledArtifactsSource,nodeId:t.nodeId}),run:e})),o=await startWorkflowPreferLatest(workflowEntryReference,[{input:e.input,serializedContext:i}]);return{continuationToken:e.continuationToken??o.runId,events:parseNdjsonStream(getRun(o.runId).getReadable()),sessionId:o.runId}},async deliver(t){let n={auth:t.auth,kind:`deliver`,payloads:[t.payload]};try{let e=normalizeWorkflowHook(await getHookByToken(t.continuationToken));return await resumeHook(t.continuationToken,n),{sessionId:e.runId}}catch(n){throw HookNotFoundError.is(n)?new RuntimeNoActiveSessionError(t.continuationToken):n}},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(`
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 `pendingRuntimeActionKeys`
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 {