experimental-ash 0.41.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # experimental-ash
2
2
 
3
+ ## 0.42.0
4
+
5
+ ### Minor Changes
6
+
7
+ - edce3c9: Unified event dispatch: single `handleEvent` pipeline for all event-based handlers (channels, hooks, dynamic tools). Dynamic tool entries now require the `tool()` wrapper — the bundler transform only matches `execute` inside `tool()` calls.
8
+
3
9
  ## 0.41.0
4
10
 
5
11
  ### Minor Changes
@@ -124,13 +124,35 @@ itself throws while the dispatcher is emitting the recoverable
124
124
  `session.failed` instead. This is the bounded second-order behavior
125
125
  when both a `lifecycle.turn` and a `turn.failed` event hook fail.
126
126
 
127
- Stream-event dispatch is `dispatchStreamEventHooks`, called from each
128
- runtime's `HarnessEmitFn` composer **after** the channel adapter's
129
- event handler runs. The dispatcher reads the typed bucket
127
+ ### Stream event dispatch (`handleEvent`)
128
+
129
+ Every stream event passes through one `handleEvent` function
130
+ (`execution/workflow-steps.ts`) that coordinates three stages:
131
+
132
+ ```
133
+ handleEvent(event):
134
+ 1. emit(event) — channel adapter handler + durable stream write
135
+ 2. dispatchStreamEventHooks — typed bucket, then wildcard bucket
136
+ 3. dispatchDynamicToolEvent — resolvers subscribed to event.type
137
+ ```
138
+
139
+ `emit` handles the channel adapter's event handler (can mutate
140
+ adapter state, can transform the event) and writes the (possibly
141
+ transformed) event to the durable stream. The durable record is
142
+ consistent with what was written even if a downstream handler throws.
143
+
144
+ `dispatchStreamEventHooks` reads the typed bucket
130
145
  (`registry.streamEventsByType.get(eventType)`) followed by the flat
131
- wildcard bucket (`registry.streamEventsWildcard`); thrown errors
132
- propagate through the emit composer. The existing harness error path
133
- catches them and emits the recoverable `turn.failed` cascade.
146
+ wildcard bucket (`registry.streamEventsWildcard`). Thrown errors
147
+ propagate through the handler; the existing harness error path catches
148
+ them and emits the recoverable `turn.failed` cascade.
149
+
150
+ `dispatchDynamicToolEvent` (`context/dynamic-tool-lifecycle.ts`) runs
151
+ dynamic tool resolvers subscribed to the event type and merges
152
+ resolved tools into `DynamicToolsKey`. The tool-loop reads this key
153
+ right before each model call. See the [event dispatch
154
+ doc](../../research/active/unified-event-dispatch.md) for the full
155
+ design.
134
156
 
135
157
  ## `SessionPreparedKey`
136
158
 
@@ -162,11 +184,31 @@ The `discover/discover-subagent.ts` path walks each local subagent's
162
184
  | `lifecycle.turn` | Caught by dispatcher → recoverable `turn.failed` cascade emitted |
163
185
  | Stream-event hooks | Propagated through the emit composer → existing harness error path emits `turn.failed` |
164
186
 
165
- The contract with `HarnessEmitFn`: the wrapped emit fn calls the
166
- channel adapter's event handler first, writes the (possibly
167
- transformed) event to the durable stream, **then** fans out to authored
168
- stream-event hook subscribers. The durable record is consistent with
169
- what was written even if a downstream hook throws.
187
+ ### Full per-turn execution order
188
+
189
+ ```
190
+ dispatchHookLifecycle():
191
+ lifecycle.session hooks (once per session, produce modelContext)
192
+ lifecycle.turn hooks (once per turn, produce modelContext)
193
+
194
+ emitTurnPreamble() via handleEvent:
195
+ handleEvent(session.started) → emit, hooks, dynamic tool resolvers
196
+ handleEvent(turn.started) → emit, hooks, dynamic tool resolvers
197
+ handleEvent(message.received) → emit, hooks
198
+
199
+ emitStepStarted() via handleEvent:
200
+ handleEvent(step.started) → emit, hooks, dynamic tool resolvers
201
+
202
+ runOneModelCall():
203
+ read DynamicToolsKey (current tools from all prior events)
204
+ build effective toolset (static + dynamic + connections)
205
+ model.stream()
206
+
207
+ emitStepActions() via handleEvent:
208
+ handleEvent(actions.requested) → emit, hooks, dynamic tool resolvers
209
+ handleEvent(action.result) × N → emit, hooks, dynamic tool resolvers
210
+ handleEvent(step.completed) → emit, hooks
211
+ ```
170
212
 
171
213
  ## Testing Strategy
172
214
 
@@ -229,7 +229,7 @@ export default defineHook({
229
229
  The session contribution lands in `modelContext` for the **first** model
230
230
  call only. Subsequent turns proceed without it.
231
231
 
232
- ## Stream events
232
+ ## Stream Events
233
233
 
234
234
  Side-effect-only handlers for accepted runtime events. Subscribe by
235
235
  event type, or use `*` for every event:
@@ -255,12 +255,15 @@ export default defineHook({
255
255
  });
256
256
  ```
257
257
 
258
- Composition:
258
+ ### Execution order
259
259
 
260
- - Stream-event hooks run **after** Ash has accepted the event and
261
- written it to the durable stream.
262
- - Return values are ignored.
263
- - Typed event hooks run **before** `*` for the same event.
260
+ When a stream event fires, three things happen in order:
261
+
262
+ 1. **Emit** the channel adapter handler runs, then the event is written to the durable stream.
263
+ 2. **Hooks** — stream event hooks fire (typed handlers first, then `*` wildcard). Return values are ignored.
264
+ 3. **Dynamic tool resolvers** — resolvers subscribed to the event type run and update the tool set.
265
+
266
+ Hooks always run **after** the event is durably recorded — if a hook throws, the stream is consistent.
264
267
 
265
268
  ## Errors
266
269
 
@@ -377,7 +377,7 @@ Dynamic tools resolve at runtime based on session context — tenant configurati
377
377
  `agent/tools/analytics.ts`
378
378
 
379
379
  ```ts
380
- import { defineTool } from "experimental-ash/tools";
380
+ import { defineTool, tool } from "experimental-ash/tools";
381
381
  import { z } from "zod";
382
382
 
383
383
  export default defineTool({
@@ -386,19 +386,19 @@ export default defineTool({
386
386
  const flags = await fetchFeatureFlags(ctx.session.id);
387
387
  if (!flags.advancedAnalytics) return null;
388
388
 
389
- return {
389
+ return tool({
390
390
  description: "Run an advanced analytics query.",
391
391
  inputSchema: z.object({ query: z.string() }),
392
392
  async execute(input) {
393
393
  return runAnalytics(input.query);
394
394
  },
395
- };
395
+ });
396
396
  },
397
397
  },
398
398
  });
399
399
  ```
400
400
 
401
- Return `null` to produce no tool. The tool name is the file slug (`analytics`), identical to a static `defineTool`.
401
+ Return `null` to produce no tool. The tool name is the file slug (`analytics`), identical to a static `defineTool`. Entries must be wrapped with `tool()` — this stamps the entry for the bundler transform so `execute` functions survive workflow step boundaries.
402
402
 
403
403
  ### Multiple Dynamic Tools
404
404
 
@@ -451,17 +451,27 @@ Dynamic tool names are derived from the file path and the definition function:
451
451
 
452
452
  `defineTool` always produces one tool named after the file slug — identical to static tools. `defineTools` always uses `slug__key`, even when the resolver returns a single entry. This keeps names stable: adding a second entry to a `defineTools` file does not rename the first.
453
453
 
454
- ### Event Scopes
454
+ ### Events
455
+
456
+ The `events` object supports three event types:
457
+
458
+ | Event | When the resolver runs | Tools available for |
459
+ | ----------------- | ---------------------- | ------------------------------- |
460
+ | `session.started` | Once per session | Every model call in the session |
461
+ | `turn.started` | Once per turn | Every model call in the turn |
462
+ | `step.started` | Before each model call | That model call |
463
+
464
+ The bundler transform ensures resolvers survive workflow step boundaries — subsequent steps reconstruct execute functions from stored closure variables without re-running the resolver. Resolvers run concurrently — if multiple dynamic tool files declare the same event, their I/O overlaps.
465
+
466
+ ### Execution Order
455
467
 
456
- The `events` object uses the same event names as hook stream events:
468
+ When a stream event fires, three things happen in order:
457
469
 
458
- | Event | When the resolver runs | I/O behavior |
459
- | ----------------- | ----------------------- | ----------------------------------- |
460
- | `session.started` | Once per session | Runs once; results cached durably |
461
- | `turn.started` | Once per turn | Runs once per turn; cached for turn |
462
- | `step.started` | Before every model call | Runs every step; no caching |
470
+ 1. **Emit** the channel adapter handler runs (can transform the event), then the event is written to the durable stream.
471
+ 2. **Hooks** stream event hooks fire (typed handlers first, then `*` wildcard).
472
+ 3. **Dynamic tool resolvers** resolvers subscribed to the event type run and update the tool set.
463
473
 
464
- Session-scoped resolvers can do I/O (fetch from a database, call an API). The framework's bundler transform ensures the I/O executes once per session subsequent workflow steps reconstruct execute functions from stored closure variables without re-running the resolver. Resolvers run concurrently — if multiple dynamic tool files declare the same event, their I/O overlaps.
474
+ The tool-loop reads the current tool set right before each model call. If an event updated tools since the last read, the model sees the updated set.
465
475
 
466
476
  ### Multiple Events
467
477
 
@@ -476,7 +486,8 @@ export default defineTools({
476
486
  return { query: tool({ description: "Query", inputSchema: {}, execute: async () => {} }) };
477
487
  },
478
488
  "turn.started": async (event, ctx) => {
479
- // Turn handler replaces session handler's tools for this file
489
+ // On each turn, re-resolve tools. Replaces this file's
490
+ // session.started tools for subsequent calls.
480
491
  return { search: tool({ description: "Search", inputSchema: {}, execute: async () => {} }) };
481
492
  },
482
493
  },
@@ -1,24 +1,8 @@
1
1
  import type { HarnessToolDefinition } from "#harness/execute-tool.js";
2
+ import type { HandleMessageStreamEvent } from "#protocol/message.js";
2
3
  import type { ResolvedDynamicToolResolver } from "#runtime/types.js";
3
- import type { AlsContext } from "#context/container.js";
4
+ import type { ContextContainer } from "#context/container.js";
4
5
  import type { DurableDynamicToolMetadata } from "#context/keys.js";
5
- type ReadableContext = Pick<AlsContext, "get">;
6
- interface WritableContext extends ReadableContext {
7
- set<T>(key: import("#context/key.js").ContextKey<T>, value: T): T;
8
- }
9
- /**
10
- * Calls session/turn-scoped resolvers and produces both live tool
11
- * definitions (for the current step) and durable metadata (for
12
- * replay on subsequent steps).
13
- *
14
- * Resolvers run concurrently via `Promise.allSettled` — each
15
- * resolver's I/O (database calls, API fetches) overlaps with the
16
- * others. A single resolver throwing does not block the rest.
17
- */
18
- export declare function resolveAndStoreDynamicSessionTools(ctx: ReadableContext & WritableContext, resolvers: readonly ResolvedDynamicToolResolver[], eventName?: string): Promise<{
19
- readonly tools: readonly HarnessToolDefinition[];
20
- readonly metadata: readonly DurableDynamicToolMetadata[];
21
- }>;
22
6
  /**
23
7
  * Reconstructs tool definitions from durable metadata using
24
8
  * registered step functions. No resolver re-invocation — the
@@ -27,9 +11,19 @@ export declare function resolveAndStoreDynamicSessionTools(ctx: ReadableContext
27
11
  */
28
12
  export declare function replayDynamicSessionTools(metadata: readonly DurableDynamicToolMetadata[], _resolvers: readonly ResolvedDynamicToolResolver[]): readonly HarnessToolDefinition[];
29
13
  /**
30
- * Resolves step-scoped dynamic tools. Called before every model call.
31
- * No caching — each step gets a fresh resolution. Resolvers run
32
- * concurrently.
14
+ * Unified dynamic tool event dispatch. Called by `handleEvent` for
15
+ * every stream event. Two phases:
16
+ *
17
+ * 1. **Build phase**: reconstruct tools from durable metadata when
18
+ * the virtual key is empty (workflow step boundary crossed).
19
+ * 2. **Resolve phase**: run resolver handlers for the current event,
20
+ * capture closures as durable metadata, produce live tools.
21
+ *
22
+ * The tool-loop reads {@link DynamicToolsKey} right before each model
23
+ * call. Events update it; the tool-loop picks up whatever is current.
33
24
  */
34
- export declare function resolveDynamicStepTools(ctx: ReadableContext, resolvers: readonly ResolvedDynamicToolResolver[]): Promise<readonly HarnessToolDefinition[]>;
35
- export {};
25
+ export declare function dispatchDynamicToolEvent(input: {
26
+ readonly ctx: ContextContainer;
27
+ readonly resolvers: readonly ResolvedDynamicToolResolver[];
28
+ readonly event: HandleMessageStreamEvent;
29
+ }): Promise<void>;
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{AuthKey,ContinuationTokenKey,DynamicToolResolverCacheKey,InitiatorAuthKey,SessionIdKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{jsonSchema,zodSchema}from"ai";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{getAdapterKind}from"#channel/adapter.js";const log=createLogger(`dynamic-tools`);function buildResolveContext(e,o,s){let c=e.get(SessionIdKey)??``,l=e.get(AuthKey)??null,u=e.get(InitiatorAuthKey)??null,d=e.get(ChannelKey),f=e.get(ContinuationTokenKey);return{session:{id:c,auth:{current:l,initiator:u}},channel:{kind:d===void 0?void 0:getAdapterKind(d),continuationToken:f},async __cache(t,n){let i=`${o}:${t}`,a=e.get(DynamicToolResolverCacheKey)??{};if(i in a)return a[i];let c=await n();if(s){let e={...a,[i]:c};s.set(DynamicToolResolverCacheKey,e)}return c}}}function toHarnessToolDefinition(e,t){return{description:t.description,execute:e=>t.execute(e,buildCallbackContext()),inputSchema:convertInputSchema(t.inputSchema),name:e}}function convertInputSchema(e){return typeof e==`object`&&e&&`~standard`in e?zodSchema(e):jsonSchema(e)}function qualifyDynamicToolNames(e,t,n){let r=Object.keys(n),i=[];if(r.length===0)return i;if(t===`single`)return i.push({name:e,entryKey:r[0],entry:n[r[0]]}),i;for(let t of r)i.push({name:`${e}__${t}`,entryKey:t,entry:n[t]});return i}async function resolveAndStoreDynamicSessionTools(e,t,n=`session.started`){let r=t.filter(e=>e.eventNames.includes(n));if(r.length===0)return{tools:[],metadata:[]};let i={type:n,data:{}},a=await Promise.allSettled(r.map(async t=>{let r=t.events[n];if(r===void 0)return null;let a=await r(i,buildResolveContext(e,t.slug,e));return a==null?null:{resolver:t,result:a}})),s=[],c=[];for(let e of a){if(e.status===`rejected`){log.error(`Dynamic tool resolver (${n}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}if(e.value===null)continue;let{resolver:t,result:r}=e.value,i=qualifyDynamicToolNames(t.slug,t.identity,r);for(let{name:e,entryKey:n,entry:r}of i){s.push(toHarnessToolDefinition(e,r));let i=`__executeStepFn`in r?r.__executeStepFn:void 0,a=`__closureVars`in r?r.__closureVars:void 0;c.push({name:e,description:r.description,inputSchema:normalizeJsonSchemaDefinition(r.inputSchema),resolverSlug:t.slug,entryKey:n,executeStepFnName:i?.stepId,closureVars:a===void 0?void 0:safeSerialize(a)})}}return{tools:s,metadata:c}}function replayDynamicSessionTools(e,t){let n=[];for(let t of e){if(!t.executeStepFnName||!t.closureVars){log.warn(`Dynamic tool "${t.name}" has no registered step function — skipping on this step. The bundler transform may not have processed this tool file.`);continue}let e=lookupStepFunction(t.executeStepFnName);if(!e){log.warn(`Dynamic tool "${t.name}" references step function "${t.executeStepFnName}" which is not registered — skipping on this step.`);continue}n.push({description:t.description,execute:n=>e(t.closureVars,n,buildCallbackContext()),inputSchema:jsonSchema(t.inputSchema),name:t.name})}return n}function lookupStepFunction(e){try{let t=globalThis[Symbol.for(`@workflow/core//registeredSteps`)];return t===void 0?null:t.get(e)||null}catch{return null}}function safeSerialize(e){try{return JSON.parse(JSON.stringify(e))}catch{return{}}}async function resolveDynamicStepTools(e,t){let n=t.filter(e=>e.eventNames.includes(`step.started`));if(n.length===0)return[];let r={type:`step.started`,data:{}},i=await Promise.allSettled(n.map(async t=>{let n=t.events[`step.started`];if(n===void 0)return null;let i=await n(r,buildResolveContext(e,t.slug));return i==null?null:{resolver:t,result:i}})),a=[];for(let e of i){if(e.status===`rejected`){log.error(`Dynamic tool resolver (step.started) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}if(e.value===null)continue;let{resolver:t,result:n}=e.value,r=qualifyDynamicToolNames(t.slug,t.identity,n);for(let{name:e,entry:t}of r)a.push(toHarnessToolDefinition(e,t))}return a}export{replayDynamicSessionTools,resolveAndStoreDynamicSessionTools,resolveDynamicStepTools};
1
+ import{createLogger}from"#internal/logging.js";import{AuthKey,ContinuationTokenKey,DynamicSessionToolMetadataKey,DynamicToolResolverCacheKey,DynamicToolsKey,InitiatorAuthKey,SessionIdKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{jsonSchema,zodSchema}from"ai";import{ALLOWED_DYNAMIC_TOOL_EVENTS}from"#shared/dynamic-tool-definition.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{getAdapterKind}from"#channel/adapter.js";const log=createLogger(`dynamic-tools`);function buildResolveContext(e,r,a){let c=e.get(SessionIdKey)??``,l=e.get(AuthKey)??null,u=e.get(InitiatorAuthKey)??null,d=e.get(ChannelKey),f=e.get(ContinuationTokenKey);return{session:{id:c,auth:{current:l,initiator:u}},channel:{kind:d===void 0?void 0:getAdapterKind(d),continuationToken:f},async __cache(t,n){let o=`${r}:${t}`,s=e.get(DynamicToolResolverCacheKey)??{};if(o in s)return s[o];let c=await n();if(a){let e={...s,[o]:c};a.set(DynamicToolResolverCacheKey,e)}return c}}}function toHarnessToolDefinition(e,t){return{description:t.description,execute:e=>t.execute(e,buildCallbackContext()),inputSchema:convertInputSchema(t.inputSchema),name:e}}function convertInputSchema(e){return typeof e==`object`&&e&&`~standard`in e?zodSchema(e):jsonSchema(e)}function qualifyDynamicToolNames(e,t,n){let r=Object.keys(n),i=[];if(r.length===0)return i;if(t===`single`)return i.push({name:e,entryKey:r[0],entry:n[r[0]]}),i;for(let t of r)i.push({name:`${e}__${t}`,entryKey:t,entry:n[t]});return i}function replayDynamicSessionTools(e,t){let n=[];for(let t of e){if(!t.executeStepFnName||!t.closureVars){log.warn(`Dynamic tool "${t.name}" has no registered step function — skipping on this step. The bundler transform may not have processed this tool file.`);continue}let e=lookupStepFunction(t.executeStepFnName);if(!e){log.warn(`Dynamic tool "${t.name}" references step function "${t.executeStepFnName}" which is not registered — skipping on this step.`);continue}n.push({description:t.description,execute:n=>e(t.closureVars,n,buildCallbackContext()),inputSchema:jsonSchema(t.inputSchema),name:t.name})}return n}function lookupStepFunction(e){try{let t=globalThis[Symbol.for(`@workflow/core//registeredSteps`)];return t===void 0?null:t.get(e)||null}catch{return null}}function safeSerialize(e){try{return JSON.parse(JSON.stringify(e))}catch{return{}}}function matchesAnySlug(e,t){for(let n of t)if(e===n||e.startsWith(`${n}__`))return!0;return!1}async function resolveToolsFromEvent(e,t,n){let i=await Promise.allSettled(t.map(async t=>{let r=t.events[n.type];if(r===void 0)return null;let i=await r(n,buildResolveContext(e,t.slug,e));return i==null?null:{resolver:t,result:i}})),a=[],o=[];for(let e of i){if(e.status===`rejected`){log.error(`Dynamic tool resolver (${n.type}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}if(e.value===null)continue;let{resolver:t,result:r}=e.value,i=qualifyDynamicToolNames(t.slug,t.identity,r);for(let{name:e,entryKey:n,entry:r}of i){a.push(toHarnessToolDefinition(e,r));let i=`__executeStepFn`in r?r.__executeStepFn:void 0,s=`__closureVars`in r?r.__closureVars:void 0;o.push({name:e,description:r.description,inputSchema:normalizeJsonSchemaDefinition(r.inputSchema),resolverSlug:t.slug,entryKey:n,executeStepFnName:i?.stepId,closureVars:s===void 0?void 0:safeSerialize(s)})}}if(o.length>0){let t=e.get(DynamicSessionToolMetadataKey)??[],n=new Set(o.map(e=>e.resolverSlug)),i=t.filter(e=>!n.has(e.resolverSlug));e.set(DynamicSessionToolMetadataKey,[...i,...o])}return a}async function dispatchDynamicToolEvent(e){let{ctx:t,resolvers:n,event:i}=e;if(t.get(DynamicToolsKey)===void 0){let e=t.get(DynamicSessionToolMetadataKey);if(e!==void 0&&e.length>0){let r=replayDynamicSessionTools(e,n);r.length>0&&t.setVirtualContext(DynamicToolsKey,[...r])}}if(!ALLOWED_DYNAMIC_TOOL_EVENTS.has(i.type))return;let o=n.filter(e=>e.eventNames.includes(i.type));if(o.length===0)return;let s=await resolveToolsFromEvent(t,o,i);if(s.length===0)return;let c=new Set(o.map(e=>e.slug)),l=(t.get(DynamicToolsKey)??[]).filter(e=>!matchesAnySlug(e.name,c));t.setVirtualContext(DynamicToolsKey,[...l,...s])}export{dispatchDynamicToolEvent,replayDynamicSessionTools};
@@ -1 +1 @@
1
- import{ContinuationTokenKey,DynamicSessionToolMetadataKey,DynamicSessionToolsKey,SandboxKey,SessionPreparedKey}from"./keys.js";import{createLogger}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeSkillPackage,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{getAdapterKind}from"#channel/adapter.js";import{emitRecoverableFailedTurn,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";import{replayDynamicSessionTools,resolveAndStoreDynamicSessionTools}from"#context/dynamic-tool-lifecycle.js";const log=createLogger(`hooks.lifecycle`);async function dispatchHookLifecycle(e){let{ctx:r,registry:a,emit:s}=e,c=getHarnessEmissionState(e.session.state),l=buildHookContext(r),u=[],d=e.session;if(a.session.length>0&&r.get(SessionPreparedKey)!==!0){r.set(SessionPreparedKey,!0);let e=[];for(let t of a.session){let n=await t.handler(l);n?.modelContext!==void 0&&n.modelContext.length>0&&u.push(...n.modelContext),e.push(...normalizeLifecycleSkillResults(r,n))}d=await materializeLifecycleSkills(r,d,e)}let f=r.require(BundleKey).resolvedAgent.dynamicToolResolvers??[];if(f.some(e=>e.eventNames.includes(`session.started`))){let e=r.get(DynamicSessionToolMetadataKey);if(e===void 0){let{tools:e,metadata:i}=await resolveAndStoreDynamicSessionTools(r,f);i.length>0&&r.set(DynamicSessionToolMetadataKey,i),e.length>0&&r.setVirtualContext(DynamicSessionToolsKey,e)}else if(e.length>0){let t=replayDynamicSessionTools(e,f);r.setVirtualContext(DynamicSessionToolsKey,[...t])}}let p=!1,m=c;try{let e=[];for(let t of a.turn){let n=await t.handler(l);n?.modelContext!==void 0&&n.modelContext.length>0&&u.push(...n.modelContext),e.push(...normalizeLifecycleSkillResults(r,n))}d=await materializeLifecycleSkills(r,d,e)}catch(t){let n=toErrorMessage(t);try{p||=(m=await emitTurnPreamble(s,{message:e.input.message},c,e.runtimeIdentity),!0);let t=await emitRecoverableFailedTurn(s,m,{code:`HOOK_TURN_FAILED`,message:n});return{kind:`turn-failed`,message:n,nextSession:setHarnessEmissionState(d,t)}}catch(e){throw log.error(`Event hook threw while emitting the turn.failed cascade for a lifecycle.turn failure — escalating to session.failed.`,{error:toErrorMessage(e)}),e}}if(f.some(e=>e.eventNames.includes(`turn.started`))){let{tools:e}=await resolveAndStoreDynamicSessionTools(r,f,`turn.started`),t=new Set(f.filter(e=>e.eventNames.includes(`turn.started`)).map(e=>e.slug)),i=(r.get(DynamicSessionToolsKey)??[]).filter(e=>{for(let n of t)if(e.name===n||e.name.startsWith(`${n}__`))return!1;return!0});r.setVirtualContext(DynamicSessionToolsKey,[...i,...e])}let h=e.input.modelContext??[],g=h.length===0?u:[...h,...u];return{kind:`proceed`,input:g.length>0?{...e.input,modelContext:g}:e.input,nextSession:d}}function normalizeLifecycleSkillResults(e,t){if(t?.skills===void 0||t.skills.length===0)return[];let n=new Set(e.require(BundleKey).resolvedAgent.skills.map(e=>e.name)),r=new Map;for(let e of t.skills){let t=normalizeSkillPackage(e);if(n.has(t.name))throw Error(`Hook-contributed skill "${t.name}" conflicts with an authored skill.`);r.set(t.name,t)}return[...r.values()]}async function materializeLifecycleSkills(e,t,n){if(n.length===0)return t;let i=new Map(n.map(e=>[e.name,e])),a=await e.require(SandboxKey).get();if(a===null)throw Error(`Dynamic skills require a sandbox for the current agent.`);for(let e of i.values())await writeSkillPackageToSandbox({sandbox:a,skill:e});let o=formatAvailableSkillsSection([...i.values()]);return o===null?t:{...t,history:[...t.history,{role:`system`,content:o}]}}async function dispatchStreamEventHooks(e){let t=e.registry.streamEventsByType.get(e.event.type)??[],n=e.registry.streamEventsWildcard;if(t.length===0&&n.length===0)return;let r=buildHookContext(e.ctx);for(let n of t)await n.handler(e.event,r);for(let t of n)await t.handler(e.event,r)}function buildHookContext(t){let n=t.require(BundleKey),r=t.get(ChannelKey),i=t.get(ContinuationTokenKey),a=r===void 0?void 0:getAdapterKind(r);return{...buildCallbackContext(),agent:{name:n.resolvedAgent.config.name??`agent`,nodeId:n.nodeId},channel:{kind:a,continuationToken:i}}}async function runHookLifecycleStep(e,t){let n=await dispatchHookLifecycle(e);return n.kind===`turn-failed`?{next:e.mode===`conversation`?null:{done:!0,output:n.message},session:n.nextSession}:t(n.nextSession,n.input)}export{dispatchHookLifecycle,dispatchStreamEventHooks,runHookLifecycleStep};
1
+ import{ContinuationTokenKey,SandboxKey,SessionPreparedKey}from"./keys.js";import{createLogger}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeSkillPackage,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{getAdapterKind}from"#channel/adapter.js";import{emitRecoverableFailedTurn,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";const log=createLogger(`hooks.lifecycle`);async function dispatchHookLifecycle(e){let{ctx:t,registry:r,emit:a}=e,o=getHarnessEmissionState(e.session.state),s=buildHookContext(t),c=[],l=e.session;if(r.session.length>0&&t.get(SessionPreparedKey)!==!0){t.set(SessionPreparedKey,!0);let e=[];for(let n of r.session){let r=await n.handler(s);r?.modelContext!==void 0&&r.modelContext.length>0&&c.push(...r.modelContext),e.push(...normalizeLifecycleSkillResults(t,r))}l=await materializeLifecycleSkills(t,l,e)}let u=!1,d=o;try{let e=[];for(let n of r.turn){let r=await n.handler(s);r?.modelContext!==void 0&&r.modelContext.length>0&&c.push(...r.modelContext),e.push(...normalizeLifecycleSkillResults(t,r))}l=await materializeLifecycleSkills(t,l,e)}catch(t){let n=toErrorMessage(t);try{u||=(d=await emitTurnPreamble(a,{message:e.input.message},o,e.runtimeIdentity),!0);let t=await emitRecoverableFailedTurn(a,d,{code:`HOOK_TURN_FAILED`,message:n});return{kind:`turn-failed`,message:n,nextSession:setHarnessEmissionState(l,t)}}catch(e){throw log.error(`Event hook threw while emitting the turn.failed cascade for a lifecycle.turn failure — escalating to session.failed.`,{error:toErrorMessage(e)}),e}}let f=e.input.modelContext??[],p=f.length===0?c:[...f,...c];return{kind:`proceed`,input:p.length>0?{...e.input,modelContext:p}:e.input,nextSession:l}}function normalizeLifecycleSkillResults(e,t){if(t?.skills===void 0||t.skills.length===0)return[];let n=new Set(e.require(BundleKey).resolvedAgent.skills.map(e=>e.name)),r=new Map;for(let e of t.skills){let t=normalizeSkillPackage(e);if(n.has(t.name))throw Error(`Hook-contributed skill "${t.name}" conflicts with an authored skill.`);r.set(t.name,t)}return[...r.values()]}async function materializeLifecycleSkills(e,n,r){if(r.length===0)return n;let i=new Map(r.map(e=>[e.name,e])),a=await e.require(SandboxKey).get();if(a===null)throw Error(`Dynamic skills require a sandbox for the current agent.`);for(let e of i.values())await writeSkillPackageToSandbox({sandbox:a,skill:e});let s=formatAvailableSkillsSection([...i.values()]);return s===null?n:{...n,history:[...n.history,{role:`system`,content:s}]}}async function dispatchStreamEventHooks(e){let t=e.registry.streamEventsByType.get(e.event.type)??[],n=e.registry.streamEventsWildcard;if(t.length===0&&n.length===0)return;let r=buildHookContext(e.ctx);for(let n of t)await n.handler(e.event,r);for(let t of n)await t.handler(e.event,r)}function buildHookContext(t){let n=t.require(BundleKey),r=t.get(ChannelKey),i=t.get(ContinuationTokenKey),a=r===void 0?void 0:getAdapterKind(r);return{...buildCallbackContext(),agent:{name:n.resolvedAgent.config.name??`agent`,nodeId:n.nodeId},channel:{kind:a,continuationToken:i}}}async function runHookLifecycleStep(e,t){let n=await dispatchHookLifecycle(e);return n.kind===`turn-failed`?{next:e.mode===`conversation`?null:{done:!0,output:n.message},session:n.nextSession}:t(n.nextSession,n.input)}export{dispatchHookLifecycle,dispatchStreamEventHooks,runHookLifecycleStep};
@@ -56,12 +56,11 @@ export declare const SessionPreparedKey: ContextKey<boolean>;
56
56
  export declare const SessionKey: ContextKey<Session>;
57
57
  export declare const SandboxKey: ContextKey<SandboxAccess>;
58
58
  /**
59
- * Virtual (non-serialized) live dynamic tool definitions for the
60
- * current step. Rebuilt per workflow step — either from live resolver
61
- * results (first step) or from durable metadata with lazy execute
62
- * wrappers (subsequent steps).
59
+ * Virtual (non-serialized) live dynamic tool definitions. Updated by
60
+ * {@link dispatchDynamicToolEvent} whenever a subscribed stream event
61
+ * fires. Read by the tool-loop when building the effective toolset.
63
62
  */
64
- export declare const DynamicSessionToolsKey: ContextKey<import("#harness/execute-tool.js").HarnessToolDefinition[]>;
63
+ export declare const DynamicToolsKey: ContextKey<import("#harness/execute-tool.js").HarnessToolDefinition[]>;
65
64
  /**
66
65
  * Durable (serialized) metadata for session-scoped dynamic tools.
67
66
  * Set on the first step when resolvers run. Read on subsequent steps
@@ -1 +1 @@
1
- import{ContextKey}from"#context/key.js";const AuthKey=new ContextKey(`ash.auth`),InitiatorAuthKey=new ContextKey(`ash.initiatorAuth`),SessionIdKey=new ContextKey(`ash.sessionId`),ContinuationTokenKey=new ContextKey(`ash.continuationToken`),ModeKey=new ContextKey(`ash.mode`),ParentSessionKey=new ContextKey(`ash.parentSession`),CapabilitiesKey=new ContextKey(`ash.capabilities`),SessionCallbackKey=new ContextKey(`ash.sessionCallback`),SessionPreparedKey=new ContextKey(`ash.sessionPrepared`),SessionKey=new ContextKey(`ash.session`),SandboxKey=new ContextKey(`ash.sandbox`),DynamicSessionToolsKey=new ContextKey(`ash.dynamicSessionTools`),DynamicSessionToolMetadataKey=new ContextKey(`ash.dynamicSessionToolMetadata`),DynamicToolResolverCacheKey=new ContextKey(`ash.dynamicToolResolverCache`);export{AuthKey,CapabilitiesKey,ContinuationTokenKey,DynamicSessionToolMetadataKey,DynamicSessionToolsKey,DynamicToolResolverCacheKey,InitiatorAuthKey,ModeKey,ParentSessionKey,SandboxKey,SessionCallbackKey,SessionIdKey,SessionKey,SessionPreparedKey};
1
+ import{ContextKey}from"#context/key.js";const AuthKey=new ContextKey(`ash.auth`),InitiatorAuthKey=new ContextKey(`ash.initiatorAuth`),SessionIdKey=new ContextKey(`ash.sessionId`),ContinuationTokenKey=new ContextKey(`ash.continuationToken`),ModeKey=new ContextKey(`ash.mode`),ParentSessionKey=new ContextKey(`ash.parentSession`),CapabilitiesKey=new ContextKey(`ash.capabilities`),SessionCallbackKey=new ContextKey(`ash.sessionCallback`),SessionPreparedKey=new ContextKey(`ash.sessionPrepared`),SessionKey=new ContextKey(`ash.session`),SandboxKey=new ContextKey(`ash.sandbox`),DynamicToolsKey=new ContextKey(`ash.dynamicTools`),DynamicSessionToolMetadataKey=new ContextKey(`ash.dynamicSessionToolMetadata`),DynamicToolResolverCacheKey=new ContextKey(`ash.dynamicToolResolverCache`);export{AuthKey,CapabilitiesKey,ContinuationTokenKey,DynamicSessionToolMetadataKey,DynamicToolResolverCacheKey,DynamicToolsKey,InitiatorAuthKey,ModeKey,ParentSessionKey,SandboxKey,SessionCallbackKey,SessionIdKey,SessionKey,SessionPreparedKey};
@@ -1,5 +1,5 @@
1
1
  import type { Runtime, SessionCapabilities } from "#channel/types.js";
2
- import type { HarnessEmitFn, HarnessToolMap, StepFn } from "#harness/types.js";
2
+ import type { HandleEventFn, HarnessToolMap, StepFn } from "#harness/types.js";
3
3
  import type { RunMode } from "#shared/run-mode.js";
4
4
  import type { RuntimeCompiledArtifactsSource } from "#runtime/compiled-artifacts-source.js";
5
5
  import type { ResolvedRuntimeAgentNode } from "#runtime/graph.js";
@@ -30,7 +30,7 @@ export interface CreateExecutionNodeStepInput {
30
30
  * delegated child runs on the same workflow runtime as the parent.
31
31
  */
32
32
  readonly createRuntime: CreateRuntime;
33
- readonly emit?: HarnessEmitFn;
33
+ readonly handleEvent?: HandleEventFn;
34
34
  readonly mode: RunMode;
35
35
  readonly node: ResolvedRuntimeAgentNode;
36
36
  }
@@ -1 +1 @@
1
- import{createLogger}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{jsonSchema}from"ai";import{buildCallbackContext}from"#context/build-callback-context.js";import{createToolLoopHarness}from"#harness/tool-loop.js";import{resolveCodeModeEnabled}from"#shared/code-mode.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}),r=collectResolvedTools(e.node),o=createToolCompactionHandler(r),s=collectRetentionPolicies(r);return createToolLoopHarness({capabilities:e.capabilities,codeMode:resolveCodeModeEnabled(e.node.agent.config?.experimental?.codeMode),emit:e.emit,mode:e.mode,onCompaction:o,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 i=t.definition,a=i.sourceId.startsWith(`ash:`),o=i.execute;return{approvalKey:i.approvalKey,description:i.description,execute:o===void 0?void 0:a?o:e=>o(e,buildCallbackContext()),inputSchema:i.inputStandardSchema??jsonSchema(i.inputSchema??{}),name:i.name,needsApproval:i.needsApproval,toModelOutput:i.toModelOutput}}export{createExecutionNodeStep,createNodeHarnessTools};
1
+ import{createLogger}from"#internal/logging.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{jsonSchema}from"ai";import{buildCallbackContext}from"#context/build-callback-context.js";import{createToolLoopHarness}from"#harness/tool-loop.js";import{resolveCodeModeEnabled}from"#shared/code-mode.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}),r=collectResolvedTools(e.node),o=createToolCompactionHandler(r),s=collectRetentionPolicies(r);return createToolLoopHarness({capabilities:e.capabilities,codeMode:resolveCodeModeEnabled(e.node.agent.config?.experimental?.codeMode),handleEvent:e.handleEvent,mode:e.mode,onCompaction:o,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 i=t.definition,a=i.sourceId.startsWith(`ash:`),o=i.execute;return{approvalKey:i.approvalKey,description:i.description,execute:o===void 0?void 0:a?o:e=>o(e,buildCallbackContext()),inputSchema:i.inputStandardSchema??jsonSchema(i.inputSchema??{}),name:i.name,needsApproval:i.needsApproval,toModelOutput:i.toModelOutput}}export{createExecutionNodeStep,createNodeHarnessTools};
@@ -1 +1 @@
1
- import{createLogger,formatError}from"#internal/logging.js";import{AuthKey,CapabilitiesKey,ContinuationTokenKey,ModeKey}from"#context/keys.js";import{createAuthorizationCompletedEvent,createSessionFailedEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{callAdapterEventHandler,defaultDeliverResult}from"#channel/adapter.js";import{getHarnessEmissionState,isHarnessBetweenTurns}from"#harness/emission.js";import{readDurableSession,writeDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession,refreshSessionFromTurnAgent}from"#execution/session.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{getPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{createWorkflowRuntime,startWorkflowPreferLatest}from"#execution/workflow-runtime.js";import{upsertProxyInputRequests}from"#harness/proxy-input-requests.js";import{coalesceTurnInputs}from"#harness/messages.js";import{dispatchStreamEventHooks,runHookLifecycleStep}from"#context/hook-lifecycle.js";import{runStep,withContextScope}from"#context/run-step.js";import{hasPendingInputBatch}from"#harness/input-requests.js";import{getRuntimeActionRequestKey}from"#runtime/actions/keys.js";import{CallbackBaseUrlKey,PendingAuthorizationResultKey,getPendingAuthorization}from"#harness/authorization.js";import{createExecutionNodeStep}from"#execution/node-step.js";import{emitProxiedInputRequest,routeDeliverPayload}from"#execution/subagent-hitl-proxy.js";import{turnWorkflow}from"#execution/turn-workflow.js";async function turnStep(e){"use step";let t=await readDurableSession(e.sessionState),i=await deserializeContext(e.serializedContext),s=i.require(ChannelKey),d=i.require(BundleKey),f=hydrateDurableSession({compactionOverrides:{thresholdPercent:d.resolvedAgent.config.compaction?.thresholdPercent},durable:t,turnAgent:d.turnAgent});try{let{getWorkflowMetadata:e}=await import(`#compiled/@workflow/core/index.js`),t=e();typeof t.url==`string`&&i.set(CallbackBaseUrlKey,t.url.replace(/\/$/,``))}catch{}let p=getPendingAuthorization(t.state),m;if(p&&e.input?.kind===`deliver`){let t=[],n=[];for(let r of e.input.payloads){let e=r.authorizationCallback;if(e){let n=p.challenges.find(t=>t.name===e.connectionName);n&&t.push({name:n.name,state:n.state,callback:e.request,hookUrl:n.hookUrl})}else n.push(r)}t.length>0&&(i.set(PendingAuthorizationResultKey,t),m=t.map(e=>e.name),e=n.length>0?{...e,input:{...e.input,payloads:n}}:{...e,input:void 0})}e.input?.kind===`deliver`&&e.input.auth!==void 0&&i.set(AuthKey,e.input.auth??null);let h=buildAdapterContext(s,i),g;if(e.input?.kind===`deliver`){let t=[];for(let n of e.input.payloads){let e=s.deliver?await s.deliver(n,h):defaultDeliverResult(n);e!=null&&t.push(e)}g=t.length===0?void 0:t.reduce(coalesceTurnInputs)}else e.input?.kind===`runtime-action-result`&&(g={runtimeActionResults:e.input.results});if(e.input?.kind===`deliver`){let e={...s,state:{...h.state}};i.set(ChannelKey,e)}if(e.input?.kind===`deliver`&&g===void 0){let t=reconcileSessionContinuationToken(i,f),n=serializeContext(i),r=t===f?e.sessionState:await writeDurableSession({session:t,writable:e.sessionWritable});return{action:`park`,...derivePendingState(t),serializedContext:n,sessionState:r}}let _=e.parentWritable.getWriter(),v=i.require(BundleKey).hookRegistry,emit=async e=>{let t=await callAdapterEventHandler(s,e,h);i.set(ChannelKey,{...s,state:{...h.state}}),await _.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t))),await dispatchStreamEventHooks({ctx:i,registry:v,event:t})},y=await runStep(i,f,async e=>{if(m){let t=getHarnessEmissionState(e.state);for(let e of m)await emit(createAuthorizationCompletedEvent({name:e,outcome:`authorized`,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId}))}let t=i.require(BundleKey),n=i.get(CapabilitiesKey),s=i.require(ModeKey),runHarnessStep=async(e,r)=>{let i=refreshSessionFromTurnAgent({compactionOverrides:{thresholdPercent:t.resolvedAgent.config.compaction?.thresholdPercent},session:e,turnAgent:t.turnAgent});return createExecutionNodeStep({capabilities:n,compiledArtifactsSource:t.compiledArtifactsSource,createRuntime:createWorkflowRuntime,emit,mode:s,node:t.graph.root})(i,r)};return g!==void 0&&isHarnessBetweenTurns(e)?runHookLifecycleStep({ctx:i,emit,input:g,mode:s,registry:t.hookRegistry,session:e},runHarnessStep):runHarnessStep(e,g)}),b=reconcileSessionContinuationToken(i,y.session),x=serializeContext(i);y={...y,session:b};let S=await writeDurableSession({session:y.session,writable:e.sessionWritable});return y.next!==null&&typeof y.next==`object`&&`done`in y.next?(await _.close(),{action:`done`,output:y.next.output,serializedContext:x,sessionState:S}):y.next===null?(_.releaseLock(),{action:`park`,...derivePendingState(y.session),serializedContext:x,sessionState:S}):(_.releaseLock(),{action:`continue`,serializedContext:x,sessionState:S})}function derivePendingState(e){let t=getPendingRuntimeActionBatch(e.state),n=getPendingAuthorization(e.state),r={authorizationNames:n?.challenges.map(e=>e.name),hasPendingAuthorization:n!==void 0,hasPendingInputBatch:hasPendingInputBatch(e.state)};return t===void 0?r:{...r,pendingRuntimeActionKeys:t.actions.map(e=>getRuntimeActionRequestKey(e))}}function reconcileSessionContinuationToken(e,t){let n=e.get(ContinuationTokenKey);return n===void 0||n===t.continuationToken?t:{...t,continuationToken:n}}const log=createLogger(`execution.workflow-entry`);async function emitTerminalSessionFailureStep(e){"use step";let n=formatError(e.error),r=typeof n.name==`string`?n.name:`WORKFLOW_EXECUTION_FAILED`,i=typeof n.message==`string`?n.message:String(e.error),a=e.serializedContext[`ash.sessionId`]??``;log.error(`workflow loop threw — emitting terminal session.failed`,{sessionId:a,errorId:typeof n.errorId==`string`?n.errorId:void 0,code:r,message:i,detail:typeof n.detail==`string`?n.detail:void 0});let o=createSessionFailedEvent({code:r,details:n,message:i,sessionId:a});try{let t=await deserializeContext(e.serializedContext),n=t.get(ChannelKey);n!==void 0&&await callAdapterEventHandler(n,o,buildAdapterContext(n,t))}catch(e){log.error(`adapter failed to handle terminal session.failed event`,{errorId:typeof n.errorId==`string`?n.errorId:void 0,sessionId:a,error:e})}try{let t=e.parentWritable.getWriter();try{await t.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(o)))}finally{t.releaseLock()}}catch(e){log.error(`failed to write terminal session.failed event to durable stream`,{errorId:typeof n.errorId==`string`?n.errorId:void 0,sessionId:a,error:e})}}async function runProxyInputRequestStep(e){"use step";let t=await readDurableSession(e.sessionState),n=await deserializeContext(e.serializedContext),r=n.require(ChannelKey),i=buildAdapterContext(r,n),o=n.require(ModeKey),s=n.require(BundleKey),c=hydrateDurableSession({compactionOverrides:{thresholdPercent:s.resolvedAgent.config.compaction?.thresholdPercent},durable:t,turnAgent:s.turnAgent}),l=e.parentWritable.getWriter(),u;try{let emit=async e=>{let t=await callAdapterEventHandler(r,e,i);await l.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t)))};u=await withContextScope(n,c,async t=>{let n=await emitProxiedInputRequest({emit,hookPayload:e.hookPayload,mode:o,session:t});return{result:n.entries,session:n.session}})}finally{l.releaseLock()}return n.set(ChannelKey,{...r,state:{...i.state}}),{serializedContext:serializeContext(n),sessionState:await writeDurableSession({session:reconcileSessionContinuationToken(n,upsertProxyInputRequests({entries:u.result,forChildContinuationToken:e.hookPayload.childContinuationToken,session:u.session})),writable:e.sessionWritable})}}async function routeProxiedDeliverStep(e){"use step";let t=await readDurableSession(e.sessionState),n=routeDeliverPayload({payload:e.payload,state:t.state}),{resumeHook:r}=await import(`#compiled/@workflow/core/runtime.js`);for(let t of n.forChildren)await r(t.childContinuationToken,{auth:e.auth,kind:`deliver`,payloads:[t.payload]});return{remainder:n.forSelf}}async function dispatchTurnStep(e){"use step";return{runId:(await startWorkflowPreferLatest(turnWorkflow,[e])).runId}}export{dispatchTurnStep,emitTerminalSessionFailureStep,reconcileSessionContinuationToken,routeProxiedDeliverStep,runProxyInputRequestStep,turnStep};
1
+ import{createLogger,formatError}from"#internal/logging.js";import{AuthKey,CapabilitiesKey,ContinuationTokenKey,ModeKey}from"#context/keys.js";import{createAuthorizationCompletedEvent,createSessionFailedEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{callAdapterEventHandler,defaultDeliverResult}from"#channel/adapter.js";import{getHarnessEmissionState,isHarnessBetweenTurns}from"#harness/emission.js";import{readDurableSession,writeDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession,refreshSessionFromTurnAgent}from"#execution/session.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{getPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{createWorkflowRuntime,startWorkflowPreferLatest}from"#execution/workflow-runtime.js";import{upsertProxyInputRequests}from"#harness/proxy-input-requests.js";import{coalesceTurnInputs}from"#harness/messages.js";import{dispatchStreamEventHooks,runHookLifecycleStep}from"#context/hook-lifecycle.js";import{dispatchDynamicToolEvent}from"#context/dynamic-tool-lifecycle.js";import{runStep,withContextScope}from"#context/run-step.js";import{hasPendingInputBatch}from"#harness/input-requests.js";import{getRuntimeActionRequestKey}from"#runtime/actions/keys.js";import{CallbackBaseUrlKey,PendingAuthorizationResultKey,getPendingAuthorization}from"#harness/authorization.js";import{createExecutionNodeStep}from"#execution/node-step.js";import{emitProxiedInputRequest,routeDeliverPayload}from"#execution/subagent-hitl-proxy.js";import{turnWorkflow}from"#execution/turn-workflow.js";async function turnStep(e){"use step";let t=await readDurableSession(e.sessionState),i=await deserializeContext(e.serializedContext),s=i.require(ChannelKey),d=i.require(BundleKey),f=hydrateDurableSession({compactionOverrides:{thresholdPercent:d.resolvedAgent.config.compaction?.thresholdPercent},durable:t,turnAgent:d.turnAgent});try{let{getWorkflowMetadata:e}=await import(`#compiled/@workflow/core/index.js`),t=e();typeof t.url==`string`&&i.set(CallbackBaseUrlKey,t.url.replace(/\/$/,``))}catch{}let p=getPendingAuthorization(t.state),m;if(p&&e.input?.kind===`deliver`){let t=[],n=[];for(let r of e.input.payloads){let e=r.authorizationCallback;if(e){let n=p.challenges.find(t=>t.name===e.connectionName);n&&t.push({name:n.name,state:n.state,callback:e.request,hookUrl:n.hookUrl})}else n.push(r)}t.length>0&&(i.set(PendingAuthorizationResultKey,t),m=t.map(e=>e.name),e=n.length>0?{...e,input:{...e.input,payloads:n}}:{...e,input:void 0})}e.input?.kind===`deliver`&&e.input.auth!==void 0&&i.set(AuthKey,e.input.auth??null);let h=buildAdapterContext(s,i),g;if(e.input?.kind===`deliver`){let t=[];for(let n of e.input.payloads){let e=s.deliver?await s.deliver(n,h):defaultDeliverResult(n);e!=null&&t.push(e)}g=t.length===0?void 0:t.reduce(coalesceTurnInputs)}else e.input?.kind===`runtime-action-result`&&(g={runtimeActionResults:e.input.results});if(e.input?.kind===`deliver`){let e={...s,state:{...h.state}};i.set(ChannelKey,e)}if(e.input?.kind===`deliver`&&g===void 0){let t=reconcileSessionContinuationToken(i,f),n=serializeContext(i),r=t===f?e.sessionState:await writeDurableSession({session:t,writable:e.sessionWritable});return{action:`park`,...derivePendingState(t),serializedContext:n,sessionState:r}}let _=e.parentWritable.getWriter(),v=d.hookRegistry,y=d.resolvedAgent.dynamicToolResolvers??[],emit=async e=>{let t=await callAdapterEventHandler(s,e,h);return i.set(ChannelKey,{...s,state:{...h.state}}),await _.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t))),t},handleEvent=async e=>{let t=await emit(e);await dispatchStreamEventHooks({ctx:i,registry:v,event:t}),await dispatchDynamicToolEvent({ctx:i,resolvers:y,event:t})},b=await runStep(i,f,async e=>{if(m){let t=getHarnessEmissionState(e.state);for(let e of m)await handleEvent(createAuthorizationCompletedEvent({name:e,outcome:`authorized`,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId}))}let t=i.get(CapabilitiesKey),n=i.require(ModeKey),runHarnessStep=async(e,r)=>{let i=refreshSessionFromTurnAgent({compactionOverrides:{thresholdPercent:d.resolvedAgent.config.compaction?.thresholdPercent},session:e,turnAgent:d.turnAgent});return createExecutionNodeStep({capabilities:t,compiledArtifactsSource:d.compiledArtifactsSource,createRuntime:createWorkflowRuntime,handleEvent,mode:n,node:d.graph.root})(i,r)};return g!==void 0&&isHarnessBetweenTurns(e)?runHookLifecycleStep({ctx:i,emit:handleEvent,input:g,mode:n,registry:d.hookRegistry,session:e},runHarnessStep):runHarnessStep(e,g)}),x=reconcileSessionContinuationToken(i,b.session),S=serializeContext(i);b={...b,session:x};let C=await writeDurableSession({session:b.session,writable:e.sessionWritable});return b.next!==null&&typeof b.next==`object`&&`done`in b.next?(await _.close(),{action:`done`,output:b.next.output,serializedContext:S,sessionState:C}):b.next===null?(_.releaseLock(),{action:`park`,...derivePendingState(b.session),serializedContext:S,sessionState:C}):(_.releaseLock(),{action:`continue`,serializedContext:S,sessionState:C})}function derivePendingState(e){let t=getPendingRuntimeActionBatch(e.state),n=getPendingAuthorization(e.state),r={authorizationNames:n?.challenges.map(e=>e.name),hasPendingAuthorization:n!==void 0,hasPendingInputBatch:hasPendingInputBatch(e.state)};return t===void 0?r:{...r,pendingRuntimeActionKeys:t.actions.map(e=>getRuntimeActionRequestKey(e))}}function reconcileSessionContinuationToken(e,t){let n=e.get(ContinuationTokenKey);return n===void 0||n===t.continuationToken?t:{...t,continuationToken:n}}const log=createLogger(`execution.workflow-entry`);async function emitTerminalSessionFailureStep(e){"use step";let n=formatError(e.error),r=typeof n.name==`string`?n.name:`WORKFLOW_EXECUTION_FAILED`,i=typeof n.message==`string`?n.message:String(e.error),a=e.serializedContext[`ash.sessionId`]??``;log.error(`workflow loop threw — emitting terminal session.failed`,{sessionId:a,errorId:typeof n.errorId==`string`?n.errorId:void 0,code:r,message:i,detail:typeof n.detail==`string`?n.detail:void 0});let o=createSessionFailedEvent({code:r,details:n,message:i,sessionId:a});try{let t=await deserializeContext(e.serializedContext),n=t.get(ChannelKey);n!==void 0&&await callAdapterEventHandler(n,o,buildAdapterContext(n,t))}catch(e){log.error(`adapter failed to handle terminal session.failed event`,{errorId:typeof n.errorId==`string`?n.errorId:void 0,sessionId:a,error:e})}try{let t=e.parentWritable.getWriter();try{await t.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(o)))}finally{t.releaseLock()}}catch(e){log.error(`failed to write terminal session.failed event to durable stream`,{errorId:typeof n.errorId==`string`?n.errorId:void 0,sessionId:a,error:e})}}async function runProxyInputRequestStep(e){"use step";let t=await readDurableSession(e.sessionState),n=await deserializeContext(e.serializedContext),r=n.require(ChannelKey),i=buildAdapterContext(r,n),o=n.require(ModeKey),s=n.require(BundleKey),c=hydrateDurableSession({compactionOverrides:{thresholdPercent:s.resolvedAgent.config.compaction?.thresholdPercent},durable:t,turnAgent:s.turnAgent}),l=e.parentWritable.getWriter(),u;try{let emit=async e=>{let t=await callAdapterEventHandler(r,e,i);await l.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t)))};u=await withContextScope(n,c,async t=>{let n=await emitProxiedInputRequest({emit,hookPayload:e.hookPayload,mode:o,session:t});return{result:n.entries,session:n.session}})}finally{l.releaseLock()}return n.set(ChannelKey,{...r,state:{...i.state}}),{serializedContext:serializeContext(n),sessionState:await writeDurableSession({session:reconcileSessionContinuationToken(n,upsertProxyInputRequests({entries:u.result,forChildContinuationToken:e.hookPayload.childContinuationToken,session:u.session})),writable:e.sessionWritable})}}async function routeProxiedDeliverStep(e){"use step";let t=await readDurableSession(e.sessionState),n=routeDeliverPayload({payload:e.payload,state:t.state}),{resumeHook:r}=await import(`#compiled/@workflow/core/runtime.js`);for(let t of n.forChildren)await r(t.childContinuationToken,{auth:e.auth,kind:`deliver`,payloads:[t.payload]});return{remainder:n.forSelf}}async function dispatchTurnStep(e){"use step";return{runId:(await startWorkflowPreferLatest(turnWorkflow,[e])).runId}}export{dispatchTurnStep,emitTerminalSessionFailureStep,reconcileSessionContinuationToken,routeProxiedDeliverStep,runProxyInputRequestStep,turnStep};
@@ -1 +1 @@
1
- import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{DynamicSessionToolsKey}from"#context/keys.js";import{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import"#shared/json.js";import{contextStorage}from"#context/container.js";import{ToolLoopAgent,isStepCount}from"ai";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{resolveDynamicStepTools}from"#context/dynamic-tool-lifecycle.js";import{ConnectionRegistryKey,DiscoveredConnectionToolsKey}from"#runtime/framework-tools/connection-search.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{getHookUrl,isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{resolveConnectionToolsFromState}from"#runtime/framework-tools/connection-tools.js";import{buildToolSetWithProviderTools}from"#harness/tools.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{clearPendingCodeModeApproval,getPendingCodeModeApproval,replaceCodeModeApprovalInterruptResult,setPendingCodeModeApproval}from"#harness/code-mode-approval.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{clearPendingCodeModeConnectionAuth,getPendingCodeModeConnectionAuth,setPendingCodeModeConnectionAuth}from"#harness/code-mode-connection-auth-state.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t){if(e!==void 0)return{functionId:e.functionId??t,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function buildTelemetryRuntimeContext(e,t){if(e!==void 0)return{...e.metadata,"ash.continuation_token":t.continuationToken,"ash.environment":environment,"ash.session.id":t.sessionId,"ash.version":ashVersion}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`ash.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.emit,o=getInstrumentationConfig();o!==void 0&&ensureOtelIntegration();let s=o===void 0?void 0:trace.getTracer(`ash`),c=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(s&&hasStepInput(t)){let t=o?.functionId??c,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId,"ash.continuation_token":e.continuationToken};t&&(r[`ai.telemetry.functionId`]=t),n=s.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(s,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(s,l,d){let f=s;d&&(f=setTurnTraceState(f,d.spanContext()));let _=getHarnessEmissionState(f.state),x=consumeDeferredStepInput({input:l,session:f});f=x.session;let D=await resolvePendingRuntimeActions({emit:n,session:f,stepInput:x.input});if(D.outcome===`unresolved`)return{next:null,session:D.session};f=D.session;let O=resolvePendingInput({history:D.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:f,stepInput:x.input});if(O.outcome===`unresolved`)return{next:null,session:O.session};n&&hasStepInput(l)&&(_=await emitTurnPreamble(n,l??{},_,t.runtimeIdentity),f=setHarnessEmissionState(f,_),d&&d.setAttribute(`ash.turn.id`,_.turnId)),f=O.session;let k=O.messages,A=await continuePendingCodeModeConnectionAuth({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:k,runStep,session:f});if(A!==null)return A;let j=await continuePendingCodeModeApproval({capabilities:t.capabilities,config:t,emit:n,emissionState:_,messages:k,runStep,session:f});if(j!==null)return j;if(x.input?.message!==void 0&&!O.deferredMessage){let e=await stageAttachmentsToSandbox(x.input.message);k.push({content:e,role:`user`})}let M=await t.resolveModel(f.agent.modelReference),N=detectPromptCachePath(M),P=N.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,F=buildGatewayAttributionHeaders(M,t.runtimeIdentity);({messages:k,session:f}=await maybeCompact({emit:n,emissionState:_,headers:F,messages:k,model:M,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:f,telemetry:enrichTelemetry(o,c)??void 0}));let I=getApprovedTools(f),L=contextStorage.getStore(),R=L?.get(ConnectionRegistryKey),z=L?.get(DiscoveredConnectionToolsKey),B=await hydrateSandboxAttachments(k),V=x.input?.modelContext,H=[],U=[];for(let e of B)e.role===`system`?H.push(e):U.push(e);if(V!==void 0)for(let e of V)e.role===`system`?H.push(e):U.push(e);let W=U,runOneModelCall=async e=>{let i=t.codeMode===!0,s=await buildToolSetWithProviderTools({approvedTools:I,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:f.agent.modelReference,tools:t.tools});if(R!==void 0&&z!==void 0){let e=await resolveConnectionToolsFromState(R,z,{approvedTools:I,authMode:i?`code-mode`:`direct`,existingToolNames:new Set(Object.keys(s))});Object.assign(s,e)}if(L!==void 0){let e=L.get(DynamicSessionToolsKey);if(e!==void 0)for(let t of e)s[t.name]===void 0&&(s[t.name]={description:t.description,inputSchema:t.inputSchema,execute:t.execute});let t=L.get(BundleKey)?.resolvedAgent?.dynamicToolResolvers??[];if(t.length>0){let e=await resolveDynamicStepTools(L,t),n=new Set(t.filter(e=>e.eventNames.includes(`step.started`)).map(e=>e.slug));for(let e of n)for(let t of Object.keys(s))(t===e||t.startsWith(`${e}__`))&&delete s[t];for(let t of e)s[t.name]={description:t.description,inputSchema:t.inputSchema,execute:t.execute}}}let l=i?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:_,tools:t.tools}),tools:s})).modelTools:s,u=P?applyLastToolCacheBreakpoint(l,P):l,d=resolveGatewayPinForStep({cachePath:N,modelReference:f.agent.modelReference,tools:u}),p=e.extraSystemNote?[{role:`system`,content:e.extraSystemNote}]:[],v=f.agent.system?[{role:`system`,content:f.agent.system}]:[],y=H.length>0||p.length>0?[...p,...v,...H]:f.agent.system||void 0,x=buildStepHooks({cachePath:N,emit:n,emissionState:_,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:d,marker:P,session:f}),S=new ToolLoopAgent({headers:F,instructions:y,model:M,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:x.onStepFinish,prepareStep:x.prepareStep,runtimeContext:buildTelemetryRuntimeContext(o,f),stopWhen:isStepCount(1),telemetry:enrichTelemetry(o,c),tools:u});return runModelCallWithRetries(async()=>{if(n){let e=await S.stream({messages:W}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,_,e.fullStream),a=await x.stepResult;return await emitStepActions(n,_,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME]),inlineActionResultCallIds:r,tools:t.tools}),i.length>0?{content:a.content,finishReason:a.finishReason,response:{...a.response,messages:[{role:`tool`,content:[...i]},...a.response.messages]},text:a.text,toolCalls:a.toolCalls,toolResults:a.toolResults,usage:a.usage}:a}return await S.generate({messages:W}),await x.stepResult},{sessionId:f.sessionId,turnId:_.turnId})},G;try{G=await runOneModelCall({})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:f.sessionId,turnId:_.turnId});if(r.outcome===`recovered`)G=r.result;else{let t=r.error;if(d&&recordErrorOnSpan(d,t),!n)throw t;let a=classifyModelCallError(t),o=createErrorId(),s=a===`terminal`?summarizeKnownModelCallConfigError(t):null,c=s===null?summarizeKnownModelCallRequestError(t):null,l=s?.message??c?.message??toErrorMessage(t),p=extractModelCallErrorDetails(t),m=buildModelCallFailureDetails({configSummary:s,error:t,errorId:o,modelCallDetails:p,requestSummary:c}),h=buildModelCallFailureLogFields({error:t,errorId:o,modelCallDetails:p,requestSummary:c,sessionId:f.sessionId,turnId:_.turnId});return a===`terminal`?(s===null?log.error(c?.message??`model call failed terminally`,h):log.error(`${s.name}: ${s.message}`,{errorId:o,sessionId:f.sessionId,turnId:_.turnId}),await emitFailedStep(n,_,{code:`MODEL_CALL_FAILED`,details:m,message:l,sessionId:f.sessionId}),{next:{done:!0,output:``},session:f}):(log.error(c?.message??`model call failed — parking session for retry by the user`,h),_=await emitRecoverableFailedTurn(n,_,{code:`MODEL_CALL_FAILED`,details:m,message:l}),{next:null,session:setHarnessEmissionState(f,_)})}}return handleStepResult({config:t,emit:n,emissionState:_,promptMessages:k,result:G,runStep,session:f})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`failed`,error:e.error};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`failed`,error:e.error};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});try{return{outcome:`recovered`,result:await e.runOneModelCall({disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n),suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:a}=e,{emissionState:s,session:c}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...c,compaction:createNextCompactionConfig(c.compaction,r,i)};if(t.codeMode===!0){let{getCodeModeInterrupt:e,isCodeModeApprovalInterrupt:a}=await loadCodeModeModule(),o=e(i);if(o!==void 0){if(isCodeModeConnectionAuthInterrupt(o))return parkOnCodeModeConnectionAuth({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u});if(a(o))return parkOnCodeModeApproval({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u})}}let p=extractToolApprovalInputRequests({content:i.content??[]}),m=new Set(p.map(e=>e.action.callId)),h=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:m}),g=[...p,...h],v=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(v.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:v,event:{sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId},responseMessages:u,session:{...f,history:[...r]}}),s)};if(g.length>0){let e=setPendingInputBatch({requests:g,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:g,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId})),t.mode===`conversation`&&(s=await emitTurnEpilogue(n,s,t.mode),e=setHarnessEmissionState(e,s))),{next:null,session:e}}let y=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(y&&isAuthorizationSignal(y.output)){let{challenges:e}=y.output;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId}));return{next:null,session:setHarnessEmissionState({...f,history:[...r],state:setPendingAuthorization(f.state,{challenges:e})},s)}}let b=pruneToolResults(r,t.retentionPolicies),S=b!==r,C=f.compaction;S&&C.lastKnownInputTokens!==void 0&&(C={recentWindowSize:C.recentWindowSize,threshold:C.threshold});let w=[...b,...u],T={...f,compaction:C,history:w},E=u.at(-1)?.role===`tool`||hasDeferredStepInput(T);return n&&(s=E?advanceStep(s):await emitTurnEpilogue(n,s,t.mode),T=setHarnessEmissionState(T,s)),E?{next:a,session:T}:{next:t.mode===`task`?{done:!0,output:d??``}:null,session:T}}async function continuePendingCodeModeApproval(e){let t=getPendingCodeModeApproval(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,getCodeModeApprovalResponse:i,isCodeModeApprovalInterrupt:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=i([...e.messages],t.interrupt);if(s===void 0)return{next:null,session:e.session};let c=contextStorage.getStore(),l=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,discovered:c?.get(DiscoveredConnectionToolsKey),registry:c?.get(ConnectionRegistryKey),tools:e.config.tools}),d;try{d=await n({approvalResponse:s,interrupt:t.interrupt,options:createAshCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),tools:l})}catch(e){logError(log,`code-mode approval continuation failed`,e),d={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let f=o(d),m=f.status===`interrupted`?f.interrupt:f.output,h=replaceCodeModeApprovalInterruptResult([...e.session.history,...t.responseMessages],t.interrupt,m),g=clearPendingCodeModeApproval({...e.session,history:h});if(f.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(f.interrupt)){let t=e.session.history.length,n=h.slice(0,t),r=h.slice(t);return g={...g,history:n},parkOnCodeModeConnectionAuth({baseSession:g,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:f.interrupt,promptMessages:n,responseMessages:r})}if(a(f.interrupt)){let t=e.session.history.length,n=h.slice(0,t),r=h.slice(t);return g={...g,history:n},parkOnCodeModeApproval({baseSession:g,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:f.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:g}}async function parkOnCodeModeConnectionAuth(e){let{connectionName:t}=e.interrupt.payload,n=(contextStorage.getStore()?.get(ConnectionRegistryKey))?.getConnections().find(e=>e.connectionName===t),r=n?.authorization&&supportsInteractiveAuthorization(n.authorization)?n.authorization:void 0,i=[];if(r){let e=getHookUrl(t);if(e){let a=resolveConnectionPrincipal(t,r),{challenge:o,state:s}=await r.startAuthorization({callbackUrl:e,connection:{url:n?.url??``},principal:a});i.push({name:t,challenge:o,hookUrl:e,state:s})}}if(e.emit)for(let t of i)await e.emit(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeConnectionAuth({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages],state:setPendingAuthorization(e.baseSession.state,{challenges:i})}})}}async function continuePendingCodeModeConnectionAuth(e){let t=getPendingCodeModeConnectionAuth(e.session.state);if(t===void 0)return null;let{continueCodeModeInterrupt:n,isCodeModeApprovalInterrupt:i,replaceCodeModeInterruptResult:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=contextStorage.getStore(),c=new Set,{connectionName:l}=t.interrupt.payload;c.add(l);let d=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,codeModeMetadataOnlyConnectionNames:c,discovered:s?.get(DiscoveredConnectionToolsKey),registry:s?.get(ConnectionRegistryKey),tools:e.config.tools}),f=e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools}),m;try{m=await n({interrupt:t.interrupt,resolution:{status:`authorized`},tools:d,options:createAshCodeModeOptions({lifecycle:f})})}catch(e){logError(log,`code-mode interrupt continuation failed`,e),m={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let h=o(m),g=h.status===`interrupted`?h.interrupt:h.output,_=a([...e.session.history,...t.responseMessages],t.interrupt.pendingContinuation,g),v=clearPendingCodeModeConnectionAuth({...e.session,history:_});if(h.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(h.interrupt)){let t=e.session.history.length,n=_.slice(0,t),r=_.slice(t);return v={...v,history:n},parkOnCodeModeConnectionAuth({baseSession:v,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:h.interrupt,promptMessages:n,responseMessages:r})}if(i(h.interrupt)){let t=e.session.history.length,n=_.slice(0,t),r=_.slice(t);return v={...v,history:n},parkOnCodeModeApproval({baseSession:v,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:h.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:v}}async function parkOnCodeModeApproval(e){let{toCodeModeApprovalMessages:t}=await loadCodeModeModule(),n=t(e.interrupt),r=extractToolApprovalInputRequests({content:extractAssistantContent(n)}),i=setPendingInputBatch({requests:r,responseMessages:n,session:setPendingCodeModeApproval({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages]}})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:r,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);i=setHarnessEmissionState(i,t)}return{next:null,session:i}}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction){let t=await e.onCompaction(i);i=t.session;for(let e of t.messages)r.push(e)}return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}export{createToolLoopHarness};
1
+ import{createErrorId,createLogger,formatError,logError,recordErrorOnSpan}from"#internal/logging.js";import{DynamicToolsKey}from"#context/keys.js";import{createAuthorizationRequiredEvent,createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import"#shared/json.js";import{contextStorage}from"#context/container.js";import{ToolLoopAgent,isStepCount}from"ai";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{ConnectionRegistryKey,DiscoveredConnectionToolsKey}from"#runtime/framework-tools/connection-search.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{CODE_MODE_TOOL_NAME,loadCodeModeModule}from"#shared/code-mode.js";import{resolveAssistantStepText}from"#harness/messages.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{getHookUrl,isAuthorizationSignal,setPendingAuthorization}from"#harness/authorization.js";import{isCodeModeConnectionAuthInterrupt}from"#runtime/framework-tools/code-mode-connection-auth.js";import{resolveConnectionToolsFromState}from"#runtime/framework-tools/connection-tools.js";import{buildToolSetWithProviderTools}from"#harness/tools.js";import{ASK_QUESTION_TOOL_NAME}from"#runtime/framework-tools/ask-question.js";import{WEB_SEARCH_TOOL_DEFINITION}from"#runtime/framework-tools/web-search.js";import{extractQuestionInputRequests,extractToolApprovalInputRequests}from"#harness/input-extraction.js";import{applyLastToolCacheBreakpoint,detectPromptCachePath,getAnthropicCacheMarker}from"#harness/prompt-cache.js";import{resolveFrameworkToolFromUpstreamType,resolveGatewayPinForWebSearchBackend,resolveWebSearchBackend}from"#harness/provider-tools.js";import{context,trace}from"#compiled/@opentelemetry/api/index.js";import{resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{supportsInteractiveAuthorization}from"#runtime/connections/types.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{applyCodeModeToToolSet,buildCodeModeHostTools,createAshCodeModeOptions}from"#harness/code-mode.js";import{createCodeModeLifecycle}from"#harness/code-mode-lifecycle.js";import{clearPendingCodeModeApproval,getPendingCodeModeApproval,replaceCodeModeApprovalInterruptResult,setPendingCodeModeApproval}from"#harness/code-mode-approval.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.js";import{clearPendingCodeModeConnectionAuth,getPendingCodeModeConnectionAuth,setPendingCodeModeConnectionAuth}from"#harness/code-mode-connection-auth-state.js";import{classifyModelCallError,extractModelCallErrorDetails,extractUnsupportedProviderToolTypes,summarizeKnownModelCallConfigError,summarizeKnownModelCallRequestError}from"#harness/model-call-error.js";import{ensureOtelIntegration}from"#harness/otel-integration.js";import{buildStepHooks,emitStepActions,isInvalidToolCall}from"#harness/step-hooks.js";import{pruneToolResults}from"#harness/tool-result-pruning.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);function logToolExecutionError(e){e.toolOutput.type===`tool-error`&&logError(log,`tool execution failed`,e.toolOutput.error,{toolName:e.toolCall.toolName,toolCallId:e.toolCall.toolCallId})}function enrichTelemetry(e,t){if(e!==void 0)return{functionId:e.functionId??t,isEnabled:!0,recordInputs:e.recordInputs??!0,recordOutputs:e.recordOutputs??!0}}function buildTelemetryRuntimeContext(e,t){if(e!==void 0)return{...e.metadata,"ash.continuation_token":t.continuationToken,"ash.environment":environment,"ash.session.id":t.sessionId,"ash.version":ashVersion}}function resolveGatewayPinForStep(e){if(e.cachePath.kind!==`gateway-auto`||e.tools[WEB_SEARCH_TOOL_DEFINITION.name]===void 0)return;let t=resolveWebSearchBackend(e.modelReference);return t===null?void 0:resolveGatewayPinForWebSearchBackend(t)??void 0}function buildGatewayAttributionHeaders(e,t){if(typeof e!=`string`)return;let n=t?.agentName??t?.agentId,r=process.env.VERCEL_PROJECT_PRODUCTION_URL||process.env.VERCEL_URL,i=r?`https://${r}`:void 0;if(!n&&!i)return;let a={};return n&&(a[`x-title`]=n),i&&(a[`http-referer`]=i),a}const TURN_TRACE_STATE_KEY=`ash.harness.turnTrace`;function getTurnTraceState(e){return e.state?.[TURN_TRACE_STATE_KEY]}function setTurnTraceState(e,t){let n={traceId:t.traceId,spanId:t.spanId,traceFlags:t.traceFlags};return{...e,state:{...e.state,[TURN_TRACE_STATE_KEY]:n}}}function resolveStepOtelContext(e,t,n){if(t)return trace.setSpan(context.active(),t);if(e){let e=getTurnTraceState(n);if(e){let t=trace.wrapSpanContext({traceId:e.traceId,spanId:e.spanId,traceFlags:e.traceFlags});return trace.setSpan(context.active(),t)}}}function createToolLoopHarness(t){let n=t.handleEvent,o=getInstrumentationConfig();o!==void 0&&ensureOtelIntegration();let s=o===void 0?void 0:trace.getTracer(`ash`),c=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(s&&hasStepInput(t)){let t=o?.functionId??c,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId,"ash.continuation_token":e.continuationToken};t&&(r[`ai.telemetry.functionId`]=t),n=s.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(s,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(s,l,d){let f=s;d&&(f=setTurnTraceState(f,d.spanContext()));let g=getHarnessEmissionState(f.state),x=consumeDeferredStepInput({input:l,session:f});f=x.session;let E=await resolvePendingRuntimeActions({emit:n,session:f,stepInput:x.input});if(E.outcome===`unresolved`)return{next:null,session:E.session};f=E.session;let O=resolvePendingInput({history:E.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:f,stepInput:x.input});if(O.outcome===`unresolved`)return{next:null,session:O.session};n&&hasStepInput(l)&&(g=await emitTurnPreamble(n,l??{},g,t.runtimeIdentity),f=setHarnessEmissionState(f,g),d&&d.setAttribute(`ash.turn.id`,g.turnId)),f=O.session;let k=O.messages,A=await continuePendingCodeModeConnectionAuth({capabilities:t.capabilities,config:t,emit:n,emissionState:g,messages:k,runStep,session:f});if(A!==null)return A;let j=await continuePendingCodeModeApproval({capabilities:t.capabilities,config:t,emit:n,emissionState:g,messages:k,runStep,session:f});if(j!==null)return j;if(x.input?.message!==void 0&&!O.deferredMessage){let e=await stageAttachmentsToSandbox(x.input.message);k.push({content:e,role:`user`})}let M=await t.resolveModel(f.agent.modelReference),N=detectPromptCachePath(M),P=N.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,F=buildGatewayAttributionHeaders(M,t.runtimeIdentity);({messages:k,session:f}=await maybeCompact({emit:n,emissionState:g,headers:F,messages:k,model:M,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:f,telemetry:enrichTelemetry(o,c)??void 0}));let I=getApprovedTools(f),L=contextStorage.getStore(),R=L?.get(ConnectionRegistryKey),z=L?.get(DiscoveredConnectionToolsKey),B=await hydrateSandboxAttachments(k),V=x.input?.modelContext,H=[],U=[];for(let e of B)e.role===`system`?H.push(e):U.push(e);if(V!==void 0)for(let e of V)e.role===`system`?H.push(e):U.push(e);let W=U,runOneModelCall=async e=>{let i=t.codeMode===!0,s=await buildToolSetWithProviderTools({approvedTools:I,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:f.agent.modelReference,tools:t.tools});if(R!==void 0&&z!==void 0){let e=await resolveConnectionToolsFromState(R,z,{approvedTools:I,authMode:i?`code-mode`:`direct`,existingToolNames:new Set(Object.keys(s))});Object.assign(s,e)}if(L!==void 0){let e=L.get(DynamicToolsKey);if(e!==void 0)for(let t of e)s[t.name]??={description:t.description,inputSchema:t.inputSchema,execute:t.execute}}let l=i?(await applyCodeModeToToolSet({harnessTools:t.tools,lifecycle:n===void 0?void 0:createCodeModeLifecycle({emit:n,emissionState:g,tools:t.tools}),tools:s})).modelTools:s,u=P?applyLastToolCacheBreakpoint(l,P):l,d=resolveGatewayPinForStep({cachePath:N,modelReference:f.agent.modelReference,tools:u}),p=e.extraSystemNote?[{role:`system`,content:e.extraSystemNote}]:[],_=f.agent.system?[{role:`system`,content:f.agent.system}]:[],v=H.length>0||p.length>0?[...p,..._,...H]:f.agent.system||void 0,y=buildStepHooks({cachePath:N,emit:n,emissionState:g,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:d,marker:P,session:f}),x=new ToolLoopAgent({headers:F,instructions:v,model:M,onToolExecutionEnd:logToolExecutionError,onError(e){logError(log,`tool-loop stream error`,e.error)},onStepFinish:y.onStepFinish,prepareStep:y.prepareStep,runtimeContext:buildTelemetryRuntimeContext(o,f),stopWhen:isStepCount(1),telemetry:enrichTelemetry(o,c),tools:u});return runModelCallWithRetries(async()=>{if(n){let e=await x.stream({messages:W}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,g,e.fullStream),a=await y.stepResult;return await emitStepActions(n,g,a,{excludedActionToolNames:new Set([ASK_QUESTION_TOOL_NAME,CODE_MODE_TOOL_NAME]),inlineActionResultCallIds:r,tools:t.tools}),i.length>0?{content:a.content,finishReason:a.finishReason,response:{...a.response,messages:[{role:`tool`,content:[...i]},...a.response.messages]},text:a.text,toolCalls:a.toolCalls,toolResults:a.toolResults,usage:a.usage}:a}return await x.generate({messages:W}),await y.stepResult},{sessionId:f.sessionId,turnId:g.turnId})};n&&await emitStepStarted(n,g);let G;try{G=await runOneModelCall({suppressStepStartedEmission:!0})}catch(t){let r=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:f.sessionId,turnId:g.turnId});if(r.outcome===`recovered`)G=r.result;else{let t=r.error;if(d&&recordErrorOnSpan(d,t),!n)throw t;let a=classifyModelCallError(t),o=createErrorId(),s=a===`terminal`?summarizeKnownModelCallConfigError(t):null,c=s===null?summarizeKnownModelCallRequestError(t):null,l=s?.message??c?.message??toErrorMessage(t),p=extractModelCallErrorDetails(t),m=buildModelCallFailureDetails({configSummary:s,error:t,errorId:o,modelCallDetails:p,requestSummary:c}),h=buildModelCallFailureLogFields({error:t,errorId:o,modelCallDetails:p,requestSummary:c,sessionId:f.sessionId,turnId:g.turnId});return a===`terminal`?(s===null?log.error(c?.message??`model call failed terminally`,h):log.error(`${s.name}: ${s.message}`,{errorId:o,sessionId:f.sessionId,turnId:g.turnId}),await emitFailedStep(n,g,{code:`MODEL_CALL_FAILED`,details:m,message:l,sessionId:f.sessionId}),{next:{done:!0,output:``},session:f}):(log.error(c?.message??`model call failed — parking session for retry by the user`,h),g=await emitRecoverableFailedTurn(n,g,{code:`MODEL_CALL_FAILED`,details:m,message:l}),{next:null,session:setHarnessEmissionState(f,g)})}}return handleStepResult({config:t,emit:n,emissionState:g,promptMessages:k,result:G,runStep,session:f})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:i,modelCallDetails:a,requestSummary:o}=e;return t===null?o===null?{...formatError(r,i),...a}:{errorId:i,message:toErrorMessage(r),name:o.name,...a}:{errorId:i,message:t.message,name:t.name,...a}}function buildModelCallFailureLogFields(e){let t={errorId:e.errorId,sessionId:e.sessionId,turnId:e.turnId};return e.requestSummary===null?{...t,error:e.error}:{...t,details:e.modelCallDetails}}async function attemptUnsupportedProviderToolRecovery(e){let t=extractUnsupportedProviderToolTypes(e.error);if(t.length===0)return{outcome:`failed`,error:e.error};let n=[];for(let e of t){let t=resolveFrameworkToolFromUpstreamType(e);t!==null&&!n.includes(t)&&n.push(t)}if(n.length===0)return{outcome:`failed`,error:e.error};log.warn(`disabling unsupported provider tool(s); retrying step once`,{disabled:n,sessionId:e.sessionId,turnId:e.turnId,upstreamTypes:t});try{return{outcome:`recovered`,result:await e.runOneModelCall({disabledProviderTools:new Set(n),extraSystemNote:buildDisabledToolNote(n),suppressStepStartedEmission:!0})}}catch(e){return{outcome:`failed`,error:e}}}function buildDisabledToolNote(e){let t=e.join(`, `);return`The following ${e.length===1?`tool is`:`tools are`} not available with the current model and has been removed: ${t}. Proceed using the remaining tools or your training knowledge.`}async function handleStepResult(e){let{config:t,emit:n,promptMessages:r,result:i,runStep:a}=e,{emissionState:s,session:c}=e,u=i.response.messages,d=resolveAssistantStepText(u,i.text),f={...c,compaction:createNextCompactionConfig(c.compaction,r,i)};if(t.codeMode===!0){let{getCodeModeInterrupt:e,isCodeModeApprovalInterrupt:a}=await loadCodeModeModule(),o=e(i);if(o!==void 0){if(isCodeModeConnectionAuthInterrupt(o))return parkOnCodeModeConnectionAuth({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u});if(a(o))return parkOnCodeModeApproval({baseSession:f,config:t,emit:n,emissionState:s,interrupt:o,promptMessages:r,responseMessages:u})}}let p=extractToolApprovalInputRequests({content:i.content??[]}),m=new Set(p.map(e=>e.action.callId)),h=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:m}),_=[...p,...h],v=(i.toolCalls??[]).filter(e=>!isInvalidToolCall(e)).filter(e=>t.tools.get(e.toolName)?.runtimeAction!==void 0).map(e=>createRuntimeActionRequestFromToolCall({toolCall:e,tools:t.tools}));if(v.length>0)return{next:null,session:setHarnessEmissionState(setPendingRuntimeActionBatch({actions:v,event:{sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId},responseMessages:u,session:{...f,history:[...r]}}),s)};if(_.length>0){let e=setPendingInputBatch({requests:_,responseMessages:u,session:{...f,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:_,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId})),t.mode===`conversation`&&(s=await emitTurnEpilogue(n,s,t.mode),e=setHarnessEmissionState(e,s))),{next:null,session:e}}let y=(i.toolResults??[]).find(e=>isAuthorizationSignal(e.output));if(y&&isAuthorizationSignal(y.output)){let{challenges:e}=y.output;if(n)for(let t of e)await n(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:s.sequence,stepIndex:s.stepIndex,turnId:s.turnId}));return{next:null,session:setHarnessEmissionState({...f,history:[...r],state:setPendingAuthorization(f.state,{challenges:e})},s)}}let b=pruneToolResults(r,t.retentionPolicies),S=b!==r,C=f.compaction;S&&C.lastKnownInputTokens!==void 0&&(C={recentWindowSize:C.recentWindowSize,threshold:C.threshold});let w=[...b,...u],T={...f,compaction:C,history:w},D=u.at(-1)?.role===`tool`||hasDeferredStepInput(T);return n&&(s=D?advanceStep(s):await emitTurnEpilogue(n,s,t.mode),T=setHarnessEmissionState(T,s)),D?{next:a,session:T}:{next:t.mode===`task`?{done:!0,output:d??``}:null,session:T}}async function continuePendingCodeModeApproval(e){let t=getPendingCodeModeApproval(e.session.state);if(t===void 0)return null;let{continueCodeModeApproval:n,getCodeModeApprovalResponse:i,isCodeModeApprovalInterrupt:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=i([...e.messages],t.interrupt);if(s===void 0)return{next:null,session:e.session};let c=contextStorage.getStore(),l=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,discovered:c?.get(DiscoveredConnectionToolsKey),registry:c?.get(ConnectionRegistryKey),tools:e.config.tools}),d;try{d=await n({approvalResponse:s,interrupt:t.interrupt,options:createAshCodeModeOptions({lifecycle:e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools})}),tools:l})}catch(e){logError(log,`code-mode approval continuation failed`,e),d={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let f=o(d),m=f.status===`interrupted`?f.interrupt:f.output,h=replaceCodeModeApprovalInterruptResult([...e.session.history,...t.responseMessages],t.interrupt,m),g=clearPendingCodeModeApproval({...e.session,history:h});if(f.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(f.interrupt)){let t=e.session.history.length,n=h.slice(0,t),r=h.slice(t);return g={...g,history:n},parkOnCodeModeConnectionAuth({baseSession:g,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:f.interrupt,promptMessages:n,responseMessages:r})}if(a(f.interrupt)){let t=e.session.history.length,n=h.slice(0,t),r=h.slice(t);return g={...g,history:n},parkOnCodeModeApproval({baseSession:g,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:f.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:g}}async function parkOnCodeModeConnectionAuth(e){let{connectionName:t}=e.interrupt.payload,n=(contextStorage.getStore()?.get(ConnectionRegistryKey))?.getConnections().find(e=>e.connectionName===t),r=n?.authorization&&supportsInteractiveAuthorization(n.authorization)?n.authorization:void 0,i=[];if(r){let e=getHookUrl(t);if(e){let a=resolveConnectionPrincipal(t,r),{challenge:o,state:s}=await r.startAuthorization({callbackUrl:e,connection:{url:n?.url??``},principal:a});i.push({name:t,challenge:o,hookUrl:e,state:s})}}if(e.emit)for(let t of i)await e.emit(createAuthorizationRequiredEvent({authorization:t.challenge,name:t.name,description:t.challenge.instructions??`Authorization required for ${t.name}`,webhookUrl:t.hookUrl,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}));return{next:null,session:setPendingCodeModeConnectionAuth({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages],state:setPendingAuthorization(e.baseSession.state,{challenges:i})}})}}async function continuePendingCodeModeConnectionAuth(e){let t=getPendingCodeModeConnectionAuth(e.session.state);if(t===void 0)return null;let{continueCodeModeInterrupt:n,isCodeModeApprovalInterrupt:i,replaceCodeModeInterruptResult:a,unwrapCodeModeResult:o}=await loadCodeModeModule(),s=contextStorage.getStore(),c=new Set,{connectionName:l}=t.interrupt.payload;c.add(l);let d=await buildCodeModeHostTools({approvedTools:getApprovedTools(e.session),capabilities:e.capabilities,codeModeMetadataOnlyConnectionNames:c,discovered:s?.get(DiscoveredConnectionToolsKey),registry:s?.get(ConnectionRegistryKey),tools:e.config.tools}),f=e.emit===void 0?void 0:createCodeModeLifecycle({emit:e.emit,emissionState:e.emissionState,skipReplayed:!0,tools:e.config.tools}),m;try{m=await n({interrupt:t.interrupt,resolution:{status:`authorized`},tools:d,options:createAshCodeModeOptions({lifecycle:f})})}catch(e){logError(log,`code-mode interrupt continuation failed`,e),m={error:`code_mode_continuation_failed`,message:toErrorMessage(e),retryable:!1}}let h=o(m),g=h.status===`interrupted`?h.interrupt:h.output,_=a([...e.session.history,...t.responseMessages],t.interrupt.pendingContinuation,g),v=clearPendingCodeModeConnectionAuth({...e.session,history:_});if(h.status===`interrupted`){if(isCodeModeConnectionAuthInterrupt(h.interrupt)){let t=e.session.history.length,n=_.slice(0,t),r=_.slice(t);return v={...v,history:n},parkOnCodeModeConnectionAuth({baseSession:v,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:h.interrupt,promptMessages:n,responseMessages:r})}if(i(h.interrupt)){let t=e.session.history.length,n=_.slice(0,t),r=_.slice(t);return v={...v,history:n},parkOnCodeModeApproval({baseSession:v,config:e.config,emit:e.emit,emissionState:e.emissionState,interrupt:h.interrupt,promptMessages:n,responseMessages:r})}}return{next:e.runStep,session:v}}async function parkOnCodeModeApproval(e){let{toCodeModeApprovalMessages:t}=await loadCodeModeModule(),n=t(e.interrupt),r=extractToolApprovalInputRequests({content:extractAssistantContent(n)}),i=setPendingInputBatch({requests:r,responseMessages:n,session:setPendingCodeModeApproval({interrupt:e.interrupt,responseMessages:e.responseMessages,session:{...e.baseSession,history:[...e.promptMessages]}})});if(e.emit&&(await e.emit(createInputRequestedEvent({requests:r,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId})),e.config.mode===`conversation`)){let t=await emitTurnEpilogue(e.emit,e.emissionState,e.config.mode);i=setHarnessEmissionState(i,t)}return{next:null,session:i}}function extractAssistantContent(e){let t=[];for(let n of e)n.role===`assistant`&&Array.isArray(n.content)&&t.push(...n.content);return t}function createNextCompactionConfig(e,t,n){let r={recentWindowSize:e.recentWindowSize,threshold:e.threshold};return n.usage?.inputTokens!==void 0&&(r.lastKnownInputTokens=n.usage.inputTokens,r.lastKnownPromptMessageCount=t.length),r}async function maybeCompact(e){let{emit:t,emissionState:n}=e,r=e.messages,i=e.session;if(!shouldCompact(r,i.compaction))return{messages:r,session:i};let a=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,a.model,i.compaction,a.providerOptions,e.telemetry,e.headers),e.onCompaction){let t=await e.onCompaction(i);i=t.session;for(let e of t.messages)r.push(e)}return t&&await t(createCompactionCompletedEvent({modelId:formatLanguageModelGatewayId(a.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId})),{messages:r,session:i}}function resolveApprovalKeyFromTools(e){return t=>{let n=e.get(t.action.toolName);if(n?.approvalKey!==void 0)return n.approvalKey(t.action.input)}}async function runModelCallWithRetries(e,t){for(let n=1;;n++)try{return await e()}catch(e){if(n===3||classifyModelCallError(e)!==`retry`)throw e;let r=500*2**(n-1)+Math.floor(Math.random()*250);log.warn(`model call failed transiently — retrying`,{attempt:n,delayMs:r,sessionId:t.sessionId,turnId:t.turnId,error:e}),await new Promise(e=>setTimeout(e,r))}}export{createToolLoopHarness};
@@ -139,6 +139,15 @@ export type HarnessToolMap = ReadonlyMap<string, HarnessToolDefinition>;
139
139
  * events without knowing about writables or handlers.
140
140
  */
141
141
  export type HarnessEmitFn = (event: HandleMessageStreamEvent) => Promise<void>;
142
+ /**
143
+ * Unified event handler: emits the event to the stream, then
144
+ * dispatches to hook subscribers and dynamic tool resolvers.
145
+ *
146
+ * Same signature as {@link HarnessEmitFn} but semantically broader —
147
+ * every event goes through channel adapter, stream write, hooks,
148
+ * and dynamic tool dispatch in one call.
149
+ */
150
+ export type HandleEventFn = (event: HandleMessageStreamEvent) => Promise<void>;
142
151
  /**
143
152
  * Dependencies injected into the tool-loop harness at construction time.
144
153
  */
@@ -156,7 +165,7 @@ export interface ToolLoopHarnessConfig {
156
165
  * `ASH_EXPERIMENTAL_CODE_MODE` env backstop). Defaults to `false`.
157
166
  */
158
167
  readonly codeMode?: boolean;
159
- readonly emit?: HarnessEmitFn;
168
+ readonly handleEvent?: HandleEventFn;
160
169
  /**
161
170
  * Execution mode for the current harness.
162
171
  *
@@ -1 +1 @@
1
- import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.41.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
1
+ import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.42.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
@@ -1 +1 @@
1
- import{expectFunction,expectObjectRecord,expectOnlyKnownKeys,expectString}from"#internal/authored-module.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{isDisabledToolSentinel}from"#public/definitions/tool.js";import{isDynamicToolSentinel,isDynamicToolsSentinel}from"#shared/dynamic-tool-definition.js";function normalizeToolDefinition(t,n){if(isDynamicToolsSentinel(t))return{kind:`dynamic-tool`,identity:`multi`,eventNames:Object.keys(t.events)};if(isDynamicToolSentinel(t))return{kind:`dynamic-tool`,identity:`single`,eventNames:Object.keys(t.events)};if(isDisabledToolSentinel(t))return{kind:`disabled`};let r=expectObjectRecord(t,n);expectOnlyKnownKeys(r,[`description`,`execute`,`inputSchema`,`needsApproval`,`onCompact`,`retentionPolicy`,`toModelOutput`],n);let i=r.inputSchema===void 0?null:normalizeJsonSchemaDefinition(r.inputSchema),a={description:expectString(r.description,n),execute:expectFunction(r.execute,n),inputSchema:i};if(r.onCompact!==void 0&&expectFunction(r.onCompact,n),r.needsApproval!==void 0&&expectFunction(r.needsApproval,n),r.retentionPolicy!==void 0){let e=r.retentionPolicy;if(e!==`auto`&&e!==`keep`&&typeof e!=`function`)throw Error(`${n} Expected \`retentionPolicy\` to be "auto", "keep", or a function.`)}return r.toModelOutput!==void 0&&expectFunction(r.toModelOutput,n),{kind:`tool`,definition:a}}export{normalizeToolDefinition};
1
+ import{expectFunction,expectObjectRecord,expectOnlyKnownKeys,expectString}from"#internal/authored-module.js";import{isDynamicToolSentinel,isDynamicToolsSentinel}from"#shared/dynamic-tool-definition.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{isDisabledToolSentinel}from"#public/definitions/tool.js";function normalizeToolDefinition(t,n){if(isDynamicToolsSentinel(t))return{kind:`dynamic-tool`,identity:`multi`,eventNames:Object.keys(t.events)};if(isDynamicToolSentinel(t))return{kind:`dynamic-tool`,identity:`single`,eventNames:Object.keys(t.events)};if(isDisabledToolSentinel(t))return{kind:`disabled`};let r=expectObjectRecord(t,n);expectOnlyKnownKeys(r,[`description`,`execute`,`inputSchema`,`needsApproval`,`onCompact`,`retentionPolicy`,`toModelOutput`],n);let i=r.inputSchema===void 0?null:normalizeJsonSchemaDefinition(r.inputSchema),a={description:expectString(r.description,n),execute:expectFunction(r.execute,n),inputSchema:i};if(r.onCompact!==void 0&&expectFunction(r.onCompact,n),r.needsApproval!==void 0&&expectFunction(r.needsApproval,n),r.retentionPolicy!==void 0){let e=r.retentionPolicy;if(e!==`auto`&&e!==`keep`&&typeof e!=`function`)throw Error(`${n} Expected \`retentionPolicy\` to be "auto", "keep", or a function.`)}return r.toModelOutput!==void 0&&expectFunction(r.toModelOutput,n),{kind:`tool`,definition:a}}export{normalizeToolDefinition};
@@ -1,4 +1,4 @@
1
- import{loadNitroRolldownParseAst}from"#internal/bundler/nitro-rolldown.js";let transformCounter=0;async function transformDynamicToolExecute(e,t){if(!t.includes(`defineTools`)&&!t.includes(`defineTool`)||!t.includes(`events`)||!t.includes(`execute`))return null;let n=findDynamicToolHandlers(t,await parseSource(e,t));return n.every(e=>e.executes.length===0)?null:applyTransform(t,n)}async function parseSource(t,n){let{parseAst:r}=await loadNitroRolldownParseAst();return r(n,{astType:`ts`,lang:inferLang(t),range:!0,sourceType:`module`},t)}function inferLang(e){return e.endsWith(`.tsx`)?`tsx`:e.endsWith(`.jsx`)?`jsx`:e.endsWith(`.js`)||e.endsWith(`.mjs`)||e.endsWith(`.cjs`)?`js`:`ts`}function findDynamicToolHandlers(e,t){let n=[];return walkNode(t,t=>{if(t.type===`CallExpression`&&t.callee?.type===`Identifier`&&(t.callee.name===`defineTools`||t.callee.name===`defineTool`)&&t.arguments?.length===1){let r=t.arguments[0];if(r.type===`ObjectExpression`){let t=findProperty(r,`events`);t?.value&&t.value.type===`ObjectExpression`&&collectHandlers(e,t.value,n)}return!1}return!0}),n}function collectHandlers(e,t,n){for(let r of t.properties??[]){if(r.type!==`Property`)continue;let t=r.value;if(!t||t.type!==`ArrowFunctionExpression`&&t.type!==`FunctionExpression`)continue;let i=t.body;if(!i)continue;let a=findBlockBodyStart(i);if(a===null)continue;let o=extractParamNames(t),s=collectScopeVarDeclarations(i),c=findExecuteFunctions(e,i);c.length>0&&n.push({handlerNode:t,bodyStart:a,scopeVars:s,paramNames:o,executes:c})}}function findBlockBodyStart(e){return e.type===`BlockStatement`&&e.start!==void 0?e.start:typeof e.body==`object`&&!Array.isArray(e.body)&&e.body?.type===`BlockStatement`&&e.body.start!==void 0?e.body.start:null}function extractParamNames(e){let t=[];for(let n of e.params??[])n.type===`Identifier`&&n.name&&t.push(n.name);return t}function collectScopeVarDeclarations(e){let t=[];return collectVarsRecursive(e,t),t}function collectVarsRecursive(e,t){if(!e||e.type===`FunctionExpression`||e.type===`ArrowFunctionExpression`||e.type===`FunctionDeclaration`)return;if(e.type===`VariableDeclaration`)for(let n of e.declarations??[])collectDeclaredNames(n,t);e.type===`ForStatement`&&e.init&&collectVarsRecursive(e.init,t);let n=e;if((e.type===`ForInStatement`||e.type===`ForOfStatement`)&&e.left&&collectVarsRecursive(e.left,t),Array.isArray(e.body))for(let n of e.body)collectVarsRecursive(n,t);else e.body&&typeof e.body==`object`&&`type`in e.body&&collectVarsRecursive(e.body,t);if(e.declarations)for(let n of e.declarations)collectVarsRecursive(n,t);if(e.expression&&collectVarsRecursive(e.expression,t),Array.isArray(n.consequent))for(let e of n.consequent)collectVarsRecursive(e,t);else n.consequent&&collectVarsRecursive(n.consequent,t);if(n.alternate&&collectVarsRecursive(n.alternate,t),n.block&&collectVarsRecursive(n.block,t),n.handler&&collectVarsRecursive(n.handler,t),n.finalizer&&collectVarsRecursive(n.finalizer,t),n.cases&&Array.isArray(n.cases))for(let e of n.cases)collectVarsRecursive(e,t)}function collectDeclaredNames(e,t){e.type===`VariableDeclarator`&&collectPatternNames(e.id,t)}function collectPatternNames(e,t){if(e){if(e.type===`Identifier`&&e.name){t.push(e.name);return}if(e.type===`ObjectPattern`)for(let n of e.properties??[])n.type===`Property`?collectPatternNames(n.value,t):n.type===`RestElement`&&collectPatternNames(n.argument,t);if(e.type===`ArrayPattern`)for(let n of e.elements??[])n&&collectPatternNames(n,t)}}function findExecuteFunctions(e,t){let n=[];return walkForExecuteProps(e,t,n,[]),n}function walkForExecuteProps(e,n,r,i){if(!n)return;if(n.type===`FunctionExpression`||n.type===`ArrowFunctionExpression`||n.type===`FunctionDeclaration`){let t=extractParamNames(n),a=n.body;if(!a)return;let o=collectScopeVarDeclarations(a),s=[...i,{params:t,vars:o}];a.type,walkForExecuteProps(e,a,r,s);return}if(n.type===`ObjectExpression`){for(let a of n.properties??[])if(a.type===`Property`&&!a.computed&&a.key?.type===`Identifier`&&a.key.name===`execute`&&a.start!==void 0&&a.end!==void 0){let n=a.value;if(!n||n.start===void 0||n.end===void 0)continue;let o=n.type===`FunctionExpression`||n.type===`ArrowFunctionExpression`,s=a.method===!0;if(o||s){let o=extractFnParams(e,n),s=extractFnBody(e,n),c=n.async===!0;r.push({propStart:a.start,propEnd:a.end,fnSource:e.slice(n.start,n.end),isAsync:c,params:o,body:s,hoistedName:`__ash_dynamic_exec_${transformCounter++}`,nestedScopes:i})}}}let walk=t=>walkForExecuteProps(e,t,r,i);if(Array.isArray(n.body))for(let e of n.body)walk(e);else n.body&&typeof n.body==`object`&&`type`in n.body&&walk(n.body);if(n.properties)for(let e of n.properties)e.value&&typeof e.value==`object`&&`type`in e.value&&walk(e.value);if(n.callee&&walk(n.callee),n.arguments)for(let e of n.arguments)walk(e);if(n.expression&&walk(n.expression),n.argument&&walk(n.argument),n.init&&walk(n.init),n.left&&walk(n.left),n.right&&walk(n.right),n.declarations)for(let e of n.declarations)walk(e);let a=n;if(Array.isArray(a.consequent))for(let e of a.consequent)walk(e);else a.consequent&&walk(a.consequent);if(a.alternate&&walk(a.alternate),a.block&&walk(a.block),a.handler&&walk(a.handler),a.finalizer&&walk(a.finalizer),a.cases&&Array.isArray(a.cases))for(let e of a.cases)walk(e)}function extractFnParams(e,t){if(!t.params||t.params.length===0)return``;let n=t.params[0],r=t.params[t.params.length-1];return n.start===void 0||r.end===void 0?``:e.slice(n.start,r.end)}function extractFnBody(e,t){let n=t.body;if(!n||n.start===void 0||n.end===void 0)return`{}`;let r=e.slice(n.start,n.end);return t.type===`ArrowFunctionExpression`&&n.type!==`BlockStatement`?`{ return ${r}; }`:r}function applyTransform(e,t){let n=[],r=[],i=[],a=[];for(let e of t)for(let t of e.executes){let o=[...e.paramNames,...e.scopeVars,...t.nestedScopes.flatMap(e=>[...e.params,...e.vars])],s=new Set,c=[];for(let e=o.length-1;e>=0;e--)s.has(o[e])||(s.add(o[e]),c.unshift(o[e]));let l=t.body,u=extractExecuteParamNames(t.params),d=c.filter(e=>!u.has(e)&&RegExp(`\\b${escapeForRegex(e)}\\b`).test(l)),f=d.length>0?`{ ${d.join(`, `)} }`:`{}`,p=t.isAsync?`async `:``,m=d.length>0?`const ${f} = __vars;\n `:``,h=t.params,g=h?`__vars, ${h}`:`__vars`,_=t.body.slice(1,-1).trim(),v=`ash:dynamic-tool//${t.hoistedName}`;r.push(`${p}function ${t.hoistedName}(${g}) {\n ${m}${_}\n}`),i.push(`${t.hoistedName}.stepId = ${JSON.stringify(v)};`),i.push(`__ashStepRegistry.set(${JSON.stringify(v)}, ${t.hoistedName});`),a.push(t.hoistedName);let y=h||``,b=h?splitParamsTopLevel(h).map(e=>extractParamBindingName(e)).join(`, `):``,x=b?`${f}, ${b}`:f,S=t.isAsync?`async `:``,C=t.isAsync?`await `:``;n.push({start:t.propStart,end:t.propEnd,text:[`execute: ${S}(${y}) => ${C}${t.hoistedName}(${x})`,`__executeStepFn: ${t.hoistedName}`,`__closureVars: ${f}`].join(`,
1
+ import{loadNitroRolldownParseAst}from"#internal/bundler/nitro-rolldown.js";let transformCounter=0;async function transformDynamicToolExecute(e,t){if(!t.includes(`defineTools`)&&!t.includes(`defineTool`)||!t.includes(`events`)||!t.includes(`execute`))return null;let n=findDynamicToolHandlers(t,await parseSource(e,t));return n.every(e=>e.executes.length===0)?null:applyTransform(t,n)}async function parseSource(t,n){let{parseAst:r}=await loadNitroRolldownParseAst();return r(n,{astType:`ts`,lang:inferLang(t),range:!0,sourceType:`module`},t)}function inferLang(e){return e.endsWith(`.tsx`)?`tsx`:e.endsWith(`.jsx`)?`jsx`:e.endsWith(`.js`)||e.endsWith(`.mjs`)||e.endsWith(`.cjs`)?`js`:`ts`}function findDynamicToolHandlers(e,t){let n=[];return walkNode(t,t=>{if(t.type===`CallExpression`&&t.callee?.type===`Identifier`&&(t.callee.name===`defineTools`||t.callee.name===`defineTool`)&&t.arguments?.length===1){let r=t.arguments[0];if(r.type===`ObjectExpression`){let t=findProperty(r,`events`);t?.value&&t.value.type===`ObjectExpression`&&collectHandlers(e,t.value,n)}return!1}return!0}),n}function collectHandlers(e,t,n){for(let r of t.properties??[]){if(r.type!==`Property`)continue;let t=r.value;if(!t||t.type!==`ArrowFunctionExpression`&&t.type!==`FunctionExpression`)continue;let i=t.body;if(!i)continue;let a=findBlockBodyStart(i);if(a===null)continue;let o=extractParamNames(t),s=collectScopeVarDeclarations(i),c=findExecuteFunctions(e,i);c.length>0&&n.push({handlerNode:t,bodyStart:a,scopeVars:s,paramNames:o,executes:c})}}function findBlockBodyStart(e){return e.type===`BlockStatement`&&e.start!==void 0?e.start:typeof e.body==`object`&&!Array.isArray(e.body)&&e.body?.type===`BlockStatement`&&e.body.start!==void 0?e.body.start:null}function extractParamNames(e){let t=[];for(let n of e.params??[])n.type===`Identifier`&&n.name&&t.push(n.name);return t}function collectScopeVarDeclarations(e){let t=[];return collectVarsRecursive(e,t),t}function collectVarsRecursive(e,t){if(!e||e.type===`FunctionExpression`||e.type===`ArrowFunctionExpression`||e.type===`FunctionDeclaration`)return;if(e.type===`VariableDeclaration`)for(let n of e.declarations??[])collectDeclaredNames(n,t);e.type===`ForStatement`&&e.init&&collectVarsRecursive(e.init,t);let n=e;if((e.type===`ForInStatement`||e.type===`ForOfStatement`)&&e.left&&collectVarsRecursive(e.left,t),Array.isArray(e.body))for(let n of e.body)collectVarsRecursive(n,t);else e.body&&typeof e.body==`object`&&`type`in e.body&&collectVarsRecursive(e.body,t);if(e.declarations)for(let n of e.declarations)collectVarsRecursive(n,t);if(e.expression&&collectVarsRecursive(e.expression,t),Array.isArray(n.consequent))for(let e of n.consequent)collectVarsRecursive(e,t);else n.consequent&&collectVarsRecursive(n.consequent,t);if(n.alternate&&collectVarsRecursive(n.alternate,t),n.block&&collectVarsRecursive(n.block,t),n.handler&&collectVarsRecursive(n.handler,t),n.finalizer&&collectVarsRecursive(n.finalizer,t),n.cases&&Array.isArray(n.cases))for(let e of n.cases)collectVarsRecursive(e,t)}function collectDeclaredNames(e,t){e.type===`VariableDeclarator`&&collectPatternNames(e.id,t)}function collectPatternNames(e,t){if(e){if(e.type===`Identifier`&&e.name){t.push(e.name);return}if(e.type===`ObjectPattern`)for(let n of e.properties??[])n.type===`Property`?collectPatternNames(n.value,t):n.type===`RestElement`&&collectPatternNames(n.argument,t);if(e.type===`ArrayPattern`)for(let n of e.elements??[])n&&collectPatternNames(n,t)}}function findExecuteFunctions(e,t){let n=[];return walkForExecuteProps(e,t,n,[]),n}function walkForExecuteProps(e,n,r,i){if(!n)return;if(n.type===`FunctionExpression`||n.type===`ArrowFunctionExpression`||n.type===`FunctionDeclaration`){let t=extractParamNames(n),a=n.body;if(!a)return;let o=collectScopeVarDeclarations(a),s=[...i,{params:t,vars:o}];a.type,walkForExecuteProps(e,a,r,s);return}if(n.type===`CallExpression`&&n.callee?.type===`Identifier`&&n.callee.name===`tool`&&n.arguments?.length===1&&n.arguments[0].type===`ObjectExpression`){let a=n.arguments[0];for(let n of a.properties??[])if(n.type===`Property`&&!n.computed&&n.key?.type===`Identifier`&&n.key.name===`execute`&&n.start!==void 0&&n.end!==void 0){let a=n.value;if(!a||a.start===void 0||a.end===void 0)continue;let o=a.type===`FunctionExpression`||a.type===`ArrowFunctionExpression`,s=n.method===!0;if(o||s){let o=extractFnParams(e,a),s=extractFnBody(e,a),c=a.async===!0;r.push({propStart:n.start,propEnd:n.end,fnSource:e.slice(a.start,a.end),isAsync:c,params:o,body:s,hoistedName:`__ash_dynamic_exec_${transformCounter++}`,nestedScopes:i})}}return}let walk=t=>walkForExecuteProps(e,t,r,i);if(Array.isArray(n.body))for(let e of n.body)walk(e);else n.body&&typeof n.body==`object`&&`type`in n.body&&walk(n.body);if(n.properties)for(let e of n.properties)e.value&&typeof e.value==`object`&&`type`in e.value&&walk(e.value);if(n.callee&&walk(n.callee),n.arguments)for(let e of n.arguments)walk(e);if(n.expression&&walk(n.expression),n.argument&&walk(n.argument),n.init&&walk(n.init),n.left&&walk(n.left),n.right&&walk(n.right),n.declarations)for(let e of n.declarations)walk(e);let a=n;if(Array.isArray(a.consequent))for(let e of a.consequent)walk(e);else a.consequent&&walk(a.consequent);if(a.alternate&&walk(a.alternate),a.block&&walk(a.block),a.handler&&walk(a.handler),a.finalizer&&walk(a.finalizer),a.cases&&Array.isArray(a.cases))for(let e of a.cases)walk(e)}function extractFnParams(e,t){if(!t.params||t.params.length===0)return``;let n=t.params[0],r=t.params[t.params.length-1];return n.start===void 0||r.end===void 0?``:e.slice(n.start,r.end)}function extractFnBody(e,t){let n=t.body;if(!n||n.start===void 0||n.end===void 0)return`{}`;let r=e.slice(n.start,n.end);return t.type===`ArrowFunctionExpression`&&n.type!==`BlockStatement`?`{ return ${r}; }`:r}function applyTransform(e,t){let n=[],r=[],i=[],a=[];for(let e of t)for(let t of e.executes){let o=[...e.paramNames,...e.scopeVars,...t.nestedScopes.flatMap(e=>[...e.params,...e.vars])],s=new Set,c=[];for(let e=o.length-1;e>=0;e--)s.has(o[e])||(s.add(o[e]),c.unshift(o[e]));let l=t.body,u=extractExecuteParamNames(t.params),d=c.filter(e=>!u.has(e)&&RegExp(`\\b${escapeForRegex(e)}\\b`).test(l)),f=d.length>0?`{ ${d.join(`, `)} }`:`{}`,p=t.isAsync?`async `:``,m=d.length>0?`const ${f} = __vars;\n `:``,h=t.params,g=h?`__vars, ${h}`:`__vars`,_=t.body.slice(1,-1).trim(),v=`ash:dynamic-tool//${t.hoistedName}`;r.push(`${p}function ${t.hoistedName}(${g}) {\n ${m}${_}\n}`),i.push(`${t.hoistedName}.stepId = ${JSON.stringify(v)};`),i.push(`__ashStepRegistry.set(${JSON.stringify(v)}, ${t.hoistedName});`),a.push(t.hoistedName);let y=h||``,b=h?splitParamsTopLevel(h).map(e=>extractParamBindingName(e)).join(`, `):``,x=b?`${f}, ${b}`:f,S=t.isAsync?`async `:``,C=t.isAsync?`await `:``;n.push({start:t.propStart,end:t.propEnd,text:[`execute: ${S}(${y}) => ${C}${t.hoistedName}(${x})`,`__executeStepFn: ${t.hoistedName}`,`__closureVars: ${f}`].join(`,
2
2
  `)})}let o=[...n].sort((e,t)=>t.start-e.start),s=e;for(let e of o)s=s.slice(0,e.start)+e.text+s.slice(e.end);s=`${[`var __ashStepRegistrySym = Symbol.for("@workflow/core//registeredSteps");`,`if (!globalThis[__ashStepRegistrySym]) globalThis[__ashStepRegistrySym] = new Map();`,`var __ashStepRegistry = globalThis[__ashStepRegistrySym];`].join(`
3
3
  `)}\n${s}`;let c=[...r,...i];return c.length>0&&(s=`${s}\n\n${c.join(`
4
4
  `)}\n`),{code:s}}function walkNode(e,t){if(t(e)){if(Array.isArray(e.body))for(let n of e.body)walkNode(n,t);else e.body&&typeof e.body==`object`&&`type`in e.body&&walkNode(e.body,t);if(e.declarations)for(let n of e.declarations)walkNode(n,t);if(e.init&&walkNode(e.init,t),e.expression&&walkNode(e.expression,t),e.declaration&&walkNode(e.declaration,t),e.argument&&walkNode(e.argument,t),e.arguments)for(let n of e.arguments)walkNode(n,t);if(e.properties)for(let n of e.properties)walkNode(n,t),n.value&&typeof n.value==`object`&&`type`in n.value&&walkNode(n.value,t);e.left&&walkNode(e.left,t),e.right&&walkNode(e.right,t)}}function findProperty(e,t){return e.properties?.find(e=>e.type===`Property`&&!e.computed&&e.key?.type===`Identifier`&&e.key.name===t)}function escapeForRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function splitParamsTopLevel(e){let t=[],n=0,r=0;for(let i=0;i<e.length;i++){let a=e[i];a===`<`||a===`(`||a===`[`||a===`{`?n++:a===`>`||a===`)`||a===`]`||a===`}`?n--:a===`,`&&n===0&&(t.push(e.slice(r,i)),r=i+1)}return t.push(e.slice(r)),t}function extractParamBindingName(e){let t=e.trim(),n=0;for(let e=0;e<t.length;e++){let r=t[e];if(r===`<`||r===`(`||r===`[`||r===`{`)n++;else if(r===`>`||r===`)`||r===`]`||r===`}`)n--;else if(n===0&&(r===`:`||r===`=`))return t.slice(0,e).trim()}return t}function extractExecuteParamNames(e){if(!e)return new Set;let t=new Set;for(let n of splitParamsTopLevel(e)){let e=extractParamBindingName(n);e&&t.add(e)}return t}export{transformDynamicToolExecute as transformDynamicToolAwait,transformDynamicToolExecute};
@@ -1,4 +1,4 @@
1
- import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.41.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.159`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
1
+ import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.42.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.159`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
2
2
  import { slackChannel } from "experimental-ash/channels/slack";
3
3
 
4
4
  export default slackChannel({
@@ -1,13 +1,15 @@
1
1
  import type { PublicToolInputSchema } from "#shared/tool-definition.js";
2
2
  import type { SessionContext } from "#public/definitions/callback-context.js";
3
3
  import type { SessionAuth } from "#context/keys.js";
4
+ import type { HandleMessageStreamEvent } from "#protocol/message.js";
4
5
  type ToolContext = SessionContext;
5
6
  /**
6
- * Event names supported by dynamic tool resolvers. Initially
7
- * `"session.started"` and `"step.started"`; more events can be
8
- * added by expanding this union.
7
+ * Subset of stream event types allowed for dynamic tool resolvers.
8
+ * The dispatch architecture supports any event — this extract
9
+ * restricts the public API surface until more events are validated.
9
10
  */
10
- export type DynamicToolEventName = "session.started" | "turn.started" | "step.started";
11
+ export type DynamicToolEventName = Extract<HandleMessageStreamEvent["type"], "session.started" | "turn.started" | "step.started">;
12
+ export declare const ALLOWED_DYNAMIC_TOOL_EVENTS: ReadonlySet<string>;
11
13
  /**
12
14
  * Context passed to a dynamic tool resolver's event handler.
13
15
  *
@@ -1 +1 @@
1
- const 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{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_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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-ash",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "bin": {
5
5
  "ash": "./bin/ash.js",
6
6
  "experimental-ash": "./bin/ash.js"