experimental-ash 0.28.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +1 -0
  3. package/dist/docs/public/cli-build-and-debugging.md +2 -0
  4. package/dist/docs/public/getting-started.md +1 -0
  5. package/dist/src/cli/run.d.ts +9 -0
  6. package/dist/src/cli/run.js +1 -1
  7. package/dist/src/context/hook-lifecycle.d.ts +23 -67
  8. package/dist/src/context/hook-lifecycle.js +1 -1
  9. package/dist/src/execution/await-authorization-orchestrator.d.ts +18 -29
  10. package/dist/src/execution/await-authorization-orchestrator.js +1 -1
  11. package/dist/src/execution/connection-auth-steps.d.ts +28 -65
  12. package/dist/src/execution/connection-auth-steps.js +1 -1
  13. package/dist/src/execution/create-session-step.d.ts +15 -0
  14. package/dist/src/execution/create-session-step.js +1 -0
  15. package/dist/src/execution/delegated-parent-notification.d.ts +15 -0
  16. package/dist/src/execution/delegated-parent-notification.js +1 -0
  17. package/dist/src/execution/delegated-parent-result.d.ts +14 -0
  18. package/dist/src/execution/delegated-parent-result.js +1 -0
  19. package/dist/src/execution/dispatch-runtime-actions-step.d.ts +20 -0
  20. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -0
  21. package/dist/src/execution/durable-session-migrations/chain.d.ts +31 -0
  22. package/dist/src/execution/durable-session-migrations/chain.js +1 -0
  23. package/dist/src/execution/durable-session-migrations/snapshot.d.ts +22 -0
  24. package/dist/src/execution/durable-session-migrations/snapshot.js +1 -0
  25. package/dist/src/execution/durable-session-store.d.ts +104 -0
  26. package/dist/src/execution/durable-session-store.js +1 -0
  27. package/dist/src/execution/next-driver-action.d.ts +30 -0
  28. package/dist/src/execution/next-driver-action.js +1 -0
  29. package/dist/src/execution/sandbox/prewarm.d.ts +12 -2
  30. package/dist/src/execution/sandbox/prewarm.js +1 -1
  31. package/dist/src/execution/session.d.ts +29 -28
  32. package/dist/src/execution/session.js +1 -1
  33. package/dist/src/execution/subagent-hitl-proxy.d.ts +8 -25
  34. package/dist/src/execution/subagent-hitl-proxy.js +1 -1
  35. package/dist/src/execution/subagent-tool.js +1 -1
  36. package/dist/src/execution/turn-workflow.d.ts +21 -21
  37. package/dist/src/execution/turn-workflow.js +1 -1
  38. package/dist/src/execution/workflow-entry.d.ts +12 -16
  39. package/dist/src/execution/workflow-entry.js +1 -1
  40. package/dist/src/execution/workflow-runtime.js +1 -1
  41. package/dist/src/execution/workflow-steps.d.ts +36 -91
  42. package/dist/src/execution/workflow-steps.js +1 -1
  43. package/dist/src/harness/emission.d.ts +3 -5
  44. package/dist/src/harness/emission.js +1 -1
  45. package/dist/src/harness/input-requests.d.ts +3 -3
  46. package/dist/src/harness/input-requests.js +1 -1
  47. package/dist/src/harness/proxy-input-requests.d.ts +12 -16
  48. package/dist/src/harness/proxy-input-requests.js +1 -1
  49. package/dist/src/harness/runtime-actions.d.ts +17 -24
  50. package/dist/src/harness/runtime-actions.js +1 -1
  51. package/dist/src/harness/tool-loop.js +1 -1
  52. package/dist/src/harness/types.d.ts +3 -1
  53. package/dist/src/internal/application/package.js +1 -1
  54. package/dist/src/internal/nitro/host/start-production-server.d.ts +8 -0
  55. package/dist/src/internal/nitro/host/start-production-server.js +3 -0
  56. package/dist/src/internal/nitro/host/types.d.ts +8 -0
  57. package/dist/src/internal/nitro/host.d.ts +2 -1
  58. package/dist/src/internal/nitro/host.js +1 -1
  59. package/dist/src/internal/nitro/routes/agent-info/load-agent-info-data.js +1 -1
  60. package/dist/src/internal/nitro/routes/info.js +1 -1
  61. package/dist/src/internal/workflow-bundle/builder.d.ts +2 -0
  62. package/dist/src/internal/workflow-bundle/builder.js +1 -1
  63. package/dist/src/runtime/cache-key.js +1 -1
  64. package/dist/src/runtime/framework-channels/index.js +1 -1
  65. package/dist/src/runtime/loaders/bundled-artifacts.d.ts +12 -0
  66. package/dist/src/runtime/loaders/bundled-artifacts.js +1 -1
  67. package/dist/src/runtime/loaders/compile-metadata.js +1 -1
  68. package/dist/src/runtime/loaders/manifest.js +1 -1
  69. package/dist/src/runtime/loaders/module-map.js +1 -1
  70. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
- import{RuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{serializeContext}from"#context/serialize.js";import{getHookByToken,getRun,resumeHook,start}from"#compiled/@workflow/core/runtime.js";import{HookNotFoundError}from"#compiled/@workflow/errors/index.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{buildRunContext}from"#execution/runtime-context.js";const WORKFLOW_ENTRY_NAME=`workflowEntry`,TURN_WORKFLOW_NAME=`turnWorkflow`,ASH_PACKAGE_INFO=resolveInstalledPackageInfo(),LATEST_DEPLOYMENT_UNSUPPORTED_MESSAGE=`deploymentId 'latest' requires a World that implements resolveLatestDeploymentId()`,STABLE_WORKFLOW_NAMES=new Set([WORKFLOW_ENTRY_NAME,TURN_WORKFLOW_NAME]),STABLE_ID_BASE=ASH_PACKAGE_INFO.name,workflowEntryReference={workflowId:`workflow//${STABLE_ID_BASE}//${WORKFLOW_ENTRY_NAME}`},turnWorkflowReference={workflowId:`workflow//${STABLE_ID_BASE}//${TURN_WORKFLOW_NAME}`};function createWorkflowRuntime(t){return{async run(e){let r=serializeContext(buildRunContext({bundle:await getCompiledRuntimeAgentBundle({compiledArtifactsSource:t.compiledArtifactsSource,nodeId:t.nodeId}),run:e})),a=await startWorkflowPreferLatest(workflowEntryReference,[{input:e.input,serializedContext:r}]);return{continuationToken:e.continuationToken??a.runId,events:parseNdjsonStream(getRun(a.runId).getReadable()),sessionId:a.runId}},async deliver(t){let n={auth:t.auth,kind:`deliver`,payloads:[t.payload]};try{let e=normalizeWorkflowHook(await getHookByToken(t.continuationToken));return await resumeHook(t.continuationToken,n),{sessionId:e.runId}}catch(n){throw HookNotFoundError.is(n)?new RuntimeNoActiveSessionError(t.continuationToken):n}},async getEventStream(e,t){return parseNdjsonStream(getRun(e).getReadable({startIndex:t?.startIndex}))}}}async function startWorkflowPreferLatest(e,t){try{return await start(e,t,{deploymentId:`latest`})}catch(n){if(!isLatestDeploymentUnsupportedError(n))throw n;return await start(e,t)}}function isLatestDeploymentUnsupportedError(e){return e instanceof Error&&e.message.includes(`deploymentId 'latest' requires a World that implements resolveLatestDeploymentId()`)}function normalizeWorkflowHook(e){if(typeof e!=`object`||!e||!(`runId`in e))throw Error(`Workflow hook did not include a run id.`);let t=e.runId;if(typeof t!=`string`||t.length===0)throw Error(`Workflow hook did not include a run id.`);return{runId:t}}function parseNdjsonStream(e){let t=new TextDecoder,n=``;return new ReadableStream({async start(r){let i=e.getReader();try{for(;;){let{value:e,done:a}=await i.read();if(a){let e=n.trim();e.length>0&&r.enqueue(JSON.parse(e)),r.close();return}n+=t.decode(e,{stream:!0});for(let e=n.indexOf(`
1
+ import{RuntimeNoActiveSessionError}from"#execution/runtime-errors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{serializeContext}from"#context/serialize.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{getHookByToken,getRun,resumeHook,start}from"#compiled/@workflow/core/runtime.js";import{HookNotFoundError}from"#compiled/@workflow/errors/index.js";import{buildRunContext}from"#execution/runtime-context.js";const WORKFLOW_ENTRY_NAME=`workflowEntry`,TURN_WORKFLOW_NAME=`turnWorkflow`,ASH_PACKAGE_INFO=resolveInstalledPackageInfo(),LATEST_DEPLOYMENT_UNSUPPORTED_MESSAGE=`deploymentId 'latest' requires a World that implements resolveLatestDeploymentId()`,STABLE_WORKFLOW_NAMES=new Set([WORKFLOW_ENTRY_NAME,TURN_WORKFLOW_NAME]),STABLE_ID_BASE=ASH_PACKAGE_INFO.name,workflowEntryReference={workflowId:`workflow//${STABLE_ID_BASE}//${WORKFLOW_ENTRY_NAME}`},turnWorkflowReference={workflowId:`workflow//${STABLE_ID_BASE}//${TURN_WORKFLOW_NAME}`};function createWorkflowRuntime(t){return{async run(e){let i=serializeContext(buildRunContext({bundle:await getCompiledRuntimeAgentBundle({compiledArtifactsSource:t.compiledArtifactsSource,nodeId:t.nodeId}),run:e})),o=await startWorkflowPreferLatest(workflowEntryReference,[{input:e.input,serializedContext:i}]);return{continuationToken:e.continuationToken??o.runId,events:parseNdjsonStream(getRun(o.runId).getReadable()),sessionId:o.runId}},async deliver(t){let n={auth:t.auth,kind:`deliver`,payloads:[t.payload]};try{let e=normalizeWorkflowHook(await getHookByToken(t.continuationToken));return await resumeHook(t.continuationToken,n),{sessionId:e.runId}}catch(n){throw HookNotFoundError.is(n)?new RuntimeNoActiveSessionError(t.continuationToken):n}},async getEventStream(e,t){return parseNdjsonStream(getRun(e).getReadable({startIndex:t?.startIndex}))}}}async function startWorkflowPreferLatest(e,t){try{return await start(e,t,{deploymentId:`latest`})}catch(n){if(!isLatestDeploymentUnsupportedError(n))throw n;return await start(e,t)}}function isLatestDeploymentUnsupportedError(e){return e instanceof Error&&e.message.includes(`deploymentId 'latest' requires a World that implements resolveLatestDeploymentId()`)}function normalizeWorkflowHook(e){if(typeof e!=`object`||!e||!(`runId`in e))throw Error(`Workflow hook did not include a run id.`);let t=e.runId;if(typeof t!=`string`||t.length===0)throw Error(`Workflow hook did not include a run id.`);return{runId:t}}function parseNdjsonStream(e){let t=new TextDecoder,n=``;return new ReadableStream({async start(r){let i=e.getReader();try{for(;;){let{value:e,done:a}=await i.read();if(a){let e=n.trim();e.length>0&&r.enqueue(JSON.parse(e)),r.close();return}n+=t.decode(e,{stream:!0});for(let e=n.indexOf(`
2
2
  `);e!==-1;e=n.indexOf(`
3
3
  `)){let t=n.slice(0,e).trim();n=n.slice(e+1),t.length>0&&r.enqueue(JSON.parse(t))}}}catch(e){r.error(e)}finally{i.releaseLock()}}})}export{LATEST_DEPLOYMENT_UNSUPPORTED_MESSAGE,STABLE_WORKFLOW_NAMES,createWorkflowRuntime,startWorkflowPreferLatest,turnWorkflowReference,workflowEntryReference};
@@ -1,144 +1,89 @@
1
1
  import type { DeliverPayload, HookPayload, SessionAuthContext, SubagentInputRequestHookPayload } from "#channel/types.js";
2
2
  import { deserializeContext } from "#context/serialize.js";
3
3
  import type { HarnessSession } from "#harness/types.js";
4
- import type { RuntimeCompiledArtifactsSource } from "#runtime/compiled-artifacts-source.js";
5
4
  import { type PendingConnectionToolCall } from "#runtime/framework-tools/pending-connection-tool-calls.js";
5
+ import { type DurableSessionSnapshot, type DurableSessionState } from "#execution/durable-session-store.js";
6
6
  import { type TurnWorkflowInput } from "#execution/turn-workflow.js";
7
- import type { RuntimeSubagentResultActionResult } from "#runtime/actions/types.js";
8
7
  /**
9
- * Serializable projection of a step result for workflow persistence.
8
+ * Result of one durable harness step, consumed by the turn workflow.
9
+ *
10
+ * `park` carries `hasPendingInputBatch` and `pendingRuntimeActionKeys`
11
+ * so the turn workflow can pick the right
12
+ * {@link import("#execution/next-driver-action.js").NextDriverAction}
13
+ * arm without re-reading the session.
10
14
  */
11
15
  export type DurableStepResult = {
12
- readonly action: "continue" | "park" | "done";
16
+ readonly action: "continue" | "done";
13
17
  readonly output?: string;
14
18
  readonly serializedContext: Record<string, unknown>;
15
- readonly session: HarnessSession;
19
+ readonly sessionState: DurableSessionState;
20
+ } | {
21
+ readonly action: "park";
22
+ readonly hasPendingInputBatch: boolean;
23
+ readonly pendingRuntimeActionKeys?: readonly string[];
24
+ readonly serializedContext: Record<string, unknown>;
25
+ readonly sessionState: DurableSessionState;
16
26
  } | {
17
27
  readonly action: "await-authorization";
18
28
  readonly pendingToolCalls: readonly PendingConnectionToolCall[];
19
29
  readonly serializedContext: Record<string, unknown>;
20
- readonly session: HarnessSession;
30
+ readonly sessionState: DurableSessionState;
21
31
  };
22
- /**
23
- * Input for one atomic harness step inside a durable `"use step"`
24
- * boundary.
25
- */
32
+ /** Input for one atomic harness step inside a durable `"use step"`. */
26
33
  export interface TurnStepInput {
27
34
  readonly input: HookPayload | undefined;
28
35
  readonly parentWritable: WritableStream<Uint8Array>;
29
36
  readonly serializedContext: Record<string, unknown>;
30
- readonly session: HarnessSession;
37
+ readonly sessionState: DurableSessionState;
38
+ readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
31
39
  }
32
40
  /**
33
41
  * Runs one atomic harness step inside a durable `"use step"` boundary.
34
42
  */
35
43
  export declare function turnStep(input: TurnStepInput): Promise<DurableStepResult>;
36
44
  /**
37
- * Reconciles `session.continuationToken` with the live
38
- * `ContinuationTokenKey` value in context.
39
- *
40
- * Channels mutate the token by calling
41
- * `ctx.session.setContinuationToken(...)`, which writes through to
42
- * `ContinuationTokenKey`. The serialized context picks the new value
43
- * up automatically, but the in-memory {@link HarnessSession} the
44
- * workflow body carries needs to be re-stamped so its `parkToken`
45
- * read after the step reflects the re-key.
46
- *
47
- * Exported for unit testing; the workflow runtime is the only
48
- * production caller.
45
+ * Re-stamps `session.continuationToken` from `ContinuationTokenKey`
46
+ * after channels call `setContinuationToken(...)`. Idempotent when the
47
+ * token is unchanged.
49
48
  */
50
49
  export declare function reconcileSessionContinuationToken(ctx: Awaited<ReturnType<typeof deserializeContext>>, session: HarnessSession): HarnessSession;
51
- /**
52
- * Starts every pending runtime action for the parked parent session.
53
- *
54
- * This currently supports delegated subagent calls only. Each child run starts
55
- * in task mode, emits a parent `subagent.called` control-plane event, and then
56
- * runs independently on its own public child stream.
57
- */
58
- export declare function dispatchPendingRuntimeActionsStep(input: {
59
- readonly callbackBaseUrl?: string;
60
- readonly parentWritable: WritableStream<Uint8Array>;
61
- readonly serializedContext: Record<string, unknown>;
62
- readonly session: HarnessSession;
63
- }): Promise<{
64
- readonly results: readonly RuntimeSubagentResultActionResult[];
65
- readonly session: HarnessSession;
66
- }>;
67
- /**
68
- * Emits a terminal `session.failed` event and delivers it to the
69
- * adapter before the workflow run tears down.
70
- */
50
+ /** Emits a terminal `session.failed` to the adapter and durable stream. */
71
51
  export declare function emitTerminalSessionFailureStep(input: {
72
52
  readonly error: unknown;
73
53
  readonly parentWritable: WritableStream<Uint8Array>;
74
54
  readonly serializedContext: Record<string, unknown>;
75
55
  }): Promise<void>;
76
- /**
77
- * Outcome of {@link runProxyInputRequestStep}, including the session
78
- * and serialized context after adapter state mutations are persisted.
79
- */
80
- export interface RunProxyInputRequestResult {
56
+ export interface ProxyInputRequestResult {
81
57
  readonly serializedContext: Record<string, unknown>;
82
- readonly session: HarnessSession;
58
+ readonly sessionState: DurableSessionState;
83
59
  }
84
60
  /**
85
61
  * Emits a proxied `input.requested` event through the parent's adapter
86
- * and records the proxy routing entries on the parent session.
62
+ * and records the routing entries on the parent session.
87
63
  */
88
64
  export declare function runProxyInputRequestStep(input: {
89
65
  readonly hookPayload: SubagentInputRequestHookPayload;
90
66
  readonly parentWritable: WritableStream<Uint8Array>;
91
67
  readonly serializedContext: Record<string, unknown>;
92
- readonly session: HarnessSession;
93
- }): Promise<RunProxyInputRequestResult>;
94
- /**
95
- * Outcome of {@link routeProxiedDeliverStep} — routing may fully consume
96
- * the inbound payload (routed to descendants only, `remainder` is
97
- * `undefined`) or leave a residual payload the parent's own harness
98
- * should still see.
99
- *
100
- * Routing itself does not mutate session state; the parent session is
101
- * unchanged on return, so it is intentionally absent from this result.
102
- */
103
- export interface RoutedProxiedDeliverResult {
68
+ readonly sessionState: DurableSessionState;
69
+ readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
70
+ }): Promise<ProxyInputRequestResult>;
71
+ export interface RoutedDeliverResult {
72
+ /** `undefined` when the entire payload was routed to descendants. */
104
73
  readonly remainder: DeliverPayload | undefined;
105
74
  }
106
75
  /**
107
76
  * Splits an inbound deliver payload into parent-local and
108
- * proxied-child buckets and forwards the child buckets to the
109
- * descendants that raised the original HITL batch.
110
- *
111
- * Runs inside a `"use step"` boundary so the per-descendant
112
- * `resumeHook` calls are durably recorded. Returns the residual
113
- * payload (if any) for the parent's own harness to process.
77
+ * proxied-child buckets and forwards the child buckets via
78
+ * `resumeHook`. Read-only: never appends a snapshot.
114
79
  */
115
80
  export declare function routeProxiedDeliverStep(input: {
116
81
  readonly auth?: SessionAuthContext | null;
117
82
  readonly parentWritable: WritableStream<Uint8Array>;
118
83
  readonly payload: DeliverPayload;
119
- readonly session: HarnessSession;
120
- }): Promise<RoutedProxiedDeliverResult>;
121
- /**
122
- * Starts a per-turn child workflow for the current driver session.
123
- *
124
- * `parentWritable` is forwarded to the child in the workflow input so
125
- * the child's step writes land directly on the driver run's stream —
126
- * no copier, no double-store.
127
- */
84
+ readonly sessionState: DurableSessionState;
85
+ }): Promise<RoutedDeliverResult>;
86
+ /** Starts a per-turn child workflow for the current driver session. */
128
87
  export declare function dispatchTurnStep(input: TurnWorkflowInput): Promise<{
129
88
  readonly runId: string;
130
89
  }>;
131
- /**
132
- * Creates the durable session state inside a step boundary before the
133
- * workflow enters its long-lived turn loop.
134
- *
135
- * The optional `nodeId` targets a specific node in the compiled
136
- * agent graph (used for subagent child workflows). When omitted, the
137
- * root agent is used.
138
- */
139
- export declare function createSessionStep(input: {
140
- readonly compiledArtifactsSource: RuntimeCompiledArtifactsSource;
141
- readonly continuationToken: string;
142
- readonly nodeId?: string;
143
- readonly sessionId: string;
144
- }): Promise<HarnessSession>;
@@ -1 +1 @@
1
- import{createLogger,formatError}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{AuthKey,CapabilitiesKey,ContinuationTokenKey,InitiatorAuthKey,ModeKey}from"#context/keys.js";import{createSessionFailedEvent,createSubagentCalledEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{callAdapterEventHandler,defaultDeliverResult}from"#channel/adapter.js";import{isHarnessBetweenTurns}from"#harness/emission.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{PendingConnectionToolCallsKey}from"#runtime/framework-tools/pending-connection-tool-calls.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{upsertProxyInputRequests}from"#harness/proxy-input-requests.js";import{createSession,refreshSessionFromTurnAgent}from"#execution/session.js";import{getPendingRuntimeActionBatch,recordPendingSubagentChildToken}from"#harness/runtime-actions.js";import{coalesceTurnInputs}from"#harness/messages.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{dispatchStreamEventHooks,runHookLifecycleStep}from"#context/hook-lifecycle.js";import{runStep}from"#context/run-step.js";import{createExecutionNodeStep}from"#execution/node-step.js";import{resolveRemoteAgentForAction,startRemoteAgentSession}from"#execution/remote-agent-dispatch.js";import{emitProxiedInputRequest,routeDeliverPayload}from"#execution/subagent-hitl-proxy.js";import{buildSubagentRunInput}from"#execution/subagent-tool.js";import{turnWorkflow}from"#execution/turn-workflow.js";import{createWorkflowRuntime,startWorkflowPreferLatest,workflowEntryReference}from"#execution/workflow-runtime.js";async function turnStep(e){"use step";let t=await deserializeContext(e.serializedContext),n=t.require(ChannelKey);e.input?.kind===`deliver`&&e.input.auth!==void 0&&t.set(AuthKey,e.input.auth??null);let a=buildAdapterContext(n,t),o;if(e.input?.kind===`deliver`){let t=[];for(let r of e.input.payloads){let e=n.deliver?await n.deliver(r,a):defaultDeliverResult(r);e!=null&&t.push(e)}o=t.length===0?void 0:t.reduce(coalesceTurnInputs)}else e.input?.kind===`runtime-action-result`&&(o={runtimeActionResults:e.input.results});if(e.input?.kind===`deliver`){let e={...n,state:{...a.state}};t.set(ChannelKey,e)}if(e.input?.kind===`deliver`&&o===void 0){let n=reconcileSessionContinuationToken(t,e.session);return{action:`park`,serializedContext:serializeContext(t),session:n}}let c=e.parentWritable.getWriter(),l=t.require(BundleKey).hookRegistry,emit=async e=>{let r=await callAdapterEventHandler(n,e,a);t.set(ChannelKey,{...n,state:{...a.state}}),await c.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(r))),await dispatchStreamEventHooks({ctx:t,registry:l,event:r})},_=await runStep(t,e.session,async e=>{let n=t.require(BundleKey),r=t.get(CapabilitiesKey),a=t.require(ModeKey),runHarnessStep=async(e,t)=>{let i=refreshSessionFromTurnAgent({compactionOverrides:{thresholdPercent:n.resolvedAgent.config.compaction?.thresholdPercent},session:e,turnAgent:n.turnAgent});return createExecutionNodeStep({capabilities:r,compiledArtifactsSource:n.compiledArtifactsSource,createRuntime:createWorkflowRuntime,emit,mode:a,node:n.graph.root})(i,t)};return o!==void 0&&isHarnessBetweenTurns(e)?runHookLifecycleStep({ctx:t,emit,input:o,mode:a,registry:n.hookRegistry,session:e},runHarnessStep):runHarnessStep(e,o)}),v=reconcileSessionContinuationToken(t,_.session),y=serializeContext(t);_={..._,session:v};let b=t.get(PendingConnectionToolCallsKey)??[];return b.length>0?(c.releaseLock(),{action:`await-authorization`,pendingToolCalls:b,serializedContext:y,session:_.session}):_.next!==null&&typeof _.next==`object`&&`done`in _.next?(await c.close(),{action:`done`,output:_.next.output,serializedContext:y,session:_.session}):_.next===null?(c.releaseLock(),{action:`park`,serializedContext:y,session:_.session}):(c.releaseLock(),{action:`continue`,serializedContext:y,session:_.session})}function reconcileSessionContinuationToken(e,t){let n=e.get(ContinuationTokenKey);return n===void 0||n===t.continuationToken?t:{...t,continuationToken:n}}async function dispatchPendingRuntimeActionsStep(e){"use step";let t=getPendingRuntimeActionBatch(e.session);if(t===void 0||t.actions.length===0)return{results:[],session:e.session};let n=await deserializeContext(e.serializedContext),a=n.require(BundleKey),s=n.require(ChannelKey),c=n.get(AuthKey)??null,d=n.get(CapabilitiesKey),f=n.get(InitiatorAuthKey)??null,h=e.parentWritable.getWriter(),g=buildAdapterContext(s,n),_=e.session,v=[];try{for(let n of t.actions){let r,i,o,p;switch(n.kind){case`subagent-call`:{let o=createWorkflowRuntime({compiledArtifactsSource:a.compiledArtifactsSource,nodeId:n.nodeId}),{childContinuationToken:s,runInput:l}=buildSubagentRunInput({action:n,auth:c,batchEvent:t.event,capabilities:d,initiatorAuth:f,session:e.session}),u=await o.run(l);_=recordPendingSubagentChildToken({callId:n.callId,childContinuationToken:s,session:_}),r=u.sessionId,i=n.name,p=n.subagentName;break}case`remote-agent-call`:{let t;try{t=resolveRemoteAgentForAction({nodeId:n.nodeId,remoteAgentName:n.remoteAgentName,registry:a.subagentRegistry.subagentsByNodeId}),r=await startRemoteAgentSession({action:n,callbackBaseUrl:e.callbackBaseUrl,remote:t,session:e.session})}catch(e){v.push(createRemoteAgentStartFailureResult({action:n,error:e}));continue}i=n.name,o={url:t.url},p=n.remoteAgentName;break}default:throw Error(`Unsupported runtime action kind "${n.kind}" in workflow runtime.`)}let m=await callAdapterEventHandler(s,createSubagentCalledEvent({callId:n.callId,childSessionId:r,name:i,remote:o,sequence:t.event.sequence,sessionId:e.session.sessionId,toolName:p,turnId:t.event.turnId,workflowId:workflowEntryReference.workflowId}),g);await h.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(m)))}}finally{h.releaseLock()}return{results:v,session:_}}function createRemoteAgentStartFailureResult(e){return{callId:e.action.callId,isError:!0,kind:`subagent-result`,output:{code:`REMOTE_AGENT_START_FAILED`,message:toErrorMessage(e.error)},subagentName:e.action.remoteAgentName}}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});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 deserializeContext(e.serializedContext),n=t.require(ChannelKey),r=buildAdapterContext(n,t),i=t.require(ModeKey),a=e.parentWritable.getWriter(),o;try{o=await emitProxiedInputRequest({emit:async e=>{let t=await callAdapterEventHandler(n,e,r);await a.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t)))},hookPayload:e.hookPayload,mode:i,session:e.session})}finally{a.releaseLock()}return t.set(ChannelKey,{...n,state:{...r.state}}),{serializedContext:serializeContext(t),session:reconcileSessionContinuationToken(t,upsertProxyInputRequests({entries:o.entries,forChildContinuationToken:e.hookPayload.childContinuationToken,session:o.session}))}}async function routeProxiedDeliverStep(e){"use step";let t=routeDeliverPayload({payload:e.payload,session:e.session}),{resumeHook:n}=await import(`#compiled/@workflow/core/runtime.js`);for(let r of t.forChildren)await n(r.childContinuationToken,{auth:e.auth,kind:`deliver`,payloads:[r.payload]});return{remainder:t.forSelf}}async function dispatchTurnStep(e){"use step";return{runId:(await startWorkflowPreferLatest(turnWorkflow,[e])).runId}}async function createSessionStep(e){"use step";let t=await getCompiledRuntimeAgentBundle({compiledArtifactsSource:e.compiledArtifactsSource,nodeId:e.nodeId});return createSession({compactionOverrides:{thresholdPercent:t.resolvedAgent.config.compaction?.thresholdPercent},continuationToken:e.continuationToken,sessionId:e.sessionId,turnAgent:t.turnAgent})}export{createSessionStep,dispatchPendingRuntimeActionsStep,dispatchTurnStep,emitTerminalSessionFailureStep,reconcileSessionContinuationToken,routeProxiedDeliverStep,runProxyInputRequestStep,turnStep};
1
+ import{createLogger,formatError}from"#internal/logging.js";import{AuthKey,CapabilitiesKey,ContinuationTokenKey,ModeKey}from"#context/keys.js";import{createSessionFailedEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{callAdapterEventHandler,defaultDeliverResult}from"#channel/adapter.js";import{isHarnessBetweenTurns}from"#harness/emission.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{PendingConnectionToolCallsKey}from"#runtime/framework-tools/pending-connection-tool-calls.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{readDurableSession,writeDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession,refreshSessionFromTurnAgent}from"#execution/session.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}from"#context/run-step.js";import{hasPendingInputBatch}from"#harness/input-requests.js";import{getRuntimeActionRequestKey}from"#runtime/actions/keys.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),o=i.require(ChannelKey),d=i.require(BundleKey),f=hydrateDurableSession({compactionOverrides:{thresholdPercent:d.resolvedAgent.config.compaction?.thresholdPercent},durable:t,turnAgent:d.turnAgent});e.input?.kind===`deliver`&&e.input.auth!==void 0&&i.set(AuthKey,e.input.auth??null);let p=buildAdapterContext(o,i),m;if(e.input?.kind===`deliver`){let t=[];for(let n of e.input.payloads){let e=o.deliver?await o.deliver(n,p):defaultDeliverResult(n);e!=null&&t.push(e)}m=t.length===0?void 0:t.reduce(coalesceTurnInputs)}else e.input?.kind===`runtime-action-result`&&(m={runtimeActionResults:e.input.results});if(e.input?.kind===`deliver`){let e={...o,state:{...p.state}};i.set(ChannelKey,e)}if(e.input?.kind===`deliver`&&m===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 h=e.parentWritable.getWriter(),g=i.require(BundleKey).hookRegistry,emit=async e=>{let t=await callAdapterEventHandler(o,e,p);i.set(ChannelKey,{...o,state:{...p.state}}),await h.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t))),await dispatchStreamEventHooks({ctx:i,registry:g,event:t})},_=await runStep(i,f,async e=>{let t=i.require(BundleKey),n=i.get(CapabilitiesKey),o=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:o,node:t.graph.root})(i,r)};return m!==void 0&&isHarnessBetweenTurns(e)?runHookLifecycleStep({ctx:i,emit,input:m,mode:o,registry:t.hookRegistry,session:e},runHarnessStep):runHarnessStep(e,m)}),v=reconcileSessionContinuationToken(i,_.session),y=serializeContext(i);_={..._,session:v};let b=await writeDurableSession({session:_.session,writable:e.sessionWritable}),x=i.get(PendingConnectionToolCallsKey)??[];return x.length>0?(h.releaseLock(),{action:`await-authorization`,pendingToolCalls:x,serializedContext:y,sessionState:b}):_.next!==null&&typeof _.next==`object`&&`done`in _.next?(await h.close(),{action:`done`,output:_.next.output,serializedContext:y,sessionState:b}):_.next===null?(h.releaseLock(),{action:`park`,...derivePendingState(_.session),serializedContext:y,sessionState:b}):(h.releaseLock(),{action:`continue`,serializedContext:y,sessionState:b})}function derivePendingState(e){let t=getPendingRuntimeActionBatch(e.state),n={hasPendingInputBatch:hasPendingInputBatch(e.state)};return t===void 0?n:{...n,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});let s=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,s,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(s)))}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{u=await emitProxiedInputRequest({emit:async e=>{let t=await callAdapterEventHandler(r,e,i);await l.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(t)))},hookPayload:e.hookPayload,mode:o,session:c})}finally{l.releaseLock()}return n.set(ChannelKey,{...r,state:{...i.state}}),{serializedContext:serializeContext(n),sessionState:await writeDurableSession({session:reconcileSessionContinuationToken(n,upsertProxyInputRequests({entries:u.entries,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};
@@ -8,7 +8,7 @@ type InlineToolResultPart = Extract<ToolResponsePart, {
8
8
  import type { AssistantStepFinishReason, RuntimeIdentity } from "#protocol/message.js";
9
9
  import type { RunMode } from "#shared/run-mode.js";
10
10
  import type { JsonObject } from "#shared/json.js";
11
- import type { HarnessEmitFn, HarnessSession, StepInput } from "#harness/types.js";
11
+ import type { HarnessEmitFn, HarnessSession, SessionStateMap, StepInput } from "#harness/types.js";
12
12
  /**
13
13
  * Tracks emission lifecycle state across harness step invocations.
14
14
  *
@@ -21,10 +21,8 @@ export interface HarnessEmissionState {
21
21
  readonly stepIndex: number;
22
22
  readonly turnId: string;
23
23
  }
24
- /**
25
- * Reads the emission state from the session, returning defaults when absent.
26
- */
27
- export declare function getHarnessEmissionState(session: HarnessSession): HarnessEmissionState;
24
+ /** Reads the emission state, returning defaults when absent. */
25
+ export declare function getHarnessEmissionState(state: SessionStateMap | undefined): HarnessEmissionState;
28
26
  /**
29
27
  * Returns `true` when the harness is **between turns** — either no turn
30
28
  * has started yet (initial state) or the previous turn has emitted its
@@ -1 +1 @@
1
- import{toError}from"#shared/errors.js";import{createActionResultEvent,createMessageAppendedEvent,createMessageCompletedEvent,createMessageReceivedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent,createSessionCompletedEvent,createSessionFailedEvent,createSessionStartedEvent,createSessionWaitingEvent,createStepFailedEvent,createStepStartedEvent,createTurnCompletedEvent,createTurnFailedEvent,createTurnStartedEvent}from"#protocol/message.js";import{createRuntimeToolResultFromStepResult}from"#harness/action-result-helpers.js";const HARNESS_EMISSION_STATE_KEY=`ash.harness.emission`,DEFAULT_EMISSION_STATE={sessionStarted:!1,sequence:0,stepIndex:0,turnId:``};function getHarnessEmissionState(e){return e.state?.[HARNESS_EMISSION_STATE_KEY]??DEFAULT_EMISSION_STATE}function isHarnessBetweenTurns(e){return getHarnessEmissionState(e).turnId===``}function setHarnessEmissionState(e,t){return{...e,state:{...e.state,[HARNESS_EMISSION_STATE_KEY]:t}}}async function emitTurnPreamble(e,t,n,r){let a=`turn_${n.sequence}`;return n.sessionStarted||await e(createSessionStartedEvent({runtime:r})),await e(createTurnStartedEvent({sequence:n.sequence,turnId:a})),t.message!==void 0&&await e(createMessageReceivedEvent({message:t.message,sequence:n.sequence,turnId:a})),{sessionStarted:!0,sequence:n.sequence,stepIndex:0,turnId:a}}async function emitStepStarted(e,t){await e(createStepStartedEvent({sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId}))}async function emitStepAndTurnFailed(e,t,n){await e(createStepFailedEvent({...n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),await e(createTurnFailedEvent({...n,sequence:t.sequence,turnId:t.turnId}))}async function emitFailedStep(e,t,n){await emitStepAndTurnFailed(e,t,n),await e(createSessionFailedEvent(n))}async function emitRecoverableFailedTurn(e,t,n){return await emitStepAndTurnFailed(e,t,n),await e(createSessionWaitingEvent()),{sessionStarted:t.sessionStarted,sequence:t.sequence+1,stepIndex:0,turnId:``}}function advanceStep(e){return{...e,stepIndex:e.stepIndex+1}}async function emitTurnEpilogue(e,t,n){return await e(createTurnCompletedEvent({sequence:t.sequence,turnId:t.turnId})),n===`conversation`?await e(createSessionWaitingEvent()):await e(createSessionCompletedEvent()),{sessionStarted:t.sessionStarted,sequence:t.sequence+1,stepIndex:0,turnId:``}}function normalizeAssistantStepFinishReason(e){switch(e){case`content-filter`:case`error`:case`length`:case`stop`:case`tool-calls`:return e;default:return`other`}}async function emitStreamContent(i,o,s){let c=``,l=``,u=`stop`,d,f=new Set,p=new Set,m=[],flushCurrentMessage=async()=>{l.length!==0&&(await i(createMessageCompletedEvent({finishReason:`tool-calls`,message:l,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),l=``)};for await(let r of s)if(d===void 0)switch(r.type){case`reasoning-delta`:c+=r.text,await i(createReasoningAppendedEvent({reasoningDelta:r.text,reasoningSoFar:c,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId}));break;case`text-delta`:c.trim().length>0&&(await i(createReasoningCompletedEvent({reasoning:c,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),c=``),l+=r.text,await i(createMessageAppendedEvent({messageDelta:r.text,messageSoFar:l,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId}));break;case`tool-call`:f.add(r.toolCallId);break;case`tool-result`:{if(f.has(r.toolCallId))break;await flushCurrentMessage();let e=r;await i(createActionResultEvent({result:createRuntimeToolResultFromStepResult(e),sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),p.add(r.toolCallId);let n=e.output;m.push({type:`tool-result`,toolCallId:e.toolCallId,toolName:e.toolName,output:typeof n==`string`?{type:`text`,value:n}:{type:`json`,value:n??null}});break}case`finish-step`:u=normalizeAssistantStepFinishReason(r.finishReason);break;case`error`:d=toError(r.error);break;default:break}if(d!==void 0)throw d;return c.trim().length>0&&await i(createReasoningCompletedEvent({reasoning:c,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),l.length>0&&await i(createMessageCompletedEvent({finishReason:u,message:l,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),{inlineActionResultCallIds:p,inlineToolResultParts:m}}export{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,isHarnessBetweenTurns,normalizeAssistantStepFinishReason,setHarnessEmissionState};
1
+ import{toError}from"#shared/errors.js";import{createActionResultEvent,createMessageAppendedEvent,createMessageCompletedEvent,createMessageReceivedEvent,createReasoningAppendedEvent,createReasoningCompletedEvent,createSessionCompletedEvent,createSessionFailedEvent,createSessionStartedEvent,createSessionWaitingEvent,createStepFailedEvent,createStepStartedEvent,createTurnCompletedEvent,createTurnFailedEvent,createTurnStartedEvent}from"#protocol/message.js";import{createRuntimeToolResultFromStepResult}from"#harness/action-result-helpers.js";const HARNESS_EMISSION_STATE_KEY=`ash.harness.emission`,DEFAULT_EMISSION_STATE={sessionStarted:!1,sequence:0,stepIndex:0,turnId:``};function getHarnessEmissionState(e){return e?.[HARNESS_EMISSION_STATE_KEY]??DEFAULT_EMISSION_STATE}function isHarnessBetweenTurns(e){return getHarnessEmissionState(e.state).turnId===``}function setHarnessEmissionState(e,t){return{...e,state:{...e.state,[HARNESS_EMISSION_STATE_KEY]:t}}}async function emitTurnPreamble(e,t,n,r){let a=`turn_${n.sequence}`;return n.sessionStarted||await e(createSessionStartedEvent({runtime:r})),await e(createTurnStartedEvent({sequence:n.sequence,turnId:a})),t.message!==void 0&&await e(createMessageReceivedEvent({message:t.message,sequence:n.sequence,turnId:a})),{sessionStarted:!0,sequence:n.sequence,stepIndex:0,turnId:a}}async function emitStepStarted(e,t){await e(createStepStartedEvent({sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId}))}async function emitStepAndTurnFailed(e,t,n){await e(createStepFailedEvent({...n,sequence:t.sequence,stepIndex:t.stepIndex,turnId:t.turnId})),await e(createTurnFailedEvent({...n,sequence:t.sequence,turnId:t.turnId}))}async function emitFailedStep(e,t,n){await emitStepAndTurnFailed(e,t,n),await e(createSessionFailedEvent(n))}async function emitRecoverableFailedTurn(e,t,n){return await emitStepAndTurnFailed(e,t,n),await e(createSessionWaitingEvent()),{sessionStarted:t.sessionStarted,sequence:t.sequence+1,stepIndex:0,turnId:``}}function advanceStep(e){return{...e,stepIndex:e.stepIndex+1}}async function emitTurnEpilogue(e,t,n){return await e(createTurnCompletedEvent({sequence:t.sequence,turnId:t.turnId})),n===`conversation`?await e(createSessionWaitingEvent()):await e(createSessionCompletedEvent()),{sessionStarted:t.sessionStarted,sequence:t.sequence+1,stepIndex:0,turnId:``}}function normalizeAssistantStepFinishReason(e){switch(e){case`content-filter`:case`error`:case`length`:case`stop`:case`tool-calls`:return e;default:return`other`}}async function emitStreamContent(i,o,s){let c=``,l=``,u=`stop`,d,f=new Set,p=new Set,m=[],flushCurrentMessage=async()=>{l.length!==0&&(await i(createMessageCompletedEvent({finishReason:`tool-calls`,message:l,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),l=``)};for await(let r of s)if(d===void 0)switch(r.type){case`reasoning-delta`:c+=r.text,await i(createReasoningAppendedEvent({reasoningDelta:r.text,reasoningSoFar:c,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId}));break;case`text-delta`:c.trim().length>0&&(await i(createReasoningCompletedEvent({reasoning:c,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),c=``),l+=r.text,await i(createMessageAppendedEvent({messageDelta:r.text,messageSoFar:l,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId}));break;case`tool-call`:f.add(r.toolCallId);break;case`tool-result`:{if(f.has(r.toolCallId))break;await flushCurrentMessage();let e=r;await i(createActionResultEvent({result:createRuntimeToolResultFromStepResult(e),sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),p.add(r.toolCallId);let n=e.output;m.push({type:`tool-result`,toolCallId:e.toolCallId,toolName:e.toolName,output:typeof n==`string`?{type:`text`,value:n}:{type:`json`,value:n??null}});break}case`finish-step`:u=normalizeAssistantStepFinishReason(r.finishReason);break;case`error`:d=toError(r.error);break;default:break}if(d!==void 0)throw d;return c.trim().length>0&&await i(createReasoningCompletedEvent({reasoning:c,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),l.length>0&&await i(createMessageCompletedEvent({finishReason:u,message:l,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),{inlineActionResultCallIds:p,inlineToolResultParts:m}}export{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStepStarted,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,isHarnessBetweenTurns,normalizeAssistantStepFinishReason,setHarnessEmissionState};
@@ -1,7 +1,7 @@
1
1
  import type { ModelMessage, ToolSet, TypedToolCall } from "ai";
2
2
  import type { RuntimeToolCallActionRequest } from "#runtime/actions/types.js";
3
3
  import type { InputRequest } from "#runtime/input/types.js";
4
- import type { HarnessSession, StepInput } from "#harness/types.js";
4
+ import type { HarnessSession, SessionStateMap, StepInput } from "#harness/types.js";
5
5
  /**
6
6
  * Returns true when the step input carries user-facing turn input.
7
7
  */
@@ -49,10 +49,10 @@ type ResolvePendingInputResult = {
49
49
  readonly session: HarnessSession;
50
50
  };
51
51
  /**
52
- * Returns true when the session is parked on a pending HITL input batch
52
+ * Returns true when the session is parked on a pending HITL batch
53
53
  * (tool approvals or `ask_question` prompts).
54
54
  */
55
- export declare function hasPendingInputBatch(session: HarnessSession): boolean;
55
+ export declare function hasPendingInputBatch(state: SessionStateMap | undefined): boolean;
56
56
  /**
57
57
  * Stores one pending HITL batch on the session until the user responds.
58
58
  */
@@ -1 +1 @@
1
- import{parseJsonObject}from"#shared/json.js";import{coalesceTurnInputs}from"#harness/messages.js";const PENDING_INPUT_BATCH_KEY=`ash.runtime.pendingInputBatch`,APPROVED_TOOLS_KEY=`ash.runtime.hitl.approvedTools`,DEFERRED_STEP_INPUT_KEY=`ash.runtime.deferredStepInput`;function hasStepInput(e){return e===void 0?!1:e.message!==void 0||(e.inputResponses?.length??0)>0}function consumeDeferredStepInput(e){let n=getDeferredStepInput(e.session);if(n===void 0)return e;let r=clearDeferredStepInput(e.session);return e.input===void 0?{input:n,session:r}:{input:coalesceTurnInputs(n,e.input),session:r}}function hasDeferredStepInput(e){return getDeferredStepInput(e)!==void 0}function resolvePendingInput(e){let{stepInput:t}=e,n=e.session,r=[...e.history??n.history],i=getPendingInputBatch(n);if(i===void 0)return{outcome:`continue`,messages:r,session:n};let a=t?.inputResponses??[];if(a.length===0&&t?.message===void 0)return{outcome:`unresolved`,messages:r,session:n};if(a.length===0&&t?.message!==void 0){let e=buildToolResponseParts(i,[]),a=[...r,...i.responseMessages];return e.length>0&&a.push({content:e,role:`tool`}),n=clearPendingInputBatch(n),i.requests.some(e=>isApprovalRequest(e))?(n=queueDeferredStepInput(n,{message:t.message}),{deferredMessage:!0,outcome:`resolved`,messages:a,session:n}):{outcome:`resolved`,messages:a,session:n}}n=recordApprovedTools({pendingBatch:i,resolveApprovalKey:e.resolveApprovalKey,responses:a,session:n});let o=buildToolResponseParts(i,a),s=[...r,...i.responseMessages];return o.length>0&&s.push({content:o,role:`tool`}),n=clearPendingInputBatch(n),t?.message!==void 0&&i.requests.some(e=>isApprovalRequest(e))?(n=queueDeferredStepInput(n,{message:t.message}),{deferredMessage:!0,outcome:`resolved`,messages:s,session:n}):{outcome:`resolved`,messages:s,session:n}}function hasPendingInputBatch(e){return getPendingInputBatch(e)!==void 0}function getPendingInputBatch(e){let t=e.state?.[PENDING_INPUT_BATCH_KEY];if(typeof t!=`object`||!t)return;let r=t;if(!(!Array.isArray(r.requests)||!Array.isArray(r.responseMessages)))return r}function setPendingInputBatch(e){let t={...e.session.state};return t[PENDING_INPUT_BATCH_KEY]={requests:[...e.requests],responseMessages:[...e.responseMessages]},{...e.session,state:t}}function clearPendingInputBatch(e){if(e.state?.[PENDING_INPUT_BATCH_KEY]===void 0)return e;let t={...e.state};return delete t[PENDING_INPUT_BATCH_KEY],{...e,state:Object.keys(t).length>0?t:void 0}}function getDeferredStepInput(e){return e.state?.[DEFERRED_STEP_INPUT_KEY]}function queueDeferredStepInput(e,n){let r=getDeferredStepInput(e),a=r===void 0?n:coalesceTurnInputs(r,n),o={...e.state};return o[DEFERRED_STEP_INPUT_KEY]=a,{...e,state:o}}function clearDeferredStepInput(e){if(e.state?.[DEFERRED_STEP_INPUT_KEY]===void 0)return e;let t={...e.state};return delete t[DEFERRED_STEP_INPUT_KEY],{...e,state:Object.keys(t).length>0?t:void 0}}function getApprovedTools(e){let t=e.state?.[APPROVED_TOOLS_KEY];return Array.isArray(t)?new Set(t):new Set}function recordApprovedTools(e){let t=new Set(e.responses.filter(e=>e.optionId===`approve`).map(e=>e.requestId)),n=e.pendingBatch.requests.filter(e=>t.has(e.requestId)).map(t=>e.resolveApprovalKey?.(t)??t.action.toolName);if(n.length===0)return e.session;let i=getApprovedTools(e.session),a=[...new Set([...i,...n])],o={...e.session.state};return o[APPROVED_TOOLS_KEY]=a,{...e.session,state:o}}function buildToolResponseParts(e,t){let n=new Map(t.map(e=>[e.requestId,e])),r=[];for(let t of e.requests)r.push(...buildToolResponsePartsForRequest(t,n.get(t.requestId)));return r}function buildToolResponsePartsForRequest(e,t){if(isApprovalRequest(e)){let n=t?.optionId===`approve`,r=t===void 0?`Ignored because the user continued without responding.`:void 0,i=[{approvalId:e.requestId,approved:n,reason:r,type:`tool-approval-response`}];return n||i.push({output:{type:`execution-denied`,reason:r},toolCallId:e.action.callId,toolName:e.action.toolName,type:`tool-result`}),i}return[{output:{type:`json`,value:t===void 0?{status:`ignored`}:{optionId:t.optionId,text:t.text,status:`answered`}},toolCallId:e.action.callId,toolName:e.action.toolName,type:`tool-result`}]}function isApprovalRequest(e){return e.options?.length===2&&e.options[0]?.id===`approve`&&e.options[1]?.id===`deny`}function createRuntimeToolCallActionFromToolCall(e){return{callId:e.toolCall.toolCallId,input:resolveToolCallInputObject(e.toolCall.input),kind:`tool-call`,toolName:e.toolCall.toolName}}function resolveToolCallInputObject(t){return t==null?{}:parseJsonObject(t)}export{consumeDeferredStepInput,createRuntimeToolCallActionFromToolCall,getApprovedTools,hasDeferredStepInput,hasPendingInputBatch,hasStepInput,resolvePendingInput,setPendingInputBatch};
1
+ import{parseJsonObject}from"#shared/json.js";import{coalesceTurnInputs}from"#harness/messages.js";const PENDING_INPUT_BATCH_KEY=`ash.runtime.pendingInputBatch`,APPROVED_TOOLS_KEY=`ash.runtime.hitl.approvedTools`,DEFERRED_STEP_INPUT_KEY=`ash.runtime.deferredStepInput`;function hasStepInput(e){return e===void 0?!1:e.message!==void 0||(e.inputResponses?.length??0)>0}function consumeDeferredStepInput(e){let n=getDeferredStepInput(e.session);if(n===void 0)return e;let r=clearDeferredStepInput(e.session);return e.input===void 0?{input:n,session:r}:{input:coalesceTurnInputs(n,e.input),session:r}}function hasDeferredStepInput(e){return getDeferredStepInput(e)!==void 0}function resolvePendingInput(e){let{stepInput:t}=e,n=e.session,r=[...e.history??n.history],i=getPendingInputBatch(n.state);if(i===void 0)return{outcome:`continue`,messages:r,session:n};let a=t?.inputResponses??[];if(a.length===0&&t?.message===void 0)return{outcome:`unresolved`,messages:r,session:n};if(a.length===0&&t?.message!==void 0){let e=buildToolResponseParts(i,[]),a=[...r,...i.responseMessages];return e.length>0&&a.push({content:e,role:`tool`}),n=clearPendingInputBatch(n),i.requests.some(e=>isApprovalRequest(e))?(n=queueDeferredStepInput(n,{message:t.message}),{deferredMessage:!0,outcome:`resolved`,messages:a,session:n}):{outcome:`resolved`,messages:a,session:n}}n=recordApprovedTools({pendingBatch:i,resolveApprovalKey:e.resolveApprovalKey,responses:a,session:n});let o=buildToolResponseParts(i,a),s=[...r,...i.responseMessages];return o.length>0&&s.push({content:o,role:`tool`}),n=clearPendingInputBatch(n),t?.message!==void 0&&i.requests.some(e=>isApprovalRequest(e))?(n=queueDeferredStepInput(n,{message:t.message}),{deferredMessage:!0,outcome:`resolved`,messages:s,session:n}):{outcome:`resolved`,messages:s,session:n}}function hasPendingInputBatch(e){return getPendingInputBatch(e)!==void 0}function getPendingInputBatch(e){let t=e?.[PENDING_INPUT_BATCH_KEY];if(typeof t!=`object`||!t)return;let r=t;if(!(!Array.isArray(r.requests)||!Array.isArray(r.responseMessages)))return r}function setPendingInputBatch(e){let t={...e.session.state};return t[PENDING_INPUT_BATCH_KEY]={requests:[...e.requests],responseMessages:[...e.responseMessages]},{...e.session,state:t}}function clearPendingInputBatch(e){if(e.state?.[PENDING_INPUT_BATCH_KEY]===void 0)return e;let t={...e.state};return delete t[PENDING_INPUT_BATCH_KEY],{...e,state:Object.keys(t).length>0?t:void 0}}function getDeferredStepInput(e){return e.state?.[DEFERRED_STEP_INPUT_KEY]}function queueDeferredStepInput(e,n){let r=getDeferredStepInput(e),a=r===void 0?n:coalesceTurnInputs(r,n),o={...e.state};return o[DEFERRED_STEP_INPUT_KEY]=a,{...e,state:o}}function clearDeferredStepInput(e){if(e.state?.[DEFERRED_STEP_INPUT_KEY]===void 0)return e;let t={...e.state};return delete t[DEFERRED_STEP_INPUT_KEY],{...e,state:Object.keys(t).length>0?t:void 0}}function getApprovedTools(e){let t=e.state?.[APPROVED_TOOLS_KEY];return Array.isArray(t)?new Set(t):new Set}function recordApprovedTools(e){let t=new Set(e.responses.filter(e=>e.optionId===`approve`).map(e=>e.requestId)),n=e.pendingBatch.requests.filter(e=>t.has(e.requestId)).map(t=>e.resolveApprovalKey?.(t)??t.action.toolName);if(n.length===0)return e.session;let i=getApprovedTools(e.session),a=[...new Set([...i,...n])],o={...e.session.state};return o[APPROVED_TOOLS_KEY]=a,{...e.session,state:o}}function buildToolResponseParts(e,t){let n=new Map(t.map(e=>[e.requestId,e])),r=[];for(let t of e.requests)r.push(...buildToolResponsePartsForRequest(t,n.get(t.requestId)));return r}function buildToolResponsePartsForRequest(e,t){if(isApprovalRequest(e)){let n=t?.optionId===`approve`,r=t===void 0?`Ignored because the user continued without responding.`:void 0,i=[{approvalId:e.requestId,approved:n,reason:r,type:`tool-approval-response`}];return n||i.push({output:{type:`execution-denied`,reason:r},toolCallId:e.action.callId,toolName:e.action.toolName,type:`tool-result`}),i}return[{output:{type:`json`,value:t===void 0?{status:`ignored`}:{optionId:t.optionId,text:t.text,status:`answered`}},toolCallId:e.action.callId,toolName:e.action.toolName,type:`tool-result`}]}function isApprovalRequest(e){return e.options?.length===2&&e.options[0]?.id===`approve`&&e.options[1]?.id===`deny`}function createRuntimeToolCallActionFromToolCall(e){return{callId:e.toolCall.toolCallId,input:resolveToolCallInputObject(e.toolCall.input),kind:`tool-call`,toolName:e.toolCall.toolName}}function resolveToolCallInputObject(t){return t==null?{}:parseJsonObject(t)}export{consumeDeferredStepInput,createRuntimeToolCallActionFromToolCall,getApprovedTools,hasDeferredStepInput,hasPendingInputBatch,hasStepInput,resolvePendingInput,setPendingInputBatch};
@@ -1,21 +1,19 @@
1
1
  import type { SubagentInputRequestHookPayload } from "#channel/types.js";
2
- import type { HarnessSession } from "#harness/types.js";
2
+ import type { HarnessSession, SessionStateMap } from "#harness/types.js";
3
3
  /**
4
- * Returns the live `requestId childContinuationToken` map stored on
5
- * the session. Always returns a fresh `Map` (never a live reference),
6
- * so callers may treat the result as read-only without worrying about
7
- * accidental mutation of session state.
4
+ * Returns the proxy-routing map as a fresh `Map`. Never returns a live
5
+ * reference so accidental mutation cannot corrupt session state.
8
6
  */
9
- export declare function getProxyInputRequests(session: HarnessSession): ReadonlyMap<string, string>;
7
+ export declare function getProxyInputRequests(state: SessionStateMap | undefined): ReadonlyMap<string, string>;
10
8
  /**
11
9
  * Returns true when the session is currently proxying one or more
12
10
  * HITL requests on behalf of a descendant subagent.
13
11
  */
14
- export declare function hasProxyInputRequests(session: HarnessSession): boolean;
12
+ export declare function hasProxyInputRequests(state: SessionStateMap | undefined): boolean;
15
13
  /**
16
- * Replaces any prior entries for `forChildContinuationToken` with the
17
- * provided `entries`. A child that raises a fresh batch overwrites its
18
- * previous batch — the parent never keeps stale request metadata.
14
+ * Replaces prior entries for `forChildContinuationToken` with the
15
+ * provided ones. A child raising a fresh batch overwrites its prior
16
+ * batch — the parent never keeps stale request metadata.
19
17
  */
20
18
  export declare function upsertProxyInputRequests(input: {
21
19
  readonly entries: readonly (readonly [requestId: string, childContinuationToken: string])[];
@@ -23,14 +21,12 @@ export declare function upsertProxyInputRequests(input: {
23
21
  readonly session: HarnessSession;
24
22
  }): HarnessSession;
25
23
  /**
26
- * Removes every entry belonging to `childContinuationToken`. Called
27
- * when a child subagent finishes (via `resolvePendingRuntimeActions`)
28
- * so stale button clicks no longer route to a dead child.
24
+ * Removes every entry for `childContinuationToken`. Called when a
25
+ * child subagent finishes so stale clicks no longer route to it.
29
26
  */
30
27
  export declare function clearProxyInputRequestsForChild(session: HarnessSession, childContinuationToken: string): HarnessSession;
31
28
  /**
32
- * Projects one child {@link SubagentInputRequestHookPayload} into the
33
- * `(requestId, childContinuationToken)` tuples the parent session
34
- * stores. Pure — does not touch session state.
29
+ * Projects a {@link SubagentInputRequestHookPayload} into the
30
+ * `(requestId, childContinuationToken)` tuples the session stores.
35
31
  */
36
32
  export declare function toProxyInputRequestEntries(payload: SubagentInputRequestHookPayload): readonly (readonly [requestId: string, childContinuationToken: string])[];
@@ -1 +1 @@
1
- const PROXY_INPUT_REQUESTS_KEY=`ash.runtime.proxyInputRequests`;function getProxyInputRequests(e){return new Map(Object.entries(readMap(e)))}function hasProxyInputRequests(e){for(let t of Object.keys(readMap(e)))return!0;return!1}function upsertProxyInputRequests(e){let t={};for(let[n,r]of Object.entries(readMap(e.session)))r!==e.forChildContinuationToken&&(t[n]=r);for(let[n,r]of e.entries)t[n]=r;return writeMap(e.session,t)}function clearProxyInputRequestsForChild(e,t){let n=readMap(e),r={},i=!1;for(let[e,a]of Object.entries(n)){if(a===t){i=!0;continue}r[e]=a}return i?writeMap(e,r):e}function toProxyInputRequestEntries(e){return e.event.requests.map(t=>[t.requestId,e.childContinuationToken])}function readMap(t){let n=t.state?.[PROXY_INPUT_REQUESTS_KEY];if(typeof n!=`object`||!n||Array.isArray(n))return{};let r={};for(let[e,t]of Object.entries(n))typeof t==`string`&&(r[e]=t);return r}function writeMap(t,n){let r={...t.state};return Object.keys(n).length===0?(delete r[PROXY_INPUT_REQUESTS_KEY],{...t,state:Object.keys(r).length>0?r:void 0}):(r[PROXY_INPUT_REQUESTS_KEY]=n,{...t,state:r})}export{clearProxyInputRequestsForChild,getProxyInputRequests,hasProxyInputRequests,toProxyInputRequestEntries,upsertProxyInputRequests};
1
+ const PROXY_INPUT_REQUESTS_KEY=`ash.runtime.proxyInputRequests`;function getProxyInputRequests(e){return new Map(Object.entries(readMap(e)))}function hasProxyInputRequests(e){for(let t of Object.keys(readMap(e)))return!0;return!1}function upsertProxyInputRequests(e){let t={};for(let[n,r]of Object.entries(readMap(e.session.state)))r!==e.forChildContinuationToken&&(t[n]=r);for(let[n,r]of e.entries)t[n]=r;return writeMap(e.session,t)}function clearProxyInputRequestsForChild(e,t){let n=readMap(e.state),r={},i=!1;for(let[e,a]of Object.entries(n)){if(a===t){i=!0;continue}r[e]=a}return i?writeMap(e,r):e}function toProxyInputRequestEntries(e){return e.event.requests.map(t=>[t.requestId,e.childContinuationToken])}function readMap(t){let n=t?.[PROXY_INPUT_REQUESTS_KEY];if(typeof n!=`object`||!n||Array.isArray(n))return{};let r={};for(let[e,t]of Object.entries(n))typeof t==`string`&&(r[e]=t);return r}function writeMap(t,n){let r={...t.state};return Object.keys(n).length===0?(delete r[PROXY_INPUT_REQUESTS_KEY],{...t,state:Object.keys(r).length>0?r:void 0}):(r[PROXY_INPUT_REQUESTS_KEY]=n,{...t,state:r})}export{clearProxyInputRequestsForChild,getProxyInputRequests,hasProxyInputRequests,toProxyInputRequestEntries,upsertProxyInputRequests};
@@ -1,6 +1,6 @@
1
1
  import type { ModelMessage, ToolSet, TypedToolCall } from "ai";
2
2
  import type { RuntimeActionRequest, RuntimeActionResult } from "#runtime/actions/types.js";
3
- import type { HarnessEmitFn, HarnessSession, HarnessToolMap, StepInput } from "#harness/types.js";
3
+ import type { HarnessEmitFn, HarnessSession, HarnessToolMap, SessionStateMap, StepInput } from "#harness/types.js";
4
4
  /**
5
5
  * Serializable event coordinates for one pending runtime-action batch.
6
6
  *
@@ -15,12 +15,10 @@ interface PendingRuntimeActionEventMetadata {
15
15
  /**
16
16
  * Serializable pending runtime-action batch stored on `session.state`.
17
17
  *
18
- * `childContinuationTokens` maps each `subagent-call` action's `callId`
19
- * to the deterministic child continuation token minted by the dispatch
20
- * site. The harness consumes these on result resolution to clear the
21
- * matching proxy-input-request entries without having to compute (or
22
- * import) the token formula itself — keeping `harness/` runtime-agnostic
23
- * (AGENTS.md #14).
18
+ * `childContinuationTokens` maps each `subagent-call` action's
19
+ * `callId` to the deterministic child token minted by dispatch, so
20
+ * the harness can clear proxy-input entries on result resolution
21
+ * without re-deriving the token (keeps `harness/` runtime-agnostic).
24
22
  */
25
23
  interface PendingRuntimeActionBatch {
26
24
  readonly actions: readonly RuntimeActionRequest[];
@@ -36,14 +34,12 @@ interface ResolvePendingRuntimeActionsResult {
36
34
  readonly outcome: "continue" | "resolved" | "unresolved";
37
35
  readonly session: HarnessSession;
38
36
  }
39
- /**
40
- * Returns the pending runtime-action batch stored on the session, if any.
41
- */
42
- export declare function getPendingRuntimeActionBatch(session: HarnessSession): PendingRuntimeActionBatch | undefined;
37
+ /** Returns the pending runtime-action batch stored on the session, if any. */
38
+ export declare function getPendingRuntimeActionBatch(state: SessionStateMap | undefined): PendingRuntimeActionBatch | undefined;
43
39
  /**
44
40
  * Returns true when the session is parked on a pending runtime-action batch.
45
41
  */
46
- export declare function hasPendingRuntimeActionBatch(session: HarnessSession): boolean;
42
+ export declare function hasPendingRuntimeActionBatch(state: SessionStateMap | undefined): boolean;
47
43
  /**
48
44
  * Stores one pending runtime-action batch on the session.
49
45
  */
@@ -54,9 +50,8 @@ export declare function setPendingRuntimeActionBatch(input: {
54
50
  readonly session: HarnessSession;
55
51
  }): HarnessSession;
56
52
  /**
57
- * Records the child continuation token minted for one dispatched
58
- * subagent-call action onto the pending batch so {@link
59
- * resolvePendingRuntimeActions} can clear the matching proxy-input
53
+ * Records the child continuation token for a dispatched subagent-call
54
+ * so {@link resolvePendingRuntimeActions} can clear proxy-input
60
55
  * entries when the child finishes.
61
56
  */
62
57
  export declare function recordPendingSubagentChildToken(input: {
@@ -65,12 +60,9 @@ export declare function recordPendingSubagentChildToken(input: {
65
60
  readonly session: HarnessSession;
66
61
  }): HarnessSession;
67
62
  /**
68
- * Discriminated item consumed by {@link accumulateRuntimeActionResults}.
69
- *
70
- * The workflow runtime receives interleaved deliveries and runtime-action
71
- * results while waiting for pending subagent calls; this union lets the
72
- * accumulation loop process both kinds without coupling to the concrete
73
- * `HookPayload` shape.
63
+ * Discriminated item consumed by {@link accumulateRuntimeActionResults}
64
+ * so the loop can process interleaved deliveries and results without
65
+ * coupling to a concrete `HookPayload` shape.
74
66
  */
75
67
  type RuntimeActionAccumulatorItem<TDeliver> = {
76
68
  readonly kind: "deliver";
@@ -80,14 +72,15 @@ type RuntimeActionAccumulatorItem<TDeliver> = {
80
72
  readonly results: readonly RuntimeActionResult[];
81
73
  };
82
74
  /**
83
- * Accumulates runtime-action results from a mixed stream of items until
84
- * every pending action in the batch has a matching result.
75
+ * Accumulates runtime-action results until every pending key has a
76
+ * matching result. The caller passes the ordered key list so the
77
+ * workflow runtime can drive the loop without hydrating a session.
85
78
  */
86
79
  export declare function accumulateRuntimeActionResults<TDeliver>(input: {
87
80
  readonly bufferedDeliveries: TDeliver[];
88
81
  readonly getNext: () => Promise<RuntimeActionAccumulatorItem<TDeliver> | null>;
89
82
  readonly initialResults?: readonly RuntimeActionResult[];
90
- readonly session: HarnessSession;
83
+ readonly pendingActionKeys: readonly string[] | undefined;
91
84
  }): Promise<RuntimeActionResult[] | null>;
92
85
  /**
93
86
  * Resolves one pending runtime-action batch back into model history.
@@ -1 +1 @@
1
- import{createActionResultEvent}from"#protocol/message.js";import{parseJsonObject}from"#shared/json.js";import{clearProxyInputRequestsForChild}from"#harness/proxy-input-requests.js";import{getRuntimeActionRequestKey,getRuntimeActionResultKey}from"#runtime/actions/keys.js";const PENDING_RUNTIME_ACTION_BATCH_KEY=`ash.runtime.pendingActionBatch`;function getPendingRuntimeActionBatch(e){let t=e.state?.[PENDING_RUNTIME_ACTION_BATCH_KEY];if(typeof t!=`object`||!t)return;let n=t;if(!(!Array.isArray(n.actions)||!Array.isArray(n.responseMessages)||typeof n.event!=`object`||n.event===null))return n}function hasPendingRuntimeActionBatch(e){return getPendingRuntimeActionBatch(e)!==void 0}function setPendingRuntimeActionBatch(e){let t={...e.session.state};return t[PENDING_RUNTIME_ACTION_BATCH_KEY]={actions:[...e.actions],event:e.event,responseMessages:[...e.responseMessages]},{...e.session,state:t}}function recordPendingSubagentChildToken(e){let t=getPendingRuntimeActionBatch(e.session);if(t===void 0)return e.session;let n={...e.session.state};return n[PENDING_RUNTIME_ACTION_BATCH_KEY]={...t,childContinuationTokens:{...t.childContinuationTokens,[e.callId]:e.childContinuationToken}},{...e.session,state:n}}async function accumulateRuntimeActionResults(e){let t=getPendingRuntimeActionBatch(e.session),n=[...e.initialResults??[]];if(t!==void 0&&n.length>0){let e=resolveRuntimeActionResultsForBatch({batch:t,results:n});if(e!==void 0)return e}for(;;){let r=await e.getNext();if(r===null)return null;if(r.kind===`deliver`){e.bufferedDeliveries.push(r.value);continue}if(n.push(...r.results),t===void 0)continue;let i=resolveRuntimeActionResultsForBatch({batch:t,results:n});if(i!==void 0)return i}}function resolveReadyRuntimeActionResults(e){let t=getPendingRuntimeActionBatch(e.session);if(t!==void 0)return resolveRuntimeActionResultsForBatch({batch:t,results:e.results})}function resolveRuntimeActionResultsForBatch(e){let{batch:t}=e,n=new Set(t.actions.map(e=>getRuntimeActionRequestKey(e))),a=new Map;for(let t of e.results){let e=getRuntimeActionResultKey(t);n.has(e)&&a.set(e,t)}let o=[];for(let e of t.actions){let t=getRuntimeActionRequestKey(e),n=a.get(t);if(n===void 0)return;o.push(n)}return o}async function resolvePendingRuntimeActions(t){let r=getPendingRuntimeActionBatch(t.session);if(r===void 0)return{messages:[...t.session.history],outcome:`continue`,session:t.session};let i=resolveReadyRuntimeActionResults({results:t.stepInput?.runtimeActionResults??[],session:t.session});if(i===void 0)return{messages:[...t.session.history],outcome:`unresolved`,session:t.session};if(t.emit!==void 0)for(let n of i)n.kind===`subagent-result`&&n.isError!==!0&&await t.emit({data:{callId:n.callId,output:typeof n.output==`string`?n.output:JSON.stringify(n.output),subagentName:n.subagentName},type:`subagent.completed`}),await t.emit(createActionResultEvent({result:n,sequence:r.event.sequence,stepIndex:r.event.stepIndex,turnId:r.event.turnId}));let o={...t.session.state};delete o[PENDING_RUNTIME_ACTION_BATCH_KEY];let s={...t.session,state:Object.keys(o).length>0?o:void 0},c=r.childContinuationTokens;if(c!==void 0)for(let e of i){if(e.kind!==`subagent-result`)continue;let t=c[e.callId];t!==void 0&&(s=clearProxyInputRequestsForChild(s,t))}let l=i.map(e=>{switch(e.kind){case`load-skill-result`:return{output:toToolResultOutput(e),toolCallId:e.callId,toolName:`load_skill`,type:`tool-result`};case`subagent-result`:return{output:toToolResultOutput(e),toolCallId:e.callId,toolName:e.subagentName,type:`tool-result`};case`tool-result`:return{output:toToolResultOutput(e),toolCallId:e.callId,toolName:e.toolName,type:`tool-result`}}throw Error(`Unsupported runtime action result kind "${String(e)}".`)}),u=[...s.history,...r.responseMessages];return l.length>0&&u.push({content:l,role:`tool`}),{messages:u,outcome:`resolved`,session:s}}function createRuntimeActionRequestFromToolCall(e){let t=e.tools.get(e.toolCall.toolName);return t?.runtimeAction?.kind===`subagent-call`?{callId:e.toolCall.toolCallId,description:t.description,input:resolveToolCallInputObject(e.toolCall.input),kind:`subagent-call`,name:t.name,nodeId:t.runtimeAction.nodeId,subagentName:t.runtimeAction.subagentName}:t?.runtimeAction?.kind===`remote-agent-call`?{callId:e.toolCall.toolCallId,description:t.description,input:resolveToolCallInputObject(e.toolCall.input),kind:`remote-agent-call`,name:t.name,nodeId:t.runtimeAction.nodeId,remoteAgentName:t.runtimeAction.remoteAgentName??t.name}:{callId:e.toolCall.toolCallId,input:resolveToolCallInputObject(e.toolCall.input),kind:`tool-call`,toolName:e.toolCall.toolName}}function resolveToolCallInputObject(e){return e==null?{}:parseJsonObject(e)}function toToolResultOutput(e){return typeof e.output==`string`?e.isError===!0?{type:`error-text`,value:e.output}:{type:`text`,value:e.output}:e.isError===!0?{type:`error-json`,value:toMutableJsonValue(e.output)}:{type:`json`,value:toMutableJsonValue(e.output)}}function toMutableJsonValue(e){if(e===null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`)return e;if(Array.isArray(e))return e.map(e=>toMutableJsonValue(e));let t={};for(let[n,r]of Object.entries(e))t[n]=toMutableJsonValue(r);return t}export{accumulateRuntimeActionResults,createRuntimeActionRequestFromToolCall,getPendingRuntimeActionBatch,hasPendingRuntimeActionBatch,recordPendingSubagentChildToken,resolvePendingRuntimeActions,setPendingRuntimeActionBatch};
1
+ import{createActionResultEvent}from"#protocol/message.js";import{parseJsonObject}from"#shared/json.js";import{clearProxyInputRequestsForChild}from"#harness/proxy-input-requests.js";import{getRuntimeActionRequestKey,getRuntimeActionResultKey}from"#runtime/actions/keys.js";const PENDING_RUNTIME_ACTION_BATCH_KEY=`ash.runtime.pendingActionBatch`;function getPendingRuntimeActionBatch(e){let t=e?.[PENDING_RUNTIME_ACTION_BATCH_KEY];if(typeof t!=`object`||!t)return;let n=t;if(!(!Array.isArray(n.actions)||!Array.isArray(n.responseMessages)||typeof n.event!=`object`||n.event===null))return n}function hasPendingRuntimeActionBatch(e){return getPendingRuntimeActionBatch(e)!==void 0}function setPendingRuntimeActionBatch(e){let t={...e.session.state};return t[PENDING_RUNTIME_ACTION_BATCH_KEY]={actions:[...e.actions],event:e.event,responseMessages:[...e.responseMessages]},{...e.session,state:t}}function recordPendingSubagentChildToken(e){let t=getPendingRuntimeActionBatch(e.session.state);if(t===void 0)return e.session;let n={...e.session.state};return n[PENDING_RUNTIME_ACTION_BATCH_KEY]={...t,childContinuationTokens:{...t.childContinuationTokens,[e.callId]:e.childContinuationToken}},{...e.session,state:n}}async function accumulateRuntimeActionResults(e){let t=e.pendingActionKeys,n=[...e.initialResults??[]];if(t!==void 0&&n.length>0){let e=resolveRuntimeActionResultsForKeys({pendingKeys:t,results:n});if(e!==void 0)return e}for(;;){let r=await e.getNext();if(r===null)return null;if(r.kind===`deliver`){e.bufferedDeliveries.push(r.value);continue}if(n.push(...r.results),t===void 0)continue;let i=resolveRuntimeActionResultsForKeys({pendingKeys:t,results:n});if(i!==void 0)return i}}function resolveReadyRuntimeActionResults(e){let t=getPendingRuntimeActionBatch(e.session.state);if(t!==void 0)return resolveRuntimeActionResultsForBatch({batch:t,results:e.results})}function resolveRuntimeActionResultsForBatch(e){return resolveRuntimeActionResultsForKeys({pendingKeys:e.batch.actions.map(e=>getRuntimeActionRequestKey(e)),results:e.results})}function resolveRuntimeActionResultsForKeys(e){let t=new Set(e.pendingKeys),n=new Map;for(let r of e.results){let e=getRuntimeActionResultKey(r);t.has(e)&&n.set(e,r)}let r=[];for(let t of e.pendingKeys){let e=n.get(t);if(e===void 0)return;r.push(e)}return r}async function resolvePendingRuntimeActions(t){let r=getPendingRuntimeActionBatch(t.session.state);if(r===void 0)return{messages:[...t.session.history],outcome:`continue`,session:t.session};let i=resolveReadyRuntimeActionResults({results:t.stepInput?.runtimeActionResults??[],session:t.session});if(i===void 0)return{messages:[...t.session.history],outcome:`unresolved`,session:t.session};if(t.emit!==void 0)for(let n of i)n.kind===`subagent-result`&&n.isError!==!0&&await t.emit({data:{callId:n.callId,output:typeof n.output==`string`?n.output:JSON.stringify(n.output),subagentName:n.subagentName},type:`subagent.completed`}),await t.emit(createActionResultEvent({result:n,sequence:r.event.sequence,stepIndex:r.event.stepIndex,turnId:r.event.turnId}));let a={...t.session.state};delete a[PENDING_RUNTIME_ACTION_BATCH_KEY];let o={...t.session,state:Object.keys(a).length>0?a:void 0},s=r.childContinuationTokens;if(s!==void 0)for(let e of i){if(e.kind!==`subagent-result`)continue;let t=s[e.callId];t!==void 0&&(o=clearProxyInputRequestsForChild(o,t))}let c=i.map(e=>{switch(e.kind){case`load-skill-result`:return{output:toToolResultOutput(e),toolCallId:e.callId,toolName:`load_skill`,type:`tool-result`};case`subagent-result`:return{output:toToolResultOutput(e),toolCallId:e.callId,toolName:e.subagentName,type:`tool-result`};case`tool-result`:return{output:toToolResultOutput(e),toolCallId:e.callId,toolName:e.toolName,type:`tool-result`}}throw Error(`Unsupported runtime action result kind "${String(e)}".`)}),l=[...o.history,...r.responseMessages];return c.length>0&&l.push({content:c,role:`tool`}),{messages:l,outcome:`resolved`,session:o}}function createRuntimeActionRequestFromToolCall(e){let t=e.tools.get(e.toolCall.toolName);return t?.runtimeAction?.kind===`subagent-call`?{callId:e.toolCall.toolCallId,description:t.description,input:resolveToolCallInputObject(e.toolCall.input),kind:`subagent-call`,name:t.name,nodeId:t.runtimeAction.nodeId,subagentName:t.runtimeAction.subagentName}:t?.runtimeAction?.kind===`remote-agent-call`?{callId:e.toolCall.toolCallId,description:t.description,input:resolveToolCallInputObject(e.toolCall.input),kind:`remote-agent-call`,name:t.name,nodeId:t.runtimeAction.nodeId,remoteAgentName:t.runtimeAction.remoteAgentName??t.name}:{callId:e.toolCall.toolCallId,input:resolveToolCallInputObject(e.toolCall.input),kind:`tool-call`,toolName:e.toolCall.toolName}}function resolveToolCallInputObject(e){return e==null?{}:parseJsonObject(e)}function toToolResultOutput(e){return typeof e.output==`string`?e.isError===!0?{type:`error-text`,value:e.output}:{type:`text`,value:e.output}:e.isError===!0?{type:`error-json`,value:toMutableJsonValue(e.output)}:{type:`json`,value:toMutableJsonValue(e.output)}}function toMutableJsonValue(e){if(e===null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`)return e;if(Array.isArray(e))return e.map(e=>toMutableJsonValue(e));let t={};for(let[n,r]of Object.entries(e))t[n]=toMutableJsonValue(r);return t}export{accumulateRuntimeActionResults,createRuntimeActionRequestFromToolCall,getPendingRuntimeActionBatch,hasPendingRuntimeActionBatch,recordPendingSubagentChildToken,resolvePendingRuntimeActions,setPendingRuntimeActionBatch};
@@ -1 +1 @@
1
- import{createErrorId,createLogger,formatError,recordErrorOnSpan}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent}from"#protocol/message.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{ConnectionRegistryKey,DiscoveredConnectionToolsKey}from"#runtime/framework-tools/connection-search.js";import{ToolLoopAgent,isStepCount}from"ai";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.js";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{resolveAssistantStepText}from"#harness/messages.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{resolveConnectionToolsFromState}from"#runtime/framework-tools/connection-tools.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.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";import{buildToolSetWithProviderTools}from"#harness/tools.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);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,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let o=a===void 0?void 0:trace.getTracer(`ash`),s=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(o&&hasStepInput(t)){let t=a?.functionId??s,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId,"ash.continuation_token":e.continuationToken};t&&(r[`ai.telemetry.functionId`]=t),n=o.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(o,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(o,c,l){let d=o;l&&(d=setTurnTraceState(d,l.spanContext()));let h=getHarnessEmissionState(d),w=consumeDeferredStepInput({input:c,session:d});d=w.session;let E=await resolvePendingRuntimeActions({emit:n,session:d,stepInput:w.input});if(E.outcome===`unresolved`)return{next:null,session:E.session};d=E.session;let D=resolvePendingInput({history:E.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:d,stepInput:w.input});if(D.outcome===`unresolved`)return{next:null,session:D.session};n&&hasStepInput(c)&&(h=await emitTurnPreamble(n,c??{},h,t.runtimeIdentity),d=setHarnessEmissionState(d,h),l&&l.setAttribute(`ash.turn.id`,h.turnId)),d=D.session;let O=D.messages;if(w.input?.message!==void 0&&!D.deferredMessage){let e=await stageAttachmentsToSandbox(w.input.message);O.push({content:e,role:`user`})}let k=await t.resolveModel(d.agent.modelReference),A=detectPromptCachePath(k),j=A.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,M=buildGatewayAttributionHeaders(k,t.runtimeIdentity);({messages:O,session:d}=await maybeCompact({emit:n,emissionState:h,headers:M,messages:O,model:k,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:d,telemetry:enrichTelemetry(a,s)??void 0}));let N=getApprovedTools(d),P=contextStorage.getStore(),F=P?.get(ConnectionRegistryKey),I=P?.get(DiscoveredConnectionToolsKey),L=await hydrateSandboxAttachments(O),R=w.input?.modelContext,z=[],B=[];for(let e of L)e.role===`system`?z.push(e):B.push(e);if(R!==void 0)for(let e of R)e.role===`system`?z.push(e):B.push(e);let V=B,runOneModelCall=async e=>{let r=await buildToolSetWithProviderTools({approvedTools:N,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:d.agent.modelReference,tools:t.tools});if(F!==void 0&&I!==void 0){let e=await resolveConnectionToolsFromState(F,I,{approvedTools:N,existingToolNames:new Set(Object.keys(r))});Object.assign(r,e)}let i=j?applyLastToolCacheBreakpoint(r,j):r,o=resolveGatewayPinForStep({cachePath:A,modelReference:d.agent.modelReference,tools:i}),c=e.extraSystemNote?[{role:`system`,content:e.extraSystemNote}]:[],l=d.agent.system?[{role:`system`,content:d.agent.system}]:[],u=z.length>0||c.length>0?[...c,...l,...z]:d.agent.system||void 0,f=buildStepHooks({cachePath:A,emit:n,emissionState:h,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:o,marker:j,session:d}),p=new ToolLoopAgent({headers:M,instructions:u,model:k,onError(){},onStepFinish:f.onStepFinish,prepareStep:f.prepareStep,runtimeContext:buildTelemetryRuntimeContext(a,d),stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,s),tools:i});return runModelCallWithRetries(async()=>{if(n){let e=await p.stream({messages:V}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,h,e.fullStream),a=await f.stepResult;return await emitStepActions(n,h,a,{excludedActionToolNames:new Set([ASK_QUESTION_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 p.generate({messages:V}),await f.stepResult},{sessionId:d.sessionId,turnId:h.turnId})},H;try{H=await runOneModelCall({})}catch(t){let a=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:d.sessionId,turnId:h.turnId});if(a.outcome===`recovered`)H=a.result;else{let t=a.error;if(l&&recordErrorOnSpan(l,t),!n)throw t;let o=classifyModelCallError(t),s=createErrorId(),c=o===`terminal`?summarizeKnownModelCallConfigError(t):null,u=c===null?summarizeKnownModelCallRequestError(t):null,m=c?.message??u?.message??toErrorMessage(t),g=extractModelCallErrorDetails(t),_=buildModelCallFailureDetails({configSummary:c,error:t,errorId:s,modelCallDetails:g,requestSummary:u}),v=buildModelCallFailureLogFields({error:t,errorId:s,modelCallDetails:g,requestSummary:u,sessionId:d.sessionId,turnId:h.turnId});return o===`terminal`?(c===null?log.error(u?.message??`model call failed terminally`,v):log.error(`${c.name}: ${c.message}`,{errorId:s,sessionId:d.sessionId,turnId:h.turnId}),await emitFailedStep(n,h,{code:`MODEL_CALL_FAILED`,details:_,message:m,sessionId:d.sessionId}),{next:{done:!0,output:``},session:d}):(log.error(u?.message??`model call failed — parking session for retry by the user`,v),h=await emitRecoverableFailedTurn(n,h,{code:`MODEL_CALL_FAILED`,details:_,message:m}),{next:null,session:setHarnessEmissionState(d,h)})}}return handleStepResult({config:t,emit:n,emissionState:h,promptMessages:O,result:H,runStep,session:d})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:a,modelCallDetails:o,requestSummary:s}=e;return t===null?s===null?{...formatError(r,a),...o}:{errorId:a,message:toErrorMessage(r),name:s.name,...o}:{errorId:a,message:t.message,name:t.name,...o}}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:o,session:c}=e,l=i.response.messages,u=resolveAssistantStepText(l,i.text),f=extractToolApprovalInputRequests({content:i.content??[]}),p=new Set(f.map(e=>e.action.callId)),m=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:p}),g=[...f,...m],_={...c,compaction:createNextCompactionConfig(c.compaction,r,i)},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:setPendingRuntimeActionBatch({actions:v,event:{sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId},responseMessages:l,session:{..._,history:[...r]}})};if(g.length>0){let e=setPendingInputBatch({requests:g,responseMessages:l,session:{..._,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:g,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),t.mode===`conversation`&&(o=await emitTurnEpilogue(n,o,t.mode),e=setHarnessEmissionState(e,o))),{next:null,session:e}}let y=pruneToolResults(r,t.retentionPolicies),b=y!==r,x=_.compaction;b&&x.lastKnownInputTokens!==void 0&&(x={recentWindowSize:x.recentWindowSize,threshold:x.threshold});let S=[...y,...l],C={..._,compaction:x,history:S},T=l.at(-1)?.role===`tool`||hasDeferredStepInput(C);return n&&(o=T?advanceStep(o):await emitTurnEpilogue(n,o,t.mode),C=setHarnessEmissionState(C,o)),T?{next:a,session:C}:{next:t.mode===`task`?{done:!0,output:u??``}:null,session:C}}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 s=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(s.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,s.model,i.compaction,s.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(s.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,recordErrorOnSpan}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{createCompactionCompletedEvent,createCompactionRequestedEvent,createInputRequestedEvent}from"#protocol/message.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{formatLanguageModelGatewayId}from"#internal/runtime-model.js";import{contextStorage}from"#context/container.js";import{advanceStep,emitFailedStep,emitRecoverableFailedTurn,emitStreamContent,emitTurnEpilogue,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{ConnectionRegistryKey,DiscoveredConnectionToolsKey}from"#runtime/framework-tools/connection-search.js";import{ToolLoopAgent,isStepCount}from"ai";import{createRuntimeActionRequestFromToolCall,resolvePendingRuntimeActions,setPendingRuntimeActionBatch}from"#harness/runtime-actions.js";import{resolveAssistantStepText}from"#harness/messages.js";import{consumeDeferredStepInput,getApprovedTools,hasDeferredStepInput,hasStepInput,resolvePendingInput,setPendingInputBatch}from"#harness/input-requests.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{resolveConnectionToolsFromState}from"#runtime/framework-tools/connection-tools.js";import{hydrateSandboxAttachments,stageAttachmentsToSandbox}from"#harness/attachment-staging.js";import{compactMessages,getInputTokenCount,resolveCompactionModel,shouldCompact}from"#harness/compaction.js";import{getInstrumentationConfig}from"#harness/instrumentation-config.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";import{buildToolSetWithProviderTools}from"#harness/tools.js";const environment=process.env.NODE_ENV??`unknown`,ashVersion=resolveInstalledPackageInfo().version,log=createLogger(`harness.tool-loop`);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,a=getInstrumentationConfig();a!==void 0&&ensureOtelIntegration();let o=a===void 0?void 0:trace.getTracer(`ash`),s=t.runtimeIdentity?.agentName;async function runStep(e,t){let n;if(o&&hasStepInput(t)){let t=a?.functionId??s,r={"ash.version":ashVersion,"ash.environment":environment,"ash.session.id":e.sessionId,"ash.continuation_token":e.continuationToken};t&&(r[`ai.telemetry.functionId`]=t),n=o.startSpan(`ai.ash.turn`,{attributes:r})}let r=resolveStepOtelContext(o,n,e),executeStep=()=>executeStepBody(e,t,n);try{return r?await context.with(r,executeStep):await executeStep()}finally{n?.end()}}async function executeStepBody(o,c,l){let d=o;l&&(d=setTurnTraceState(d,l.spanContext()));let h=getHarnessEmissionState(d.state),S=consumeDeferredStepInput({input:c,session:d});d=S.session;let w=await resolvePendingRuntimeActions({emit:n,session:d,stepInput:S.input});if(w.outcome===`unresolved`)return{next:null,session:w.session};d=w.session;let T=resolvePendingInput({history:w.messages,resolveApprovalKey:resolveApprovalKeyFromTools(t.tools),session:d,stepInput:S.input});if(T.outcome===`unresolved`)return{next:null,session:T.session};n&&hasStepInput(c)&&(h=await emitTurnPreamble(n,c??{},h,t.runtimeIdentity),d=setHarnessEmissionState(d,h),l&&l.setAttribute(`ash.turn.id`,h.turnId)),d=T.session;let O=T.messages;if(S.input?.message!==void 0&&!T.deferredMessage){let e=await stageAttachmentsToSandbox(S.input.message);O.push({content:e,role:`user`})}let k=await t.resolveModel(d.agent.modelReference),A=detectPromptCachePath(k),j=A.kind===`anthropic-direct`?getAnthropicCacheMarker():void 0,M=buildGatewayAttributionHeaders(k,t.runtimeIdentity);({messages:O,session:d}=await maybeCompact({emit:n,emissionState:h,headers:M,messages:O,model:k,onCompaction:t.onCompaction,resolveModel:t.resolveModel,session:d,telemetry:enrichTelemetry(a,s)??void 0}));let N=getApprovedTools(d),P=contextStorage.getStore(),F=P?.get(ConnectionRegistryKey),I=P?.get(DiscoveredConnectionToolsKey),L=await hydrateSandboxAttachments(O),R=S.input?.modelContext,z=[],B=[];for(let e of L)e.role===`system`?z.push(e):B.push(e);if(R!==void 0)for(let e of R)e.role===`system`?z.push(e):B.push(e);let V=B,runOneModelCall=async e=>{let r=await buildToolSetWithProviderTools({approvedTools:N,capabilities:t.capabilities,disabledProviderTools:e.disabledProviderTools,modelReference:d.agent.modelReference,tools:t.tools});if(F!==void 0&&I!==void 0){let e=await resolveConnectionToolsFromState(F,I,{approvedTools:N,existingToolNames:new Set(Object.keys(r))});Object.assign(r,e)}let i=j?applyLastToolCacheBreakpoint(r,j):r,o=resolveGatewayPinForStep({cachePath:A,modelReference:d.agent.modelReference,tools:i}),c=e.extraSystemNote?[{role:`system`,content:e.extraSystemNote}]:[],l=d.agent.system?[{role:`system`,content:d.agent.system}]:[],u=z.length>0||c.length>0?[...c,...l,...z]:d.agent.system||void 0,f=buildStepHooks({cachePath:A,emit:n,emissionState:h,emitStepStarted:e.suppressStepStartedEmission!==!0,gatewayPinProvider:o,marker:j,session:d}),p=new ToolLoopAgent({headers:M,instructions:u,model:k,onError(){},onStepFinish:f.onStepFinish,prepareStep:f.prepareStep,runtimeContext:buildTelemetryRuntimeContext(a,d),stopWhen:isStepCount(1),telemetry:enrichTelemetry(a,s),tools:i});return runModelCallWithRetries(async()=>{if(n){let e=await p.stream({messages:V}),{inlineActionResultCallIds:r,inlineToolResultParts:i}=await emitStreamContent(n,h,e.fullStream),a=await f.stepResult;return await emitStepActions(n,h,a,{excludedActionToolNames:new Set([ASK_QUESTION_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 p.generate({messages:V}),await f.stepResult},{sessionId:d.sessionId,turnId:h.turnId})},H;try{H=await runOneModelCall({})}catch(t){let a=await attemptUnsupportedProviderToolRecovery({error:t,runOneModelCall,sessionId:d.sessionId,turnId:h.turnId});if(a.outcome===`recovered`)H=a.result;else{let t=a.error;if(l&&recordErrorOnSpan(l,t),!n)throw t;let o=classifyModelCallError(t),s=createErrorId(),c=o===`terminal`?summarizeKnownModelCallConfigError(t):null,u=c===null?summarizeKnownModelCallRequestError(t):null,m=c?.message??u?.message??toErrorMessage(t),g=extractModelCallErrorDetails(t),_=buildModelCallFailureDetails({configSummary:c,error:t,errorId:s,modelCallDetails:g,requestSummary:u}),v=buildModelCallFailureLogFields({error:t,errorId:s,modelCallDetails:g,requestSummary:u,sessionId:d.sessionId,turnId:h.turnId});return o===`terminal`?(c===null?log.error(u?.message??`model call failed terminally`,v):log.error(`${c.name}: ${c.message}`,{errorId:s,sessionId:d.sessionId,turnId:h.turnId}),await emitFailedStep(n,h,{code:`MODEL_CALL_FAILED`,details:_,message:m,sessionId:d.sessionId}),{next:{done:!0,output:``},session:d}):(log.error(u?.message??`model call failed — parking session for retry by the user`,v),h=await emitRecoverableFailedTurn(n,h,{code:`MODEL_CALL_FAILED`,details:_,message:m}),{next:null,session:setHarnessEmissionState(d,h)})}}return handleStepResult({config:t,emit:n,emissionState:h,promptMessages:O,result:H,runStep,session:d})}return runStep}function buildModelCallFailureDetails(e){let{configSummary:t,error:r,errorId:a,modelCallDetails:o,requestSummary:s}=e;return t===null?s===null?{...formatError(r,a),...o}:{errorId:a,message:toErrorMessage(r),name:s.name,...o}:{errorId:a,message:t.message,name:t.name,...o}}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:o,session:c}=e,l=i.response.messages,u=resolveAssistantStepText(l,i.text),f=extractToolApprovalInputRequests({content:i.content??[]}),p=new Set(f.map(e=>e.action.callId)),m=extractQuestionInputRequests({toolCalls:i.toolCalls,excludedCallIds:p}),g=[...f,...m],_={...c,compaction:createNextCompactionConfig(c.compaction,r,i)},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:setPendingRuntimeActionBatch({actions:v,event:{sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId},responseMessages:l,session:{..._,history:[...r]}})};if(g.length>0){let e=setPendingInputBatch({requests:g,responseMessages:l,session:{..._,history:[...r]}});return n&&(await n(createInputRequestedEvent({requests:g,sequence:o.sequence,stepIndex:o.stepIndex,turnId:o.turnId})),t.mode===`conversation`&&(o=await emitTurnEpilogue(n,o,t.mode),e=setHarnessEmissionState(e,o))),{next:null,session:e}}let y=pruneToolResults(r,t.retentionPolicies),b=y!==r,x=_.compaction;b&&x.lastKnownInputTokens!==void 0&&(x={recentWindowSize:x.recentWindowSize,threshold:x.threshold});let C=[...y,...l],E={..._,compaction:x,history:C},D=l.at(-1)?.role===`tool`||hasDeferredStepInput(E);return n&&(o=D?advanceStep(o):await emitTurnEpilogue(n,o,t.mode),E=setHarnessEmissionState(E,o)),D?{next:a,session:E}:{next:t.mode===`task`?{done:!0,output:u??``}:null,session:E}}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 s=await resolveCompactionModel({compactionModelReference:i.agent.compactionModelReference,model:e.model,modelReference:i.agent.modelReference,resolveModel:e.resolveModel});if(t&&await t(createCompactionRequestedEvent({modelId:formatLanguageModelGatewayId(s.model),sequence:n.sequence,sessionId:i.sessionId,turnId:n.turnId,usageInputTokens:getInputTokenCount(r,i.compaction)})),r=await compactMessages(r,s.model,i.compaction,s.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(s.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};
@@ -16,6 +16,8 @@ import type { RetentionPolicy } from "#harness/tool-result-pruning.js";
16
16
  * across workflow step boundaries.
17
17
  */
18
18
  export type SessionToolDefinition = Readonly<InternalToolDefinition>;
19
+ /** Authored-key → opaque-value map stored on `session.state`. */
20
+ export type SessionStateMap = Readonly<Record<string, unknown>>;
19
21
  /**
20
22
  * Compaction configuration stored on the session.
21
23
  */
@@ -52,7 +54,7 @@ export interface HarnessSession {
52
54
  readonly history: ModelMessage[];
53
55
  readonly sessionId: string;
54
56
  readonly sandboxState?: SandboxState;
55
- readonly state?: Readonly<Record<string, unknown>>;
57
+ readonly state?: SessionStateMap;
56
58
  }
57
59
  /**
58
60
  * Result returned by the compaction callback.