experimental-ash 0.42.0 → 0.44.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 (184) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/bin/ash.js +1 -0
  3. package/dist/docs/internals/mechanical-invariants.md +16 -0
  4. package/dist/docs/public/README.md +8 -8
  5. package/dist/docs/public/advanced/{auth-and-route-protection.md → auth-and-route-protection.mdx} +11 -0
  6. package/dist/docs/public/advanced/context-control.md +4 -4
  7. package/dist/docs/public/advanced/{evals.md → evals.mdx} +11 -1
  8. package/dist/docs/public/advanced/{hooks.md → hooks.mdx} +28 -40
  9. package/dist/docs/public/advanced/instrumentation.md +142 -3
  10. package/dist/docs/public/advanced/project-layout.md +5 -5
  11. package/dist/docs/public/advanced/runs-and-streaming.md +8 -2
  12. package/dist/docs/public/advanced/session-context.md +1 -1
  13. package/dist/docs/public/advanced/typescript-api.md +50 -7
  14. package/dist/docs/public/advanced/vercel-deployment.md +1 -1
  15. package/dist/docs/public/agent-ts.md +5 -5
  16. package/dist/docs/public/channels/{discord.md → discord.mdx} +11 -0
  17. package/dist/docs/public/channels/index.md +10 -10
  18. package/dist/docs/public/channels/{slack.md → slack.mdx} +11 -0
  19. package/dist/docs/public/channels/{teams.md → teams.mdx} +12 -0
  20. package/dist/docs/public/channels/{telegram.md → telegram.mdx} +11 -0
  21. package/dist/docs/public/channels/{twilio.md → twilio.mdx} +11 -0
  22. package/dist/docs/public/{connections.md → connections.mdx} +18 -6
  23. package/dist/docs/public/frontend/README.md +16 -0
  24. package/dist/docs/public/frontend/meta.json +3 -0
  25. package/dist/docs/public/frontend/nextjs.md +192 -0
  26. package/dist/docs/public/frontend/use-ash-agent.md +332 -0
  27. package/dist/docs/public/{getting-started.md → getting-started.mdx} +12 -1
  28. package/dist/docs/public/{human-in-the-loop.md → human-in-the-loop.mdx} +12 -1
  29. package/dist/docs/public/meta.json +1 -0
  30. package/dist/docs/public/sandbox.md +39 -1
  31. package/dist/docs/public/{schedules.md → schedules.mdx} +9 -0
  32. package/dist/docs/public/skills.md +2 -2
  33. package/dist/docs/public/{subagents.md → subagents.mdx} +10 -0
  34. package/dist/docs/public/{tools.md → tools.mdx} +41 -26
  35. package/dist/src/channel/adapter.d.ts +13 -0
  36. package/dist/src/channel/compiled-channel.d.ts +4 -1
  37. package/dist/src/channel/compiled-channel.js +1 -1
  38. package/dist/src/channel/instrumentation.d.ts +10 -0
  39. package/dist/src/channel/instrumentation.js +1 -0
  40. package/dist/src/channel/routes.d.ts +8 -10
  41. package/dist/src/channel/send.js +1 -1
  42. package/dist/src/channel/types.d.ts +16 -0
  43. package/dist/src/cli/commands/channels.d.ts +2 -1
  44. package/dist/src/cli/commands/channels.js +1 -1
  45. package/dist/src/compiled/.vendor-stamp.json +1 -1
  46. package/dist/src/compiled/@vercel/sandbox/index.d.ts +12 -19
  47. package/dist/src/compiled/@vercel/sandbox/network-policy.d.ts +161 -0
  48. package/dist/src/compiled/just-bash/index.d.ts +15 -2
  49. package/dist/src/compiled/just-bash/network/types.d.ts +155 -0
  50. package/dist/src/compiler/artifacts.d.ts +1 -0
  51. package/dist/src/compiler/artifacts.js +1 -1
  52. package/dist/src/compiler/channel-instrumentation-types.d.ts +8 -0
  53. package/dist/src/compiler/channel-instrumentation-types.js +2 -0
  54. package/dist/src/compiler/manifest.d.ts +13 -1
  55. package/dist/src/compiler/manifest.js +1 -1
  56. package/dist/src/compiler/module-map.js +1 -1
  57. package/dist/src/compiler/normalize-manifest.js +1 -1
  58. package/dist/src/compiler/normalize-skill.d.ts +15 -2
  59. package/dist/src/compiler/normalize-skill.js +1 -1
  60. package/dist/src/compiler/normalize-tool.js +1 -1
  61. package/dist/src/context/dynamic-skill-lifecycle.d.ts +23 -0
  62. package/dist/src/context/dynamic-skill-lifecycle.js +1 -0
  63. package/dist/src/context/dynamic-tool-lifecycle.d.ts +2 -0
  64. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  65. package/dist/src/context/hook-lifecycle.d.ts +4 -6
  66. package/dist/src/context/hook-lifecycle.js +1 -1
  67. package/dist/src/context/keys.d.ts +6 -4
  68. package/dist/src/context/keys.js +1 -1
  69. package/dist/src/context/providers/connection.d.ts +9 -0
  70. package/dist/src/context/providers/connection.js +1 -1
  71. package/dist/src/context/providers/sandbox.js +1 -1
  72. package/dist/src/execution/ash-workflow-attributes.d.ts +118 -0
  73. package/dist/src/execution/ash-workflow-attributes.js +1 -0
  74. package/dist/src/execution/channel-context.d.ts +5 -0
  75. package/dist/src/execution/channel-context.js +1 -0
  76. package/dist/src/execution/create-session-step.d.ts +28 -1
  77. package/dist/src/execution/create-session-step.js +1 -1
  78. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  79. package/dist/src/execution/durable-session-store.d.ts +7 -0
  80. package/dist/src/execution/runtime-context.js +1 -1
  81. package/dist/src/execution/sandbox/bindings/local.js +1 -1
  82. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  83. package/dist/src/execution/sandbox/prewarm.js +1 -1
  84. package/dist/src/execution/sandbox/session.d.ts +6 -1
  85. package/dist/src/execution/sandbox/session.js +1 -1
  86. package/dist/src/execution/session.d.ts +6 -0
  87. package/dist/src/execution/session.js +2 -2
  88. package/dist/src/execution/skills/instructions.d.ts +3 -2
  89. package/dist/src/execution/subagent-tool.js +1 -1
  90. package/dist/src/execution/workflow-entry.js +1 -1
  91. package/dist/src/execution/workflow-steps.js +1 -1
  92. package/dist/src/harness/attachment-staging.js +1 -1
  93. package/dist/src/harness/code-mode.d.ts +0 -5
  94. package/dist/src/harness/code-mode.js +1 -1
  95. package/dist/src/harness/emission.d.ts +1 -1
  96. package/dist/src/harness/emission.js +1 -1
  97. package/dist/src/harness/instrumentation-config.d.ts +1 -1
  98. package/dist/src/harness/instrumentation-metadata.d.ts +23 -0
  99. package/dist/src/harness/instrumentation-metadata.js +1 -0
  100. package/dist/src/harness/otel-integration.d.ts +2 -2
  101. package/dist/src/harness/otel-integration.js +1 -1
  102. package/dist/src/harness/step-hooks.js +1 -1
  103. package/dist/src/harness/tool-loop.js +1 -1
  104. package/dist/src/harness/turn-tag-state.d.ts +50 -0
  105. package/dist/src/harness/turn-tag-state.js +1 -0
  106. package/dist/src/harness/types.d.ts +11 -2
  107. package/dist/src/internal/application/package.js +1 -1
  108. package/dist/src/internal/authored-definition/schema-backed.d.ts +0 -1
  109. package/dist/src/internal/authored-definition/schema-backed.js +1 -1
  110. package/dist/src/internal/instrumentation.d.ts +39 -0
  111. package/dist/src/internal/instrumentation.js +1 -0
  112. package/dist/src/internal/workflow/builtins.d.ts +32 -0
  113. package/dist/src/internal/workflow/builtins.js +1 -1
  114. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +1 -1
  115. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +1 -1
  116. package/dist/src/internal/workflow-bundle/workflow-core-shim.d.ts +34 -0
  117. package/dist/src/internal/workflow-bundle/workflow-core-shim.js +1 -1
  118. package/dist/src/internal/workflow-bundle/workflow-transformer.js +1 -1
  119. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  120. package/dist/src/packages/ash-scaffold/src/steps/run-add-to-agent.js +2 -2
  121. package/dist/src/packages/ash-scaffold/src/web-template.js +1 -0
  122. package/dist/src/public/channels/discord/discordChannel.d.ts +5 -2
  123. package/dist/src/public/channels/index.d.ts +1 -1
  124. package/dist/src/public/channels/slack/attachments.js +1 -1
  125. package/dist/src/public/channels/slack/index.d.ts +1 -1
  126. package/dist/src/public/channels/slack/slackChannel.d.ts +12 -8
  127. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  128. package/dist/src/public/channels/teams/teamsChannel.d.ts +5 -2
  129. package/dist/src/public/channels/telegram/telegramChannel.d.ts +5 -2
  130. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  131. package/dist/src/public/channels/twilio/index.d.ts +1 -1
  132. package/dist/src/public/channels/twilio/twilioChannel.d.ts +12 -3
  133. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  134. package/dist/src/public/definitions/defineChannel.d.ts +17 -4
  135. package/dist/src/public/definitions/defineChannel.js +1 -1
  136. package/dist/src/public/definitions/hook.d.ts +3 -11
  137. package/dist/src/public/definitions/instrumentation.d.ts +1 -66
  138. package/dist/src/public/definitions/instrumentation.js +1 -1
  139. package/dist/src/public/definitions/skill.d.ts +5 -0
  140. package/dist/src/public/definitions/tool.d.ts +25 -66
  141. package/dist/src/public/definitions/tool.js +1 -1
  142. package/dist/src/public/instrumentation/index.d.ts +175 -1
  143. package/dist/src/public/instrumentation/index.js +1 -1
  144. package/dist/src/public/sandbox/index.d.ts +1 -0
  145. package/dist/src/public/skills/index.d.ts +2 -0
  146. package/dist/src/public/skills/index.js +1 -1
  147. package/dist/src/public/tools/index.d.ts +2 -2
  148. package/dist/src/public/tools/index.js +1 -1
  149. package/dist/src/runtime/agent/mock-model-adapter.js +4 -7
  150. package/dist/src/runtime/agent/mock-model-skill-selection.d.ts +9 -0
  151. package/dist/src/runtime/agent/mock-model-skill-selection.js +4 -0
  152. package/dist/src/runtime/attributes/emit.d.ts +73 -0
  153. package/dist/src/runtime/attributes/emit.js +1 -0
  154. package/dist/src/runtime/channels/registry.js +1 -1
  155. package/dist/src/runtime/connections/mcp-client.js +1 -1
  156. package/dist/src/runtime/framework-tools/code-mode-connection-auth.d.ts +2 -0
  157. package/dist/src/runtime/framework-tools/connection-search-dynamic.d.ts +34 -0
  158. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +1 -0
  159. package/dist/src/runtime/framework-tools/index.d.ts +7 -5
  160. package/dist/src/runtime/framework-tools/index.js +1 -1
  161. package/dist/src/runtime/prompt/connections.js +1 -1
  162. package/dist/src/runtime/resolve-agent-graph.js +1 -1
  163. package/dist/src/runtime/resolve-agent.js +1 -1
  164. package/dist/src/runtime/resolve-channel.js +1 -1
  165. package/dist/src/runtime/resolve-dynamic-skill.d.ts +8 -0
  166. package/dist/src/runtime/resolve-dynamic-skill.js +1 -0
  167. package/dist/src/runtime/resolve-dynamic-tool.js +1 -1
  168. package/dist/src/runtime/sessions/compiled-agent-cache.js +1 -1
  169. package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
  170. package/dist/src/runtime/types.d.ts +13 -4
  171. package/dist/src/shared/dynamic-tool-definition.d.ts +51 -76
  172. package/dist/src/shared/dynamic-tool-definition.js +1 -1
  173. package/dist/src/shared/guards.d.ts +14 -0
  174. package/dist/src/shared/guards.js +1 -1
  175. package/dist/src/shared/sandbox-network-policy.d.ts +23 -0
  176. package/dist/src/shared/sandbox-network-policy.js +1 -0
  177. package/dist/src/shared/sandbox-session.d.ts +15 -0
  178. package/dist/src/shared/skill-definition.d.ts +5 -4
  179. package/dist/src/shared/tool-definition.d.ts +12 -0
  180. package/package.json +2 -1
  181. package/dist/src/runtime/framework-tools/connection-search.d.ts +0 -57
  182. package/dist/src/runtime/framework-tools/connection-search.js +0 -1
  183. package/dist/src/runtime/framework-tools/connection-tools.d.ts +0 -55
  184. package/dist/src/runtime/framework-tools/connection-tools.js +0 -1
@@ -1,4 +1,5 @@
1
- import type { PublicToolInputSchema } from "#shared/tool-definition.js";
1
+ import type { ModelMessage } from "ai";
2
+ import type { PublicToolInputSchema, ToolModelOutput } from "#shared/tool-definition.js";
2
3
  import type { SessionContext } from "#public/definitions/callback-context.js";
3
4
  import type { SessionAuth } from "#context/keys.js";
4
5
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
@@ -11,14 +12,14 @@ type ToolContext = SessionContext;
11
12
  export type DynamicToolEventName = Extract<HandleMessageStreamEvent["type"], "session.started" | "turn.started" | "step.started">;
12
13
  export declare const ALLOWED_DYNAMIC_TOOL_EVENTS: ReadonlySet<string>;
13
14
  /**
14
- * Context passed to a dynamic tool resolver's event handler.
15
+ * Context passed to a dynamic resolver's event handler (tools and skills).
15
16
  *
16
17
  * Provides read-only access to session identity, auth, and channel
17
18
  * metadata. State access is not exposed here — resolvers read state
18
19
  * through `defineState` handles or via the session context available
19
20
  * inside tool `execute` functions.
20
21
  */
21
- export interface DynamicToolResolveContext {
22
+ export interface DynamicResolveContext {
22
23
  readonly session: {
23
24
  readonly id: string;
24
25
  readonly auth: SessionAuth;
@@ -27,108 +28,82 @@ export interface DynamicToolResolveContext {
27
28
  readonly kind?: string;
28
29
  readonly continuationToken?: string;
29
30
  };
30
- /**
31
- * Compiler-injected durable cache for resolver I/O results.
32
- *
33
- * First call (per session): executes `fn`, stores the serializable
34
- * result durably, returns it.
35
- * Replay (subsequent workflow steps): returns the cached result
36
- * without calling `fn`.
37
- *
38
- * The cached value must be JSON-serializable. Non-serializable
39
- * values cause a runtime error on first cache write.
40
- *
41
- * @internal Injected by the `ash:dynamic-tool-await-cache` bundler
42
- * transform. Not intended for direct author use.
43
- */
44
- __cache<T>(key: string, fn: () => T | Promise<T>): Promise<T>;
31
+ readonly messages: readonly ModelMessage[];
45
32
  }
46
33
  /**
47
34
  * A single tool entry within a resolved dynamic tool set.
48
35
  *
49
- * Identity comes from the entry's Record key there is no `name`
50
- * field. For `defineTools`, the runtime tool name is always
51
- * `slug__key`. For `defineTool`, it is the file slug alone.
36
+ * Identity comes from context: when the resolver handler returns a
37
+ * single entry, the tool is named after the file slug. When the
38
+ * handler returns a `Record<string, DynamicToolEntry>`, each entry
39
+ * is named `slug__key`.
52
40
  *
53
41
  * `TInput` defaults to `Record<string, unknown>` but is inferred
54
- * automatically when `inputSchema` is a Standard Schema (e.g. Zod):
55
- *
56
- * ```ts
57
- * {
58
- * inputSchema: z.object({ city: z.string() }),
59
- * async execute(input) { return fetchWeather(input.city); },
60
- * }
61
- * ```
62
- *
63
- * When `inputSchema` is a plain JSON object, annotate `execute`
64
- * explicitly:
65
- *
66
- * ```ts
67
- * {
68
- * inputSchema: { type: "object", properties: { city: { type: "string" } } },
69
- * async execute(input: { city: string }) { return fetchWeather(input.city); },
70
- * }
71
- * ```
42
+ * automatically when `inputSchema` is a Standard Schema (e.g. Zod)
43
+ * via the `defineTool` wrapper.
72
44
  */
73
- export interface DynamicToolEntry<TInput = Record<string, unknown>, TOutput = unknown> {
74
- /** @internal Stamp set by `tool()` — raw object literals are rejected. */
75
- readonly __brand: "ash:tool";
45
+ export interface DynamicToolEntry<TInput = Record<string, unknown>, TOutput = any> {
76
46
  readonly description: string;
77
47
  readonly inputSchema: PublicToolInputSchema<TInput>;
78
48
  execute(input: TInput, ctx: ToolContext): TOutput | Promise<TOutput>;
49
+ readonly toModelOutput?: (output: TOutput) => ToolModelOutput | Promise<ToolModelOutput>;
79
50
  }
80
51
  /**
81
52
  * A resolved tool set: keys are entry identifiers, values are tool
82
- * entries produced by the {@link tool} helper. Uses `any` for the
83
- * type params so entries with different schemas are assignable to
84
- * the same Record — `tool()` captures the per-entry types before
53
+ * entries produced by {@link defineTool}. Uses `any` for the type
54
+ * params so entries with different schemas are assignable to the
55
+ * same Record — `defineTool()` captures the per-entry types before
85
56
  * they reach this widened container.
86
57
  */
87
58
  export type DynamicToolSet = Readonly<Record<string, DynamicToolEntry<any, any>>>;
88
59
  /**
89
- * Event handler map for dynamic tool definitions. Each key is a
90
- * supported event name; each value is a resolver function that
91
- * receives the stream event and resolve context, and returns a
92
- * record of tool entries or `null`.
93
- *
94
- * The handler return type is `DynamicToolSet` — using
95
- * `DynamicToolEntry<any, any>` so per-entry Zod schemas infer
96
- * their own `execute(input)` type without being widened.
60
+ * Return type for a `defineDynamic` event handler. Each handler can
61
+ * return a single tool entry (named after the file slug), a map of
62
+ * entries (named `slug__key`), or `null` to produce no tools.
63
+ */
64
+ export type DynamicToolResult = DynamicToolEntry<any, any> | DynamicToolSet | null;
65
+ /**
66
+ * Event handler map for `defineDynamic`. Each key is a supported
67
+ * event name; each value is a resolver function that receives the
68
+ * stream event and resolve context, and returns a single tool entry,
69
+ * a record of tool entries, or `null`.
97
70
  */
98
71
  export type DynamicToolEvents = {
99
- readonly [K in DynamicToolEventName]?: (event: unknown, ctx: DynamicToolResolveContext) => DynamicToolSet | null | Promise<DynamicToolSet | null>;
72
+ readonly [K in DynamicToolEventName]?: (event: unknown, ctx: DynamicResolveContext) => DynamicToolResult | Promise<DynamicToolResult>;
100
73
  };
101
74
  /**
102
- * Event handler map for a singular dynamic tool. Each handler returns
103
- * a single tool entry or `null`.
75
+ * Base event handler map accepted by `defineDynamic`. Intentionally
76
+ * wide the slot directory (tools/ vs skills/) determines what the
77
+ * handlers must return, validated at runtime by the respective
78
+ * resolver. This type exists so `defineDynamic` can accept both
79
+ * tool-returning and skill-returning handlers.
104
80
  */
105
- export type DynamicSingleToolEvents = {
106
- readonly [K in DynamicToolEventName]?: (event: unknown, ctx: DynamicToolResolveContext) => DynamicToolEntry | null | Promise<DynamicToolEntry | null>;
81
+ export type DynamicEvents = {
82
+ readonly [K in DynamicToolEventName]?: (event: unknown, ctx: DynamicResolveContext) => unknown | Promise<unknown>;
107
83
  };
108
84
  /**
109
- * Marker discriminator for a `defineTools({ events })` export.
85
+ * Marker discriminator for a `defineDynamic({ events })` export.
110
86
  */
111
- export declare const DYNAMIC_TOOLS_SENTINEL_KIND: "ash:dynamic-tools";
87
+ export declare const DYNAMIC_SENTINEL_KIND: "ash:dynamic";
112
88
  /**
113
- * Marker discriminator for a `defineTool({ events })` export.
89
+ * Runtime shape of a `defineDynamic({ events })` export, stamped
90
+ * with a sentinel kind so the compiler/normalizer can detect it.
114
91
  */
115
- export declare const DYNAMIC_TOOL_SENTINEL_KIND: "ash:dynamic-tool";
92
+ export interface DynamicSentinel {
93
+ readonly kind: typeof DYNAMIC_SENTINEL_KIND;
94
+ readonly events: DynamicEvents;
95
+ }
96
+ export declare function isDynamicSentinel(value: unknown): value is DynamicSentinel;
116
97
  /**
117
- * Runtime shape of a `defineTools({ events })` export, stamped with a
118
- * sentinel kind so the compiler/normalizer can detect it.
98
+ * Symbol-based brand stamped by `defineTool` on every entry. Invisible
99
+ * in IntelliSense but checked at runtime to enforce the wrapper and
100
+ * to distinguish a single entry from a map of entries.
119
101
  */
120
- export interface DynamicToolsSentinel {
121
- readonly kind: typeof DYNAMIC_TOOLS_SENTINEL_KIND;
122
- readonly events: DynamicToolEvents;
123
- }
102
+ export declare const TOOL_BRAND: unique symbol;
124
103
  /**
125
- * Runtime shape of a `defineTool({ events })` export, stamped with a
126
- * sentinel kind so the compiler/normalizer can detect it.
104
+ * Returns true if `value` was stamped by `defineTool` (has the brand
105
+ * symbol). Used to detect single entry vs map of entries, and to
106
+ * validate that entries are properly wrapped.
127
107
  */
128
- export interface DynamicToolSentinel {
129
- readonly kind: typeof DYNAMIC_TOOL_SENTINEL_KIND;
130
- readonly events: DynamicSingleToolEvents;
131
- }
132
- export declare function isDynamicToolsSentinel(value: unknown): value is DynamicToolsSentinel;
133
- export declare function isDynamicToolSentinel(value: unknown): value is DynamicToolSentinel;
108
+ export declare function isBrandedToolEntry(value: unknown): boolean;
134
109
  export {};
@@ -1 +1 @@
1
- const ALLOWED_DYNAMIC_TOOL_EVENTS=new Set([`session.started`,`turn.started`,`step.started`]),DYNAMIC_TOOLS_SENTINEL_KIND=`ash:dynamic-tools`,DYNAMIC_TOOL_SENTINEL_KIND=`ash:dynamic-tool`;function isDynamicToolsSentinel(e){return typeof e==`object`&&!!e&&e.kind===`ash:dynamic-tools`}function isDynamicToolSentinel(e){return typeof e==`object`&&!!e&&e.kind===`ash:dynamic-tool`}export{ALLOWED_DYNAMIC_TOOL_EVENTS,DYNAMIC_TOOLS_SENTINEL_KIND,DYNAMIC_TOOL_SENTINEL_KIND,isDynamicToolSentinel,isDynamicToolsSentinel};
1
+ const ALLOWED_DYNAMIC_TOOL_EVENTS=new Set([`session.started`,`turn.started`,`step.started`]),DYNAMIC_SENTINEL_KIND=`ash:dynamic`;function isDynamicSentinel(e){return typeof e==`object`&&!!e&&e.kind===`ash:dynamic`}const TOOL_BRAND=Symbol.for(`ash:tool-brand`);function isBrandedToolEntry(e){return typeof e==`object`&&!!e&&e[TOOL_BRAND]===!0}export{ALLOWED_DYNAMIC_TOOL_EVENTS,DYNAMIC_SENTINEL_KIND,TOOL_BRAND,isBrandedToolEntry,isDynamicSentinel};
@@ -29,3 +29,17 @@ export declare function isObject(value: unknown): value is Record<string, unknow
29
29
  * meaningful value by display or correlation logic.
30
30
  */
31
31
  export declare function isNonEmptyString(value: unknown): value is string;
32
+ /**
33
+ * Returns `true` when `value` is a thenable — an object with a `then`
34
+ * function. Used to reject async instrumentation metadata projectors
35
+ * that accidentally return a Promise instead of a plain record.
36
+ */
37
+ export declare function isThenable(value: unknown): value is PromiseLike<unknown>;
38
+ /**
39
+ * Returns `true` when `value` is a plain object record — `{}`,
40
+ * `Object.create(null)`, or an object whose prototype is
41
+ * `Object.prototype`. Class instances, `Map`, `Date`, and other
42
+ * exotic objects are excluded even though `isObject` would accept
43
+ * them.
44
+ */
45
+ export declare function isPlainRecord(value: unknown): value is Record<string, unknown>;
@@ -1 +1 @@
1
- function isObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function isNonEmptyString(e){return typeof e==`string`&&e.length>0}export{isNonEmptyString,isObject};
1
+ function isObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function isNonEmptyString(e){return typeof e==`string`&&e.length>0}function isThenable(e){return isObject(e)&&typeof e.then==`function`}function isPlainRecord(e){if(!isObject(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}export{isNonEmptyString,isObject,isPlainRecord,isThenable};
@@ -0,0 +1,23 @@
1
+ import type { NetworkPolicy } from "#compiled/@vercel/sandbox/index.js";
2
+ /**
3
+ * Firewall network policy applied to a live sandbox session.
4
+ *
5
+ * Ash-owned alias of the backend network-policy shape. Use it to restrict
6
+ * egress (`"deny-all"`, an allow-list) or to broker credentials onto
7
+ * outgoing requests — a per-domain `transform` injects headers at the
8
+ * firewall so secrets never enter the sandbox process:
9
+ *
10
+ * ```ts
11
+ * const sandbox = await ctx.getSandbox();
12
+ * await sandbox.setNetworkPolicy({
13
+ * allow: {
14
+ * "github.com": [{ transform: [{ headers: { authorization: "Basic …" } }] }],
15
+ * "*": [],
16
+ * },
17
+ * });
18
+ * ```
19
+ *
20
+ * The local backend rejects `setNetworkPolicy`: just-bash cannot update its
21
+ * network policy at run time and runs no binaries to govern.
22
+ */
23
+ export type SandboxNetworkPolicy = NetworkPolicy;
@@ -0,0 +1 @@
1
+ export{};
@@ -1,4 +1,5 @@
1
1
  import type { Experimental_Sandbox as AiSdkSandbox } from "ai";
2
+ import type { SandboxNetworkPolicy } from "#shared/sandbox-network-policy.js";
2
3
  /**
3
4
  * Options for running one command in a sandbox. Shape mirrors the AI
4
5
  * SDK {@link AiSdkSandbox} `run` argument so authored code that targets
@@ -83,6 +84,20 @@ export interface SandboxSession extends Pick<AiSdkSandbox, "run" | "spawn" | "re
83
84
  * The read and write methods already apply this internally.
84
85
  */
85
86
  resolvePath(path: string): string;
87
+ /**
88
+ * Applies a firewall network policy to this live sandbox at run time —
89
+ * for changing the policy *during* a turn (e.g. brokering a credential
90
+ * resolved mid-turn, or tightening egress after fetching data). A
91
+ * per-domain `transform` injects headers at the firewall so secrets
92
+ * never enter the sandbox process. The policy takes effect from the time
93
+ * the call resolves, so await it before the egress you want governed.
94
+ *
95
+ * When the policy is known at session start, prefer configuring it up
96
+ * front in the sandbox backend factory or `onSession`'s `use()`. The
97
+ * local backend rejects this call: just-bash cannot update its network
98
+ * policy at run time and runs no binaries to govern.
99
+ */
100
+ setNetworkPolicy(policy: SandboxNetworkPolicy): Promise<void>;
86
101
  }
87
102
  /**
88
103
  * Internal sandbox session, used to construct the public {@link SandboxSession}.
@@ -4,8 +4,8 @@
4
4
  */
5
5
  export type SkillFileContent = string | Uint8Array;
6
6
  /**
7
- * Public skill package content shared by TypeScript-authored skills and
8
- * runtime hook contributions.
7
+ * Public skill package content for authored skills and dynamic skill
8
+ * resolvers.
9
9
  */
10
10
  export interface SkillPackageDefinition {
11
11
  readonly description: string;
@@ -15,8 +15,9 @@ export interface SkillPackageDefinition {
15
15
  readonly files?: Readonly<Record<string, SkillFileContent>>;
16
16
  }
17
17
  /**
18
- * Runtime-contributed skill package. Hook-created skills do not have an
19
- * authored file path, so they carry their name explicitly.
18
+ * Skill package with an explicit name. Used by compiled skill entries
19
+ * (where the name is path-derived) and by dynamic skill resolvers
20
+ * (where the name is qualified from the resolver slug + entry key).
20
21
  */
21
22
  export interface NamedSkillDefinition extends SkillPackageDefinition {
22
23
  readonly name: string;
@@ -35,4 +35,16 @@ export interface InternalToolDefinitionWithExecuteFn<TInput = unknown, TOutput =
35
35
  export interface PublicToolDefinitionWithExecuteFn<TInput = unknown, TOutput = unknown> extends PublicToolDefinition<TInput> {
36
36
  execute: ToolExecuteFn<TInput, TOutput>;
37
37
  }
38
+ /**
39
+ * Ash-owned shape for the model-facing tool result produced by
40
+ * `toModelOutput`. Structurally compatible with the AI SDK's
41
+ * `ToolResultOutput` so the harness can forward it without conversion.
42
+ */
43
+ export type ToolModelOutput = {
44
+ readonly type: "text";
45
+ readonly value: string;
46
+ } | {
47
+ readonly type: "json";
48
+ readonly value: unknown;
49
+ };
38
50
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-ash",
3
- "version": "0.42.0",
3
+ "version": "0.44.0",
4
4
  "bin": {
5
5
  "ash": "./bin/ash.js",
6
6
  "experimental-ash": "./bin/ash.js"
@@ -191,6 +191,7 @@
191
191
  "@vercel/sandbox": "2.0.1",
192
192
  "@workflow/core": "5.0.0-beta.10",
193
193
  "@workflow/errors": "5.0.0-beta.6",
194
+ "@workflow/world": "5.0.0-beta.5",
194
195
  "@workflow/world-local": "5.0.0-beta.11",
195
196
  "ai": "7.0.0-canary.159",
196
197
  "autoevals": "0.0.132",
@@ -1,57 +0,0 @@
1
- import { ContextKey } from "#context/key.js";
2
- import { type AuthorizationSignal } from "#harness/authorization.js";
3
- import { type ConnectionRegistry, type ConnectionToolMetadata } from "#runtime/connections/types.js";
4
- import type { ResolvedToolDefinition } from "#runtime/types.js";
5
- import { type CodeModeConnectionAuthContext } from "#runtime/framework-tools/code-mode-connection-auth.js";
6
- /**
7
- * Context key for the per-session connection registry.
8
- *
9
- * Created as a derived key (no codec) because the registry holds live
10
- * client instances that cannot be serialized across step boundaries.
11
- * The `connectionProvider` reconstructs it each step.
12
- */
13
- export declare const ConnectionRegistryKey: ContextKey<ConnectionRegistry>;
14
- /**
15
- * Durable state tracking which connection tools have been discovered
16
- * via `connection_search`.
17
- *
18
- * Maps connection name → tool metadata. Persists across step
19
- * boundaries so the tool-building code can register discovered tools
20
- * as first-class AI SDK tools on subsequent steps.
21
- */
22
- export interface DiscoveredConnectionToolsState {
23
- readonly byConnection: Readonly<Record<string, readonly ConnectionToolMetadata[]>>;
24
- }
25
- /**
26
- * Durable context key for discovered connection tools.
27
- *
28
- * Written by `connection_search` after fetching tool metadata.
29
- * Read by `resolveDiscoveredConnectionTools` at the start of each
30
- * step to inject discovered tools into the ToolSet.
31
- */
32
- export declare const DiscoveredConnectionToolsKey: ContextKey<DiscoveredConnectionToolsState>;
33
- interface ConnectionSearchInput {
34
- readonly connection?: string;
35
- readonly keywords: string;
36
- readonly limit?: number;
37
- }
38
- interface ConnectionSearchResultItem {
39
- readonly connection: string;
40
- readonly description: string;
41
- readonly error?: string;
42
- readonly inputSchema?: Record<string, unknown>;
43
- readonly needsAuthorization?: boolean;
44
- readonly tool?: string;
45
- readonly qualifiedName?: string;
46
- }
47
- /**
48
- * Framework-provided `connection_search` tool.
49
- */
50
- export declare const CONNECTION_SEARCH_TOOL_DEFINITION: ResolvedToolDefinition;
51
- export declare function executeConnectionSearch(input: ConnectionSearchInput, meta?: {
52
- readonly codeMode?: boolean;
53
- readonly codeModeInterrupt?: CodeModeConnectionAuthContext;
54
- }): Promise<ConnectionSearchResultItem[] | AuthorizationSignal>;
55
- export declare function tokenize(text: string): string[];
56
- export declare function scoreMatch(queryTokens: string[], tool: ConnectionToolMetadata): number;
57
- export {};
@@ -1 +0,0 @@
1
- import{createLogger}from"#internal/logging.js";import{loadContext}from"#context/container.js";import{ContextKey}from"#context/key.js";import{loadCodeModeModule}from"#shared/code-mode.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,isCodeModeToolExecutionOptions,readCodeModeConnectionAuthContext,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{qualifiedConnectionToolName}from"#runtime/framework-tools/connection-tools.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";const logger=createLogger(`framework.connection-search`),ConnectionRegistryKey=new ContextKey(`ash.connectionRegistry`),DiscoveredConnectionToolsKey=new ContextKey(`ash.discoveredConnectionTools`),CONNECTION_SEARCH_TOOL_DEFINITION={description:`Search for tools across your connections. Discovered tools become directly callable by their qualified name (e.g. linear__list_issues) in your next response.`,execute:async(e,t)=>executeConnectionSearch(e,{codeMode:isCodeModeToolExecutionOptions(t),codeModeInterrupt:readCodeModeConnectionAuthContext(t)}),inputSchema:{additionalProperties:!1,properties:{connection:{description:`Optional: limit search to a specific connection name.`,type:`string`},limit:{description:`Max results to return. Default 10.`,type:`number`},keywords:{description:`Search keywords and expanded aliases. Distill intent into keywords; avoid stop words like 'a', 'the', 'in'.`,type:`string`}},required:[`keywords`],type:`object`},logicalPath:`ash:framework/connection-search`,name:`connection_search`,onCompact(){return loadContext().set(DiscoveredConnectionToolsKey,{byConnection:{}}),{}},sourceId:`ash:connection-search-tool`,sourceKind:`module`};async function executeConnectionSearch(e,n){let i=loadContext(),c=i.get(ConnectionRegistryKey);if(c===void 0)return[];await completePendingAuthorizations(c);let l=e.limit??10,u=tokenize(e.keywords),d=[],f=[],p=e.connection!==void 0&&e.connection!==``?c.getConnections().filter(t=>t.connectionName===e.connection):c.getConnections(),m={...i.get(DiscoveredConnectionToolsKey)?.byConnection},h=[];for(let t of p){if(n?.codeModeInterrupt?.resolution.status===`failed`&&n.codeModeInterrupt.payload.connectionName===t.connectionName){let{resolution:e}=n.codeModeInterrupt;f.push({connection:t.connectionName,description:t.description,error:e.reason,needsAuthorization:e.retryable});continue}let i;try{i=await c.getClient(t.connectionName).getToolMetadata()}catch(i){if(isConnectionAuthorizationRequiredError(i)){let i=resolveInteractiveAuth(c,t.connectionName);if(i&&n?.codeMode===!0){let{requestCodeModeInterrupt:n}=await loadCodeModeModule();n({args:toCodeModeConnectionAuthArgs(e),connectionName:t.connectionName,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:`connection_search`})}if(i){let e=getHookUrl(t.connectionName);if(e){let n=resolveConnectionPrincipal(t.connectionName,i);try{let{challenge:r,state:a}=await i.startAuthorization({callbackUrl:e,connection:{url:t.url??``},principal:n});h.push({name:t.connectionName,challenge:r,hookUrl:e,state:a})}catch(e){logger.warn(`startAuthorization failed`,{connection:t.connectionName,error:e instanceof Error?e:Error(String(e))})}}}f.push({connection:t.connectionName,description:t.description,needsAuthorization:!0});continue}if(isConnectionAuthorizationFailedError(i)){logger.warn(`connection authorization failed`,{connection:t.connectionName,reason:i.reason,retryable:i.retryable,error:i}),f.push({connection:t.connectionName,description:t.description,error:`Authorization failed for ${t.connectionName}: ${i.message}`});continue}let o=i instanceof Error?i.message:`unknown error`;logger.warn(`failed to load connection tools`,{connection:t.connectionName,error:i instanceof Error?i:Error(o)}),f.push({connection:t.connectionName,description:t.description,error:`Failed to load tools for "${t.connectionName}": ${o}`});continue}m[t.connectionName]=i;for(let e of i){let n=scoreMatch(u,e);n>0&&d.push({item:{connection:t.connectionName,description:e.description,inputSchema:e.inputSchema,qualifiedName:qualifiedConnectionToolName(t.connectionName,e.name),tool:e.name},score:n})}}if(i.set(DiscoveredConnectionToolsKey,{byConnection:m}),h.length>0)return requestAuthorization(h);d.sort((e,t)=>t.score-e.score);let g=d.slice(0,l).map(e=>e.item);return g.length>0?[...g,...f]:p.map(e=>f.find(t=>t.connection===e.connectionName)||{connection:e.connectionName,description:e.description})}async function completePendingAuthorizations(e){let n=loadContext();for(let t of e.getConnections()){let r=getAuthorizationResult(t.connectionName);if(!r)continue;let a=resolveInteractiveAuth(e,t.connectionName);if(!a)continue;let o=resolveConnectionPrincipal(t.connectionName,a),s=await a.completeAuthorization({callbackUrl:r.hookUrl,connection:{url:t.url??``},principal:o,request:r.callback,state:r.state});writeCachedToken(n,t.connectionName,principalKey(o),s)}}function tokenize(e){return e.toLowerCase().split(/[\s_\-./]+/).filter(e=>e.length>1)}function scoreMatch(e,t){let n=tokenize(t.name),r=tokenize(t.description),i=0;for(let t of e){for(let e of n)(e.includes(t)||t.includes(e))&&(i+=3);for(let e of r)(e.includes(t)||t.includes(e))&&(i+=1)}return i}function resolveInteractiveAuth(e,t){let n=e.getConnections().find(e=>e.connectionName===t);if(n?.authorization&&supportsInteractiveAuthorization(n.authorization))return n.authorization}export{CONNECTION_SEARCH_TOOL_DEFINITION,ConnectionRegistryKey,DiscoveredConnectionToolsKey,executeConnectionSearch,scoreMatch,tokenize};
@@ -1,55 +0,0 @@
1
- import { type ToolSet } from "ai";
2
- import { type ConnectionRegistry } from "#runtime/connections/types.js";
3
- import type { ResolvedConnectionDefinition } from "#runtime/types.js";
4
- import { type DiscoveredConnectionToolsState } from "#runtime/framework-tools/connection-search.js";
5
- /**
6
- * Builds the qualified tool name for a connection tool.
7
- *
8
- * Uses double-underscore as the separator because it is safe in
9
- * tool-name schemas and unlikely to appear in authored tool names.
10
- */
11
- export declare function qualifiedConnectionToolName(connectionName: string, toolName: string): string;
12
- /**
13
- * Builds AI SDK tools for every connection tool that has been
14
- * discovered via a prior `connection_search` call.
15
- */
16
- export declare function resolveConnectionToolsFromState(registry: ConnectionRegistry, discovered: DiscoveredConnectionToolsState, input: {
17
- readonly approvedTools?: ReadonlySet<string>;
18
- readonly authMode?: "code-mode" | "direct";
19
- readonly codeModeMetadataOnlyConnectionNames?: ReadonlySet<string>;
20
- readonly existingToolNames?: ReadonlySet<string>;
21
- }): Promise<ToolSet>;
22
- /**
23
- * AI SDK tool-execute function shape. Captured locally so we can wrap
24
- * the `execute` returned by `@ai-sdk/mcp`'s `toolsFromDefinitions()`
25
- * without taking a direct dependency on the SDK's internal types.
26
- */
27
- type ToolExecuteFn = (args: unknown, options: unknown) => Promise<unknown>;
28
- /**
29
- * Wraps one connection tool's `execute` for nested code-mode execution.
30
- *
31
- * Interactive auth pauses the outer `code_mode` tool with a generic
32
- * interruption instead of writing a top-level placeholder result. The
33
- * durable workflow completes OAuth, then code mode replays the same
34
- * sandbox program and re-enters this nested host tool with
35
- * `options.codeModeInterrupt` populated.
36
- */
37
- export declare function wrapCodeModeConnectionToolExecute(params: {
38
- readonly connectionDefinition: ResolvedConnectionDefinition | undefined;
39
- readonly connectionName: string;
40
- readonly originalExecute: ToolExecuteFn | undefined;
41
- readonly toolName: string;
42
- }): ToolExecuteFn | undefined;
43
- /**
44
- * Wraps one connection tool's `execute` to handle interactive auth.
45
- * On auth error, starts authorization and returns an
46
- * `AuthorizationSignal`. On re-execution after callback, completes
47
- * auth, caches the token, and falls through to normal execute.
48
- */
49
- export declare function wrapConnectionToolExecute(params: {
50
- readonly connectionDefinition: ResolvedConnectionDefinition | undefined;
51
- readonly connectionName: string;
52
- readonly originalExecute: ToolExecuteFn | undefined;
53
- readonly toolName: string;
54
- }): ToolExecuteFn | undefined;
55
- export {};
@@ -1 +0,0 @@
1
- import{isObject}from"#shared/guards.js";import{loadContext}from"#context/container.js";import{jsonSchema}from"ai";import"#runtime/framework-tools/connection-search.js";import{loadCodeModeModule}from"#shared/code-mode.js";import{getAuthorizationResult,getHookUrl,requestAuthorization}from"#harness/authorization.js";import{CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toCodeModeConnectionAuthArgs}from"#runtime/framework-tools/code-mode-connection-auth.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";function qualifiedConnectionToolName(e,t){return`${e}__${t}`}async function resolveConnectionToolsFromState(t,n,r){let i={};for(let[a,o]of Object.entries(n.byConnection)){let n=r.authMode===`code-mode`&&r.codeModeMetadataOnlyConnectionNames?.has(a)===!0,s=n?void 0:await t.getClient(a).getTools(),c=t.getConnectionApproval(a);for(let l of o){let o=qualifiedConnectionToolName(a,l.name);if(r.existingToolNames?.has(o))continue;let u=s?.[l.name]??(n?createMetadataOnlyTool(l,o):void 0);if(u===void 0)continue;let d=!n&&c!==void 0?async t=>c({approvedTools:r.approvedTools??new Set,toolInput:isObject(t)?t:void 0,toolName:o}):void 0,f=t.getConnections().find(e=>e.connectionName===a),p=u.execute,m=r.authMode===`code-mode`?wrapCodeModeConnectionToolExecute({connectionDefinition:f,connectionName:a,originalExecute:p,toolName:l.name}):wrapConnectionToolExecute({connectionDefinition:f,connectionName:a,originalExecute:p,toolName:l.name});i[o]={...u,execute:m,needsApproval:d}}}return i}function createMetadataOnlyTool(e,t){return{description:e.description,execute:async()=>{throw Error(`Connection tool "${t}" cannot execute without authorization.`)},inputSchema:jsonSchema(e.inputSchema)}}function wrapCodeModeConnectionToolExecute(e){let{connectionDefinition:n,connectionName:a,originalExecute:o,toolName:m}=e;if(o===void 0)return;let h=n?.authorization&&supportsInteractiveAuthorization(n.authorization)?n.authorization:void 0,g=n?.url??``;return async(e,n)=>{if(h){let e=getAuthorizationResult(a);if(e){let n=loadContext(),r=resolveConnectionPrincipal(a,h),i=await h.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:g},principal:r,request:e.callback,state:e.state});writeCachedToken(n,a,principalKey(r),i)}}try{return await o(e,n)}catch(t){if(!isConnectionAuthorizationRequiredError(t)||!h)throw t;let{requestCodeModeInterrupt:n}=await loadCodeModeModule();n({args:toCodeModeConnectionAuthArgs(e),connectionName:a,kind:CODE_MODE_CONNECTION_AUTH_INTERRUPT_KIND,toolName:m})}}}function wrapConnectionToolExecute(e){let{connectionDefinition:n,connectionName:r,originalExecute:s}=e;if(s===void 0)return;let c=n?.authorization&&supportsInteractiveAuthorization(n.authorization)?n.authorization:void 0,m=n?.url??``;return async(e,n)=>{if(c){let e=getAuthorizationResult(r);if(e){let n=loadContext(),i=resolveConnectionPrincipal(r,c),a=await c.completeAuthorization({callbackUrl:e.hookUrl,connection:{url:m},principal:i,request:e.callback,state:e.state});writeCachedToken(n,r,principalKey(i),a)}}try{return await s(e,n)}catch(e){if(!isConnectionAuthorizationRequiredError(e)||!c)throw e;let t=getHookUrl(r);if(!t)throw e;let n=resolveConnectionPrincipal(r,c),{challenge:i,state:s}=await c.startAuthorization({callbackUrl:t,connection:{url:m},principal:n});return requestAuthorization([{name:r,challenge:i,hookUrl:t,state:s}])}}}export{qualifiedConnectionToolName,resolveConnectionToolsFromState,wrapCodeModeConnectionToolExecute,wrapConnectionToolExecute};