experimental-ash 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/docs/public/README.md +8 -8
  3. package/dist/docs/public/advanced/{auth-and-route-protection.md → auth-and-route-protection.mdx} +11 -0
  4. package/dist/docs/public/advanced/context-control.md +4 -4
  5. package/dist/docs/public/advanced/{evals.md → evals.mdx} +11 -1
  6. package/dist/docs/public/advanced/{hooks.md → hooks.mdx} +28 -40
  7. package/dist/docs/public/advanced/instrumentation.md +92 -3
  8. package/dist/docs/public/advanced/project-layout.md +5 -5
  9. package/dist/docs/public/advanced/runs-and-streaming.md +8 -2
  10. package/dist/docs/public/advanced/session-context.md +1 -1
  11. package/dist/docs/public/advanced/typescript-api.md +49 -6
  12. package/dist/docs/public/advanced/vercel-deployment.md +1 -1
  13. package/dist/docs/public/agent-ts.md +5 -5
  14. package/dist/docs/public/channels/{discord.md → discord.mdx} +11 -0
  15. package/dist/docs/public/channels/index.md +10 -10
  16. package/dist/docs/public/channels/{slack.md → slack.mdx} +11 -0
  17. package/dist/docs/public/channels/{teams.md → teams.mdx} +12 -0
  18. package/dist/docs/public/channels/{telegram.md → telegram.mdx} +11 -0
  19. package/dist/docs/public/channels/{twilio.md → twilio.mdx} +11 -0
  20. package/dist/docs/public/{connections.md → connections.mdx} +18 -6
  21. package/dist/docs/public/frontend/README.md +16 -0
  22. package/dist/docs/public/frontend/meta.json +3 -0
  23. package/dist/docs/public/frontend/nextjs.md +192 -0
  24. package/dist/docs/public/frontend/use-ash-agent.md +332 -0
  25. package/dist/docs/public/{getting-started.md → getting-started.mdx} +12 -1
  26. package/dist/docs/public/{human-in-the-loop.md → human-in-the-loop.mdx} +12 -1
  27. package/dist/docs/public/meta.json +1 -0
  28. package/dist/docs/public/sandbox.md +1 -1
  29. package/dist/docs/public/{schedules.md → schedules.mdx} +9 -0
  30. package/dist/docs/public/skills.md +2 -2
  31. package/dist/docs/public/{subagents.md → subagents.mdx} +10 -0
  32. package/dist/docs/public/{tools.md → tools.mdx} +41 -26
  33. package/dist/src/channel/adapter.d.ts +13 -0
  34. package/dist/src/channel/instrumentation.d.ts +10 -0
  35. package/dist/src/channel/instrumentation.js +1 -0
  36. package/dist/src/channel/send.js +1 -1
  37. package/dist/src/channel/types.d.ts +16 -0
  38. package/dist/src/cli/commands/channels.d.ts +2 -1
  39. package/dist/src/cli/commands/channels.js +1 -1
  40. package/dist/src/compiler/manifest.d.ts +13 -1
  41. package/dist/src/compiler/manifest.js +1 -1
  42. package/dist/src/compiler/module-map.js +1 -1
  43. package/dist/src/compiler/normalize-manifest.js +1 -1
  44. package/dist/src/compiler/normalize-skill.d.ts +15 -2
  45. package/dist/src/compiler/normalize-skill.js +1 -1
  46. package/dist/src/compiler/normalize-tool.js +1 -1
  47. package/dist/src/context/dynamic-skill-lifecycle.d.ts +23 -0
  48. package/dist/src/context/dynamic-skill-lifecycle.js +1 -0
  49. package/dist/src/context/dynamic-tool-lifecycle.d.ts +2 -0
  50. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  51. package/dist/src/context/hook-lifecycle.d.ts +4 -6
  52. package/dist/src/context/hook-lifecycle.js +1 -1
  53. package/dist/src/context/keys.d.ts +6 -4
  54. package/dist/src/context/keys.js +1 -1
  55. package/dist/src/context/providers/connection.d.ts +9 -0
  56. package/dist/src/context/providers/connection.js +1 -1
  57. package/dist/src/context/providers/sandbox.js +1 -1
  58. package/dist/src/execution/ash-workflow-attributes.d.ts +118 -0
  59. package/dist/src/execution/ash-workflow-attributes.js +1 -0
  60. package/dist/src/execution/channel-context.d.ts +5 -0
  61. package/dist/src/execution/channel-context.js +1 -0
  62. package/dist/src/execution/create-session-step.d.ts +28 -1
  63. package/dist/src/execution/create-session-step.js +1 -1
  64. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  65. package/dist/src/execution/durable-session-store.d.ts +7 -0
  66. package/dist/src/execution/runtime-context.js +1 -1
  67. package/dist/src/execution/sandbox/prewarm.js +1 -1
  68. package/dist/src/execution/session.d.ts +6 -0
  69. package/dist/src/execution/session.js +2 -2
  70. package/dist/src/execution/skills/instructions.d.ts +3 -2
  71. package/dist/src/execution/subagent-tool.js +1 -1
  72. package/dist/src/execution/workflow-entry.js +1 -1
  73. package/dist/src/execution/workflow-steps.js +1 -1
  74. package/dist/src/harness/attachment-staging.js +1 -1
  75. package/dist/src/harness/code-mode.d.ts +0 -5
  76. package/dist/src/harness/code-mode.js +1 -1
  77. package/dist/src/harness/emission.d.ts +1 -1
  78. package/dist/src/harness/emission.js +1 -1
  79. package/dist/src/harness/instrumentation-config.d.ts +1 -1
  80. package/dist/src/harness/instrumentation-metadata.d.ts +23 -0
  81. package/dist/src/harness/instrumentation-metadata.js +1 -0
  82. package/dist/src/harness/otel-integration.d.ts +2 -2
  83. package/dist/src/harness/otel-integration.js +1 -1
  84. package/dist/src/harness/step-hooks.js +1 -1
  85. package/dist/src/harness/tool-loop.js +1 -1
  86. package/dist/src/harness/turn-tag-state.d.ts +50 -0
  87. package/dist/src/harness/turn-tag-state.js +1 -0
  88. package/dist/src/harness/types.d.ts +11 -2
  89. package/dist/src/internal/application/package.js +1 -1
  90. package/dist/src/internal/authored-definition/schema-backed.d.ts +0 -1
  91. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  92. package/dist/src/internal/instrumentation.d.ts +39 -0
  93. package/dist/src/internal/instrumentation.js +1 -0
  94. package/dist/src/internal/workflow/builtins.d.ts +32 -0
  95. package/dist/src/internal/workflow/builtins.js +1 -1
  96. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +1 -1
  97. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
  98. package/dist/src/internal/workflow-bundle/workflow-core-shim.d.ts +34 -0
  99. package/dist/src/internal/workflow-bundle/workflow-core-shim.js +1 -1
  100. package/dist/src/internal/workflow-bundle/workflow-transformer.js +1 -1
  101. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  102. package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +2 -2
  103. package/dist/src/public/channels/slack/attachments.js +1 -1
  104. package/dist/src/public/channels/slack/index.d.ts +1 -1
  105. package/dist/src/public/channels/slack/slackChannel.d.ts +6 -0
  106. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  107. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  108. package/dist/src/public/channels/twilio/index.d.ts +1 -1
  109. package/dist/src/public/channels/twilio/twilioChannel.d.ts +6 -0
  110. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  111. package/dist/src/public/definitions/defineChannel.d.ts +12 -2
  112. package/dist/src/public/definitions/defineChannel.js +1 -1
  113. package/dist/src/public/definitions/hook.d.ts +3 -11
  114. package/dist/src/public/definitions/instrumentation.d.ts +88 -2
  115. package/dist/src/public/definitions/skill.d.ts +5 -0
  116. package/dist/src/public/definitions/tool.d.ts +25 -66
  117. package/dist/src/public/definitions/tool.js +1 -1
  118. package/dist/src/public/instrumentation/index.d.ts +1 -4
  119. package/dist/src/public/instrumentation/index.js +1 -1
  120. package/dist/src/public/skills/index.d.ts +2 -0
  121. package/dist/src/public/skills/index.js +1 -1
  122. package/dist/src/public/tools/index.d.ts +2 -2
  123. package/dist/src/public/tools/index.js +1 -1
  124. package/dist/src/runtime/agent/mock-model-adapter.js +4 -7
  125. package/dist/src/runtime/agent/mock-model-skill-selection.d.ts +9 -0
  126. package/dist/src/runtime/agent/mock-model-skill-selection.js +4 -0
  127. package/dist/src/runtime/attributes/emit.d.ts +73 -0
  128. package/dist/src/runtime/attributes/emit.js +1 -0
  129. package/dist/src/runtime/channels/registry.js +1 -1
  130. package/dist/src/runtime/connections/mcp-client.js +1 -1
  131. package/dist/src/runtime/framework-tools/code-mode-connection-auth.d.ts +2 -0
  132. package/dist/src/runtime/framework-tools/connection-search-dynamic.d.ts +34 -0
  133. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -0
  134. package/dist/src/runtime/framework-tools/index.d.ts +7 -5
  135. package/dist/src/runtime/framework-tools/index.js +1 -1
  136. package/dist/src/runtime/prompt/connections.js +1 -1
  137. package/dist/src/runtime/resolve-agent-graph.js +1 -1
  138. package/dist/src/runtime/resolve-agent.js +1 -1
  139. package/dist/src/runtime/resolve-dynamic-skill.d.ts +8 -0
  140. package/dist/src/runtime/resolve-dynamic-skill.js +1 -0
  141. package/dist/src/runtime/resolve-dynamic-tool.js +1 -1
  142. package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
  143. package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
  144. package/dist/src/runtime/types.d.ts +13 -4
  145. package/dist/src/shared/dynamic-tool-definition.d.ts +51 -76
  146. package/dist/src/shared/dynamic-tool-definition.js +1 -1
  147. package/dist/src/shared/guards.d.ts +14 -0
  148. package/dist/src/shared/guards.js +1 -1
  149. package/dist/src/shared/skill-definition.d.ts +5 -4
  150. package/dist/src/shared/tool-definition.d.ts +12 -0
  151. package/package.json +2 -1
  152. package/dist/src/runtime/framework-tools/connection-search.d.ts +0 -57
  153. package/dist/src/runtime/framework-tools/connection-search.js +0 -1
  154. package/dist/src/runtime/framework-tools/connection-tools.d.ts +0 -55
  155. package/dist/src/runtime/framework-tools/connection-tools.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # experimental-ash
2
2
 
3
+ ## 0.43.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 52146a5: Migrate `connection_search` from a framework tool singleton to a dynamic tool resolver. Discovered connection tools are now derived from conversation history via `ctx.messages` — no `DiscoveredConnectionToolsKey` durable state or `onCompact` hook needed. Deletes `connection-tools.ts` and the old `connection-search.ts` implementation.
8
+ - 2469218: Unified dynamic tool API: `defineTools` and `defineTool({ events })` are replaced by `defineDynamic`, and the `tool()` entry wrapper is replaced by `defineTool`. Handlers return either a single `defineTool(...)` (named after the file slug) or a `Record<string, defineTool(...)>` (named `slug__key`). The `identity` field is removed from compiled manifests — single vs map detection is now runtime-based.
9
+ - 192a1eb: Add dynamic instrumentation metadata via `metadata["step.started"]`, with per-attempt session, turn, step, channel, and model-input access. The channel projection exposes `kind` (`channel:<filename>` for authored channels, or `http`/`schedule`/`subagent`), also emitted as the `ash.channel.kind` span attribute. Authored metadata is parsed defensively: resolver failures, thenables, reserved `ash.*` keys, and non-string values are dropped without interrupting the model call.
10
+ - d7de8e0: Clean up `DynamicToolResolveContext`: remove `__cache`, add `messages` for history access, add `toModelOutput` to `DynamicToolEntry`, and run the dynamic tool bundler transform on framework tools during the ash build.
11
+ - dc0db8e: Dynamic skills can now be authored with `defineDynamic` + `defineSkill` in `agent/skills/`, matching the `defineDynamic` + `defineTool` pattern for dynamic tools. Resolvers subscribe to lifecycle events (`session.started`, `turn.started`, `step.started`) and return branded `defineSkill(...)` entries that are materialized to the sandbox. Single returns use the file slug as the skill name; map returns use `slug__key`. Hook-contributed skills via `LifecycleHookResult.skills` continue to work but are deprecated in favor of this approach.
12
+ - 80b4c1d: Remove `LifecycleHookResult.skills` — lifecycle hooks can no longer contribute skills. Use `defineDynamic` + `defineSkill` in `agent/skills/` instead.
13
+
14
+ ### Patch Changes
15
+
16
+ - 3df1814: Emit `$ash.*` workflow run attributes for agent run observability. Every
17
+ session, turn, and subagent run now tags itself with a small set of
18
+ reserved-namespace attributes (`$ash.type`, `$ash.parent`,
19
+ `$ash.root`, `$ash.subagent`, `$ash.trigger`, `$ash.title`,
20
+ `$ash.model`, `$ash.input_tokens`, `$ash.output_tokens`,
21
+ `$ash.cache_read_tokens`, `$ash.tool_count`) so the Vercel dashboard can
22
+ hydrate the Agent Runs view via the workflow runtime's
23
+ `workflow_run_attributes` table without scraping events.
24
+
25
+ Tag writes are best-effort — runtime failures are logged once per process
26
+ and swallowed so a broken attribute write can never break a session. No
27
+ public API changes; authored agent code never sees the `$ash.*` namespace.
28
+ See `research/active/workflow-run-attributes.md` for the full design.
29
+
30
+ - 70a2d17: Authenticate Slack Enterprise Grid file downloads so agents can read private attachments served from workspace `enterprise.slack.com` hosts.
31
+ - 08e2696: Deploy scaffolded Web Chat routes to Vercel when channel setup adds a deployable route.
32
+
3
33
  ## 0.42.0
4
34
 
5
35
  ### Minor Changes
@@ -15,24 +15,24 @@ Important naming note:
15
15
 
16
16
  Read in this order:
17
17
 
18
- 1. [Getting Started](./getting-started.md)
18
+ 1. [Getting Started](./getting-started.mdx)
19
19
  2. [Project Layout](./advanced/project-layout.md)
20
20
  3. [`agent.ts`](./agent-ts.md)
21
21
  4. [TypeScript API](./advanced/typescript-api.md)
22
22
  5. [Context Control](./advanced/context-control.md)
23
23
  6. [Skills](./skills.md)
24
- 7. [Tools](./tools.md)
25
- 8. [Connections](./connections.md)
24
+ 7. [Tools](./tools.mdx)
25
+ 8. [Connections](./connections.mdx)
26
26
  9. [Workspace](./advanced/workspace.md)
27
27
  10. [Sandboxes](./sandbox.md)
28
28
  11. [Channels](./channels/README.md)
29
- 12. [Human In The Loop](./human-in-the-loop.md)
29
+ 12. [Human In The Loop](./human-in-the-loop.mdx)
30
30
  13. [Session Context](./advanced/session-context.md)
31
31
  14. [Sessions And Streaming](./advanced/runs-and-streaming.md)
32
- 15. [Subagents](./subagents.md)
33
- 16. [Schedules](./schedules.md)
34
- 17. [Evals](./advanced/evals.md)
35
- 18. [Auth And Route Protection](./advanced/auth-and-route-protection.md)
32
+ 15. [Subagents](./subagents.mdx)
33
+ 16. [Schedules](./schedules.mdx)
34
+ 17. [Evals](./advanced/evals.mdx)
35
+ 18. [Auth And Route Protection](./advanced/auth-and-route-protection.mdx)
36
36
  19. [Vercel Deployment](./advanced/vercel-deployment.md)
37
37
  20. [CLI, Build, And Debugging](./advanced/cli-build-and-debugging.md)
38
38
 
@@ -16,6 +16,17 @@ These settings apply to:
16
16
  - `POST /ash/v1/session/:sessionId`
17
17
  - `GET /ash/v1/session/:sessionId/stream`
18
18
 
19
+ <CopyPrompt text="Protect the user's Ash HTTP routes through the channel layer. In Ash, route auth and IP policy live on channel factories, usually agent/channels/ash.ts with ashChannel auth config, not in agent.ts. Inspect the existing channel file and app auth code, replace any generated exampleProductionAuth placeholder with the smallest AuthFn or helper composition, use helpers such as localDev, vercelOidc, none, httpBasic, jwtHmac, jwtEcdsa, or oidc as appropriate, keep secrets in environment variables, preserve localDev and vercelOidc behavior where useful, verify unauthenticated production browser requests are rejected with 401, and do not commit unless the user asks.">
20
+ Protect the user's Ash HTTP routes through the channel layer. In Ash, route auth and IP policy
21
+ live on channel factories, usually agent/channels/ash.ts with ashChannel auth config, not in
22
+ agent.ts. Inspect the existing channel file and app auth code, replace any generated
23
+ exampleProductionAuth placeholder with the smallest AuthFn or helper composition, use helpers such
24
+ as localDev, vercelOidc, none, httpBasic, jwtHmac, jwtEcdsa, or oidc as appropriate, keep secrets
25
+ in environment variables, preserve localDev and vercelOidc behavior where useful, verify
26
+ unauthenticated production browser requests are rejected with 401, and do not commit unless the
27
+ user asks.
28
+ </CopyPrompt>
29
+
19
30
  ## Generated Web Chat Auth
20
31
 
21
32
  `pnpm create experimental-ash-agent` scaffolds `agent/channels/ash.ts` from the Web Chat example.
@@ -116,7 +116,7 @@ Subagents are a context-control tool too:
116
116
  - they can have their own tools and their own sandbox
117
117
  - they run inside their own delegated subagent context instead of extending the root agent inline
118
118
 
119
- See [Subagents](./subagents.md).
119
+ See [Subagents](./subagents.mdx).
120
120
 
121
121
  ## Choosing The Right Lever
122
122
 
@@ -140,8 +140,8 @@ For most agents:
140
140
 
141
141
  ## What To Read Next
142
142
 
143
- - [Tools](./tools.md)
144
- - [Hooks](./hooks.md)
143
+ - [Tools](./tools.mdx)
144
+ - [Hooks](./hooks.mdx)
145
145
  - [Skills](./skills.md)
146
146
  - [Workspace](./workspace.md)
147
- - [Subagents](./subagents.md)
147
+ - [Subagents](./subagents.mdx)
@@ -13,6 +13,16 @@ Use evals when you want to:
13
13
  - compare output against expected text, JSON, SQL, or session behavior
14
14
  - export results to Braintrust for review and comparison
15
15
 
16
+ <CopyPrompt text="Add repeatable Ash evals to the user's project. Ash eval suites live under the app root evals directory, use .eval.ts files, and export defineEvalSuite from experimental-ash/evals; suite identity is derived from the file path, not an authored id or name. Inspect existing evals and agent behavior, create a focused smoke suite with model, cases or load, and scores, prefer cheap scorers such as Run.didNotFail, Run.usedTool, Text.includes, Json.deepEqual, or Sql.exactNormalized before LLM judges, add fixture loading only if it helps, run ash eval with the relevant suite flags, and do not commit unless the user asks.">
17
+ Add repeatable Ash evals to the user's project. Ash eval suites live under the app root evals
18
+ directory, use .eval.ts files, and export defineEvalSuite from experimental-ash/evals; suite
19
+ identity is derived from the file path, not an authored id or name. Inspect existing evals and
20
+ agent behavior, create a focused smoke suite with model, cases or load, and scores, prefer cheap
21
+ scorers such as Run.didNotFail, Run.usedTool, Text.includes, Json.deepEqual, or
22
+ Sql.exactNormalized before LLM judges, add fixture loading only if it helps, run ash eval with the
23
+ relevant suite flags, and do not commit unless the user asks.
24
+ </CopyPrompt>
25
+
16
26
  ## Where Evals Live
17
27
 
18
28
  Ash discovers eval suites under the app root `evals/` directory.
@@ -276,5 +286,5 @@ For most apps:
276
286
  ## What To Read Next
277
287
 
278
288
  - [TypeScript API](./typescript-api.md)
279
- - [Tools](./tools.md)
289
+ - [Tools](./tools.mdx)
280
290
  - [Sessions And Streaming](./runs-and-streaming.md)
@@ -7,10 +7,24 @@ url: /hooks
7
7
  Hooks are Ash's authored extension points for the per-turn lifecycle and
8
8
  the runtime event stream.
9
9
 
10
+ This page is about `agent/hooks/*.ts` runtime hooks. For the React client hook,
11
+ see [`useAshAgent`](../frontend/use-ash-agent.md).
12
+
10
13
  Use a hook when you want to run code **around** a turn (before the model
11
14
  runs, around stream events) without writing a tool, a context provider,
12
15
  or a channel adapter handler.
13
16
 
17
+ <CopyPrompt text="Add an Ash hook to the user's project. Ash hooks live under agent/hooks, use defineHook from experimental-ash/hooks, and can subscribe to lifecycle.session, lifecycle.turn, or stream events. Use lifecycle hooks when the model needs ephemeral modelContext before the next model call; use events handlers for observe-only side effects after events are durably recorded. Inspect existing hooks and agent/lib, choose the smallest hook file and path-derived slug, use defineState only when durable shared state is needed, wrap best-effort side effects so they do not fail sessions accidentally, verify the relevant lifecycle or stream event behavior, and do not commit unless the user asks.">
18
+ Add an Ash hook to the user's project. Ash hooks live under agent/hooks, use defineHook from
19
+ experimental-ash/hooks, and can subscribe to lifecycle.session, lifecycle.turn, or stream events.
20
+ Use lifecycle hooks when the model needs ephemeral modelContext before the next model call; use
21
+ events handlers for observe-only side effects after events are durably recorded. Inspect existing
22
+ hooks and agent/lib, choose the smallest hook file and path-derived slug, use defineState only
23
+ when durable shared state is needed, wrap best-effort side effects so they do not fail sessions
24
+ accidentally, verify the relevant lifecycle or stream event behavior, and do not commit unless the
25
+ user asks.
26
+ </CopyPrompt>
27
+
14
28
  ## The Main API
15
29
 
16
30
  `agent/hooks/audit.ts`
@@ -103,7 +117,7 @@ Lifecycle hooks share one signature:
103
117
  ```ts
104
118
  type LifecycleHook = (
105
119
  ctx: HookContext,
106
- ) => void | { modelContext?: readonly ModelMessage[]; skills?: readonly NamedSkillDefinition[] } | Promise<…>;
120
+ ) => void | { modelContext?: readonly ModelMessage[] } | Promise<…>;
107
121
  ```
108
122
 
109
123
  Session and turn metadata are available on `ctx.session`. For example,
@@ -113,14 +127,16 @@ Session and turn metadata are available on `ctx.session`. For example,
113
127
  model turn. `lifecycle.turn` runs **once per fresh delivery** — tool-loop
114
128
  continuations and HITL resumes do not re-fire it.
115
129
 
116
- Both keys may return `{ modelContext, skills }`. `modelContext`
117
- contributions are concatenated session-then-turn before the harness's
118
- next model call, and are never written to durable history. Dynamic
119
- `skills` are written into `/workspace/skills/<name>/` and announced
120
- through durable system history so the next model call can activate them
121
- with `load_skill`. Channels may also contribute the same `modelContext`
122
- shape through `SendPayload`; channel-provided messages appear before
123
- lifecycle hook messages.
130
+ Both keys may return `{ modelContext }`. `modelContext` contributions
131
+ are concatenated session-then-turn before the harness's next model
132
+ call, and are never written to durable history. Channels may also
133
+ contribute the same `modelContext` shape through `SendPayload`;
134
+ channel-provided messages appear before lifecycle hook messages.
135
+
136
+ Browser UIs can contribute one-turn page state through `useAshAgent()`
137
+ `prepareSend` and `clientContext`. That client context is merged before
138
+ runtime lifecycle hook context, so use it for page state and use runtime hooks
139
+ for server-side policy and durable context.
124
140
 
125
141
  ### Seed durable context
126
142
 
@@ -174,36 +190,6 @@ export default defineHook({
174
190
  `modelContext` messages are appended in registry order and visible to
175
191
  the next model call only — never persisted.
176
192
 
177
- ### Contribute dynamic skills
178
-
179
- ```ts
180
- // agent/hooks/tenant-skills.ts
181
- import { defineHook } from "experimental-ash/hooks";
182
-
183
- export default defineHook({
184
- lifecycle: {
185
- async session(ctx) {
186
- return {
187
- skills: [
188
- {
189
- name: "tenant-policy",
190
- description: "Apply tenant-specific policy before answering.",
191
- markdown: "Read the policy reference before making a recommendation.",
192
- files: {
193
- "references/policy.md": "Policy body\n",
194
- },
195
- },
196
- ],
197
- };
198
- },
199
- },
200
- });
201
- ```
202
-
203
- Dynamic skills may be returned from `lifecycle.session` or
204
- `lifecycle.turn`. They can update earlier dynamic skills with the same
205
- name, but cannot replace authored skills discovered under `agent/skills/`.
206
-
207
193
  ### Once-per-session text
208
194
 
209
195
  ```ts
@@ -301,7 +287,9 @@ when both are registered.
301
287
 
302
288
  ## What to read next
303
289
 
304
- - [Tools](./tools.md)
290
+ - [`useAshAgent`](../frontend/use-ash-agent.md)
291
+ - [Next.js](../frontend/nextjs.md)
292
+ - [Tools](./tools.mdx)
305
293
  - [Context Control](./context-control.md)
306
294
  - [Sessions And Streaming](./runs-and-streaming.md)
307
295
  - [Session Context](./session-context.md)
@@ -52,7 +52,95 @@ These fields control what the AI SDK records inside OTel spans:
52
52
  - `recordOutputs` -- record model outputs on spans (defaults to `true`). Set to `false` to disable
53
53
  output recording.
54
54
  - `functionId` -- override the function name on spans (defaults to the agent name)
55
- - `metadata` -- additional key-value pairs added to every span
55
+ - `metadata["step.started"]` -- a synchronous callback that returns key-value pairs for one
56
+ model-call attempt
57
+
58
+ ## Metadata
59
+
60
+ Use `metadata["step.started"]` when the metadata depends on the current session, turn, step,
61
+ channel, or model input:
62
+
63
+ ```ts
64
+ import type { SlackInstrumentationMetadata } from "experimental-ash/channels/slack";
65
+
66
+ // A Slack-backed channel authored at `agent/channels/support.ts` has kind
67
+ // `channel:support` (its filename).
68
+ declare module "experimental-ash/instrumentation" {
69
+ interface ChannelMetadataMap {
70
+ readonly "channel:support": SlackInstrumentationMetadata;
71
+ }
72
+ }
73
+
74
+ export default defineInstrumentation({
75
+ metadata: {
76
+ "step.started"(input) {
77
+ if (input.channel.kind !== "channel:support") {
78
+ return {};
79
+ }
80
+
81
+ return {
82
+ "slack.channel_id": input.channel.metadata.channelId ?? "",
83
+ "slack.user_id": input.channel.metadata.triggeringUserId ?? "",
84
+ };
85
+ },
86
+ },
87
+ });
88
+ ```
89
+
90
+ The callback runs after Ash has assembled the model input for the attempt and before constructing
91
+ the AI SDK call. That timing lets the returned values attach to the AI SDK model-call span and its
92
+ child spans. It runs for each model-call attempt, including a retry of the same logical step when
93
+ Ash changes provider settings or instructions.
94
+
95
+ The callback receives:
96
+
97
+ - `session` -- the session id, current/initiator auth, and parent session lineage when this is a
98
+ child run
99
+ - `turn` -- the stream turn id and sequence, e.g. `turn_0`
100
+ - `step` -- the zero-based step index inside the turn
101
+ - `channel` -- the channel's `kind` and the metadata projected by the active channel
102
+ - `modelInput` -- the final instructions and messages passed to the model call
103
+
104
+ A channel exposes its identity through `kind`: the discriminant you narrow on. For authored
105
+ channels it is `channel:<name>`, where `<name>` is the channel's filename under
106
+ `agent/channels/` — `agent/channels/support.ts` is `channel:support`. Framework channels use
107
+ `http`, `schedule`, or `subagent`. It is also emitted as the `ash.channel.kind` span attribute.
108
+
109
+ Channel metadata is channel-owned. Built-in channels expose only the fields they choose to make
110
+ observable; for example, Slack projects `channelId`, `teamId`, `threadTs`, and `triggeringUserId`
111
+ from its durable channel state. User-authored channels expose their own projection by returning
112
+ `metadata(state)` from `defineChannel`. To make TypeScript narrow `input.channel.metadata`,
113
+ declaration-merge the same `channel:<name>` kind into `ChannelMetadataMap`:
114
+
115
+ `metadata(state)` must return an object composed of JSON primitives, arrays, and plain objects.
116
+ Ash omits `undefined` object properties and drops projections containing values such as `Date` or
117
+ `Map` with a warning.
118
+
119
+ ```ts
120
+ import { defineChannel } from "experimental-ash/channels";
121
+
122
+ declare module "experimental-ash/instrumentation" {
123
+ interface ChannelMetadataMap {
124
+ readonly "channel:support": {
125
+ readonly triggeringUserId: string | null;
126
+ };
127
+ }
128
+ }
129
+
130
+ export default defineChannel({
131
+ state: { triggeringUserId: null as string | null },
132
+ metadata(state) {
133
+ return { triggeringUserId: state.triggeringUserId };
134
+ },
135
+ routes: [],
136
+ });
137
+ ```
138
+
139
+ Metadata failures are non-destructive. If the callback throws, returns a non-record, or returns
140
+ non-string values, Ash logs a warning, drops the invalid metadata, and continues the model call.
141
+ Thenables are rejected; the callback is intentionally synchronous. Keys beginning with `ash.` are
142
+ reserved, so authored metadata cannot override framework metadata. Instrumentation projections
143
+ containing values outside the JSON shape, such as `Date` or `Map`, are dropped with a warning.
56
144
 
57
145
  ## Trace Hierarchy
58
146
 
@@ -70,8 +158,9 @@ ash.turn {ash.session.id, ash.turn.id}
70
158
  ```
71
159
 
72
160
  Ash creates the `ash.turn` parent span per turn and passes enriched telemetry to the AI SDK so model
73
- calls and tool executions are traced automatically. Session context (`ash.version`, `ash.session.id`,
74
- `ash.continuation_token`, `ash.environment`) is injected into span metadata.
161
+ calls and tool executions are traced automatically. Session, turn, step, and channel context
162
+ (`ash.version`, `ash.session.id`, `ash.environment`, `ash.turn.id`, `ash.turn.sequence`,
163
+ `ash.step.index`, `ash.channel.kind`) is injected into span metadata.
75
164
 
76
165
  ## What To Read Next
77
166
 
@@ -39,7 +39,7 @@ my-agent/
39
39
  | `instrumentation.ts` | Telemetry config | OTel exporter setup and AI SDK span settings; auto-discovered and run before agent code |
40
40
  | `channels/` | HTTP or messaging entrypoints | Root-only today |
41
41
  | `connections/` | External service connections (MCP) | Each file defines one connection; name derived from filename |
42
- | `hooks/` | Lifecycle and stream-event subscribers | Module-backed only. Recursive directories supported. See [Hooks](./hooks.md). |
42
+ | `hooks/` | Lifecycle and stream-event subscribers | Module-backed only. Recursive directories supported. See [Hooks](./hooks.mdx). |
43
43
  | `skills/` | On-demand procedures and capability packs | Flat markdown, module-backed skills, or packaged skills |
44
44
  | `lib/` | Shared authored helper code | Import-only source, not mounted into the workspace |
45
45
  | `sandbox/` or `sandbox.ts` | The agent's single sandbox. Use top-level `sandbox.ts` for a definition-only override; use `sandbox/sandbox.ts` + optional `sandbox/workspace/**` when you also want seeded files. Framework default applies when neither is authored. | Supported on the root agent and local subagents |
@@ -156,15 +156,15 @@ Use:
156
156
  - `channels/` for HTTP or messaging ingress and delivery
157
157
  - `lib/` for shared helper modules imported by the slots above
158
158
 
159
- See [Connections](./connections.md) for the connection authorization shape, including the
159
+ See [Connections](./connections.mdx) for the connection authorization shape, including the
160
160
  `@vercel/connect/ash` helper for OAuth-backed connections.
161
161
 
162
162
  ## What To Read Next
163
163
 
164
164
  - [`agent.ts`](./agent-ts.md)
165
165
  - [Context Control](./context-control.md)
166
- - [Connections](./connections.md)
167
- - [Hooks](./hooks.md)
166
+ - [Connections](./connections.mdx)
167
+ - [Hooks](./hooks.mdx)
168
168
  - [Skills](./skills.md)
169
- - [Tools](./tools.md)
169
+ - [Tools](./tools.mdx)
170
170
  - [Channels](./channels/README.md)
@@ -13,6 +13,10 @@ The key client model is:
13
13
  - `POST /ash/v1/session/:sessionId` sends a follow-up message
14
14
  - `GET /ash/v1/session/:sessionId/stream` lets you watch the session in real time
15
15
 
16
+ React apps can use [`useAshAgent()`](../frontend/use-ash-agent.md) instead of calling these
17
+ routes directly. Next.js apps can use [`withAsh()`](../frontend/nextjs.md) to proxy these
18
+ routes to the Ash runtime from the same origin.
19
+
16
20
  Ash keeps those separate on purpose:
17
21
 
18
22
  - channels own `continuationToken` as the resume handle
@@ -109,6 +113,8 @@ Important behavior:
109
113
 
110
114
  ## What To Read Next
111
115
 
116
+ - [`useAshAgent`](../frontend/use-ash-agent.md)
117
+ - [Next.js](../frontend/nextjs.md)
112
118
  - [Session Context](./session-context.md)
113
- - [Subagents](./subagents.md)
114
- - [Schedules](./schedules.md)
119
+ - [Subagents](./subagents.mdx)
120
+ - [Schedules](./schedules.mdx)
@@ -205,5 +205,5 @@ the public accessors.
205
205
  ## What To Read Next
206
206
 
207
207
  - [Sessions And Streaming](./runs-and-streaming.md)
208
- - [Subagents](./subagents.md)
208
+ - [Subagents](./subagents.mdx)
209
209
  - [Skills](./skills.md)
@@ -14,6 +14,9 @@ Source of truth:
14
14
  - tools subpath: [`../../packages/ash/src/public/tools/index.ts`](../../packages/ash/src/public/tools/index.ts)
15
15
  - sandbox subpath: [`../../packages/ash/src/public/sandbox/index.ts`](../../packages/ash/src/public/sandbox/index.ts)
16
16
  - channel subpaths: [`../../packages/ash/src/public/channels/index.ts`](../../packages/ash/src/public/channels/index.ts)
17
+ - Next.js subpath: [`../../packages/ash/src/public/next/index.ts`](../../packages/ash/src/public/next/index.ts)
18
+ - React subpath: [`../../packages/ash/src/react/index.ts`](../../packages/ash/src/react/index.ts)
19
+ - client subpath: [`../../packages/ash/src/client/index.ts`](../../packages/ash/src/client/index.ts)
17
20
  - evals subpath: [`../../packages/ash/src/evals/index.ts`](../../packages/ash/src/evals/index.ts)
18
21
 
19
22
  ## Definition Helpers
@@ -69,6 +72,26 @@ Related exported types by subpath:
69
72
  Ash also exports lower-level runtime primitives such as `createToolLoopHarness(...)`
70
73
  and `createWorkflowRuntime(...)`.
71
74
 
75
+ Next.js helpers exported from `experimental-ash/next`:
76
+
77
+ - `withAsh(config, options?)` - wraps a Next.js config so same-origin Ash protocol routes proxy to an Ash service
78
+ - `ASH_NEXT_SERVICE_PREFIX` - default private Vercel service prefix for the Ash service
79
+ - `WithAshOptions` - options for `ashRoot`, `ashBuildCommand`, `configureVercelJson`, and `servicePrefix`
80
+
81
+ React helpers exported from `experimental-ash/react`:
82
+
83
+ - `useAshAgent(options?)` - React hook for sending turns, streaming events, projecting UI state, and tracking a session cursor
84
+ - `defaultMessageReducer()` - default chat-style event projection used by `useAshAgent()`
85
+ - `UseAshAgentOptions`, `UseAshAgentHelpers`, `UseAshAgentSnapshot`, `UseAshAgentStatus` - hook configuration and state types
86
+ - `AshMessageData`, `AshMessage`, `AshMessagePart`, `AshDynamicToolPart` - default message projection types
87
+
88
+ Client helpers exported from `experimental-ash/client`:
89
+
90
+ - `Client` - HTTP client for an Ash server
91
+ - `ClientSession` - one durable session cursor with `send(...)`, `sendMessage(...)`, and stream helpers
92
+ - `defaultMessageReducer()` - reducer shared with the React package
93
+ - `SessionState`, `SendTurnInput`, `SendMessageOptions`, `ClientOptions` - client request and session types
94
+
72
95
  Channel and Slack types exported from `experimental-ash/channels/slack`:
73
96
 
74
97
  - `slackChannel` - Slack channel factory; zero-config by default, accepts a `SlackChannelConfig`
@@ -231,7 +254,7 @@ import {
231
254
  } from "experimental-ash/tools/defaults";
232
255
  ```
233
256
 
234
- See [Tools](./tools.md) for the full override patterns.
257
+ See [Tools](./tools.mdx) for the full override patterns.
235
258
 
236
259
  ### Runtime Mode
237
260
 
@@ -239,6 +262,24 @@ See [Tools](./tools.md) for the full override patterns.
239
262
  import type { RunMode } from "experimental-ash";
240
263
  ```
241
264
 
265
+ ### Next.js Integration
266
+
267
+ ```ts
268
+ import { withAsh } from "experimental-ash/next";
269
+ ```
270
+
271
+ ### React Client
272
+
273
+ ```ts
274
+ import { useAshAgent } from "experimental-ash/react";
275
+ ```
276
+
277
+ ### HTTP Client
278
+
279
+ ```ts
280
+ import { Client } from "experimental-ash/client";
281
+ ```
282
+
242
283
  ### Eval Suite
243
284
 
244
285
  ```ts
@@ -252,14 +293,16 @@ import { Braintrust } from "experimental-ash/evals/reporters";
252
293
 
253
294
  - `defineAgent` -> [`agent.ts`](./agent-ts.md)
254
295
  - `defineChannel` and channel factories -> [Channels](./channels/README.md)
255
- - `defineTool`, `disableTool`, `defineBashTool`, `defineReadFileTool`, `defineWriteFileTool`, and `experimental-ash/tools/defaults` -> [Tools](./tools.md)
256
- - `defineHook`, `HookContext`, and lifecycle/event types -> [Hooks](./hooks.md)
296
+ - `defineTool`, `disableTool`, `defineBashTool`, `defineReadFileTool`, `defineWriteFileTool`, and `experimental-ash/tools/defaults` -> [Tools](./tools.mdx)
297
+ - `defineHook`, `HookContext`, and lifecycle/event types -> [Hooks](./hooks.mdx)
257
298
  - `defineSandbox` and `ctx.getSandbox()` -> [Sandboxes](./sandbox.md)
258
299
  - `defineSkill` and `ctx.getSkill()` -> [Skills](./skills.md)
259
300
  - `ctx.session` -> [Session Context](./session-context.md)
260
- - subagents (authored with `defineAgent` under `subagents/<id>/agent.ts`) -> [Subagents](./subagents.md)
261
- - `defineSchedule` -> [Schedules](./schedules.md)
262
- - `defineEvalSuite`, loaders, reporters, and scorers -> [Evals](./evals.md)
301
+ - `withAsh` -> [Next.js](../frontend/nextjs.md)
302
+ - `useAshAgent` -> [`useAshAgent`](../frontend/use-ash-agent.md)
303
+ - subagents (authored with `defineAgent` under `subagents/<id>/agent.ts`) -> [Subagents](./subagents.mdx)
304
+ - `defineSchedule` -> [Schedules](./schedules.mdx)
305
+ - `defineEvalSuite`, loaders, reporters, and scorers -> [Evals](./evals.mdx)
263
306
 
264
307
  ## Practical Advice
265
308
 
@@ -106,4 +106,4 @@ This is useful for smoke tests against preview or production environments.
106
106
 
107
107
  - [Workspace](./workspace.md)
108
108
  - [Sandboxes](./sandbox.md)
109
- - [Auth And Route Protection](./auth-and-route-protection.md)
109
+ - [Auth And Route Protection](./auth-and-route-protection.mdx)
@@ -38,7 +38,7 @@ Use `agent.ts` for:
38
38
  - provider-specific model options
39
39
 
40
40
  Per-tool approval (formerly "human-in-the-loop policy") lives on each tool via `needsApproval`,
41
- not in `agent.ts`. See [Human In The Loop](./human-in-the-loop.md).
41
+ not in `agent.ts`. See [Human In The Loop](./human-in-the-loop.mdx).
42
42
 
43
43
  For OpenTelemetry configuration, use `instrumentation.ts` instead. See
44
44
  [`instrumentation.ts`](./instrumentation.md).
@@ -135,7 +135,7 @@ The shared per-run workspace is configured on the sandbox, not on `agent.ts`. Se
135
135
  ## Human In The Loop
136
136
 
137
137
  Approval policy lives on individual tools via `needsApproval` in `defineTool({...})`, not on
138
- `agent.ts`. See [Human In The Loop](./human-in-the-loop.md) for the runtime flow, `input.requested`
138
+ `agent.ts`. See [Human In The Loop](./human-in-the-loop.mdx) for the runtime flow, `input.requested`
139
139
  events, and HTTP response payloads.
140
140
 
141
141
  ## Telemetry
@@ -149,7 +149,7 @@ OpenTelemetry tracing is configured in `agent/instrumentation.ts`, not in `agent
149
149
 
150
150
  Those concerns live on the HTTP channel layer instead. See:
151
151
 
152
- - [Auth And Route Protection](./auth-and-route-protection.md)
152
+ - [Auth And Route Protection](./auth-and-route-protection.mdx)
153
153
  - [Channels](./channels/README.md)
154
154
 
155
155
  ## A Good Default
@@ -169,6 +169,6 @@ That is enough to start. Add build and compaction settings only when you need th
169
169
  ## What To Read Next
170
170
 
171
171
  - [Context Control](./context-control.md)
172
- - [Human In The Loop](./human-in-the-loop.md)
172
+ - [Human In The Loop](./human-in-the-loop.mdx)
173
173
  - [Workspace](./workspace.md)
174
- - [Auth And Route Protection](./auth-and-route-protection.md)
174
+ - [Auth And Route Protection](./auth-and-route-protection.mdx)
@@ -9,6 +9,17 @@ The Discord channel accepts HTTP Interactions: slash/application commands, messa
9
9
  modal submissions. It verifies Discord's Ed25519 signature headers before parsing the request,
10
10
  acknowledges commands immediately, and continues Ash work in the background.
11
11
 
12
+ <CopyPrompt text="Add Discord support to the user's Ash project. Ash Discord channels are authored in agent/channels/discord.ts with discordChannel, verify Discord Ed25519 interaction signatures, mount POST /ash/v1/discord by default, defer command responses, deliver replies and HITL components, and can use proactive bot-token messages. Inspect existing channels and env conventions, add the channel file, configure DISCORD_PUBLIC_KEY, DISCORD_APPLICATION_ID, and DISCORD_BOT_TOKEN or equivalent config, set the Discord Interactions Endpoint URL, register or update an application command with a string message option, verify command delivery and HITL behavior, and do not commit unless the user asks.">
13
+ Add Discord support to the user's Ash project. Ash Discord channels are authored in
14
+ agent/channels/discord.ts with discordChannel, verify Discord Ed25519 interaction signatures,
15
+ mount POST /ash/v1/discord by default, defer command responses, deliver replies and HITL
16
+ components, and can use proactive bot-token messages. Inspect existing channels and env
17
+ conventions, add the channel file, configure DISCORD_PUBLIC_KEY, DISCORD_APPLICATION_ID, and
18
+ DISCORD_BOT_TOKEN or equivalent config, set the Discord Interactions Endpoint URL, register or
19
+ update an application command with a string message option, verify command delivery and HITL
20
+ behavior, and do not commit unless the user asks.
21
+ </CopyPrompt>
22
+
12
23
  ## Add The Channel
13
24
 
14
25
  Create `agent/channels/discord.ts`: