experimental-ash 0.29.0 → 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.
- package/CHANGELOG.md +34 -0
- package/dist/src/context/hook-lifecycle.d.ts +23 -67
- package/dist/src/context/hook-lifecycle.js +1 -1
- package/dist/src/execution/await-authorization-orchestrator.d.ts +18 -29
- package/dist/src/execution/await-authorization-orchestrator.js +1 -1
- package/dist/src/execution/connection-auth-steps.d.ts +28 -65
- package/dist/src/execution/connection-auth-steps.js +1 -1
- package/dist/src/execution/create-session-step.d.ts +15 -0
- package/dist/src/execution/create-session-step.js +1 -0
- package/dist/src/execution/delegated-parent-notification.d.ts +15 -0
- package/dist/src/execution/delegated-parent-notification.js +1 -0
- package/dist/src/execution/delegated-parent-result.d.ts +14 -0
- package/dist/src/execution/delegated-parent-result.js +1 -0
- package/dist/src/execution/dispatch-runtime-actions-step.d.ts +20 -0
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -0
- package/dist/src/execution/durable-session-migrations/chain.d.ts +31 -0
- package/dist/src/execution/durable-session-migrations/chain.js +1 -0
- package/dist/src/execution/durable-session-migrations/snapshot.d.ts +22 -0
- package/dist/src/execution/durable-session-migrations/snapshot.js +1 -0
- package/dist/src/execution/durable-session-store.d.ts +104 -0
- package/dist/src/execution/durable-session-store.js +1 -0
- package/dist/src/execution/next-driver-action.d.ts +30 -0
- package/dist/src/execution/next-driver-action.js +1 -0
- package/dist/src/execution/session.d.ts +29 -28
- package/dist/src/execution/session.js +1 -1
- package/dist/src/execution/subagent-hitl-proxy.d.ts +8 -25
- package/dist/src/execution/subagent-hitl-proxy.js +1 -1
- package/dist/src/execution/subagent-tool.js +1 -1
- package/dist/src/execution/turn-workflow.d.ts +21 -21
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-entry.d.ts +12 -16
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-steps.d.ts +36 -91
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/emission.d.ts +3 -5
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/input-requests.d.ts +3 -3
- package/dist/src/harness/input-requests.js +1 -1
- package/dist/src/harness/proxy-input-requests.d.ts +12 -16
- package/dist/src/harness/proxy-input-requests.js +1 -1
- package/dist/src/harness/runtime-actions.d.ts +17 -24
- package/dist/src/harness/runtime-actions.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/harness/types.d.ts +3 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/nitro/routes/info.js +1 -1
- package/dist/src/internal/workflow-bundle/builder.d.ts +2 -0
- package/dist/src/internal/workflow-bundle/builder.js +1 -1
- package/dist/src/runtime/framework-channels/index.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
*
|
|
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" | "
|
|
16
|
+
readonly action: "continue" | "done";
|
|
13
17
|
readonly output?: string;
|
|
14
18
|
readonly serializedContext: Record<string, unknown>;
|
|
15
|
-
readonly
|
|
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
|
|
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
|
|
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
|
-
*
|
|
38
|
-
* `
|
|
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
|
|
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
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
109
|
-
*
|
|
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
|
|
120
|
-
}): Promise<
|
|
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{
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
5
|
-
*
|
|
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(
|
|
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(
|
|
12
|
+
export declare function hasProxyInputRequests(state: SessionStateMap | undefined): boolean;
|
|
15
13
|
/**
|
|
16
|
-
* Replaces
|
|
17
|
-
* provided
|
|
18
|
-
*
|
|
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
|
|
27
|
-
*
|
|
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
|
|
33
|
-
* `(requestId, childContinuationToken)` tuples the
|
|
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
|
|
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
|
|
19
|
-
* to the deterministic child
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
-
|
|
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(
|
|
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
|
|
58
|
-
*
|
|
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
|
-
*
|
|
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
|
|
84
|
-
*
|
|
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
|
|
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
|
|
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?:
|
|
57
|
+
readonly state?: SessionStateMap;
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
60
|
* Result returned by the compaction callback.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.
|
|
1
|
+
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.30.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
|