experimental-ash 0.47.0 → 0.49.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 +36 -0
- package/dist/docs/internals/context.md +6 -12
- package/dist/docs/internals/hooks.md +15 -74
- package/dist/docs/internals/mechanical-invariants.md +3 -4
- package/dist/docs/internals/message-runtime.md +2 -3
- package/dist/docs/public/advanced/hooks.mdx +39 -76
- package/dist/docs/public/advanced/project-layout.md +1 -2
- package/dist/docs/public/advanced/typescript-api.md +1 -1
- package/dist/docs/public/channels/index.md +2 -2
- package/dist/docs/public/channels/slack.mdx +12 -17
- package/dist/docs/public/frontend/use-ash-agent.md +13 -17
- package/dist/src/channel/adapter.js +1 -1
- package/dist/src/channel/routes.d.ts +5 -7
- package/dist/src/channel/send.js +1 -1
- package/dist/src/channel/types.d.ts +3 -3
- package/dist/src/compiler/manifest.d.ts +1 -1
- package/dist/src/compiler/normalize-hook.d.ts +4 -4
- package/dist/src/context/build-dynamic-tools.d.ts +13 -0
- package/dist/src/context/build-dynamic-tools.js +1 -0
- package/dist/src/context/dynamic-instruction-lifecycle.d.ts +13 -13
- package/dist/src/context/dynamic-instruction-lifecycle.js +1 -1
- package/dist/src/context/dynamic-resolve-context.d.ts +12 -0
- package/dist/src/context/dynamic-resolve-context.js +1 -0
- package/dist/src/context/dynamic-skill-lifecycle.js +1 -1
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +4 -10
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/hook-lifecycle.d.ts +1 -46
- package/dist/src/context/hook-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +30 -32
- package/dist/src/context/keys.js +1 -1
- package/dist/src/execution/create-session-step.d.ts +3 -4
- package/dist/src/execution/create-session-step.js +1 -1
- package/dist/src/execution/dispatch-runtime-actions-step.d.ts +2 -3
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
- package/dist/src/execution/durable-session-store.d.ts +24 -24
- package/dist/src/execution/durable-session-store.js +1 -1
- package/dist/src/execution/turn-workflow.d.ts +4 -5
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-runtime.d.ts +1 -1
- package/dist/src/execution/workflow-steps.d.ts +1 -3
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/code-mode.js +1 -1
- package/dist/src/harness/compaction.js +1 -1
- package/dist/src/harness/messages.js +1 -1
- package/dist/src/harness/prompt-cache.d.ts +11 -1
- package/dist/src/harness/prompt-cache.js +1 -1
- package/dist/src/harness/step-hooks.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/harness/types.d.ts +4 -5
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/public/channels/ash.js +2 -2
- package/dist/src/public/channels/discord/discordChannel.d.ts +1 -2
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/discord/inbound.d.ts +0 -3
- package/dist/src/public/channels/discord/inbound.js +1 -1
- package/dist/src/public/channels/discord/index.d.ts +1 -1
- package/dist/src/public/channels/discord/index.js +1 -1
- package/dist/src/public/channels/slack/inbound.d.ts +4 -13
- package/dist/src/public/channels/slack/inbound.js +1 -1
- package/dist/src/public/channels/slack/slackChannel.d.ts +3 -7
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/inbound.d.ts +0 -3
- package/dist/src/public/channels/teams/inbound.js +2 -2
- package/dist/src/public/channels/teams/index.d.ts +1 -1
- package/dist/src/public/channels/teams/index.js +1 -1
- package/dist/src/public/channels/teams/teamsChannel.d.ts +1 -2
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/inbound.d.ts +0 -3
- package/dist/src/public/channels/telegram/inbound.js +1 -1
- package/dist/src/public/channels/telegram/index.d.ts +1 -1
- package/dist/src/public/channels/telegram/index.js +1 -1
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +1 -2
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/inbound.d.ts +0 -3
- package/dist/src/public/channels/twilio/inbound.js +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/definitions/hook.d.ts +6 -22
- package/dist/src/public/definitions/instructions.d.ts +7 -12
- package/dist/src/public/definitions/skill.js +1 -1
- package/dist/src/public/hooks/index.d.ts +4 -5
- package/dist/src/runtime/graph.d.ts +2 -3
- package/dist/src/runtime/hooks/registry.d.ts +5 -20
- package/dist/src/runtime/hooks/registry.js +1 -1
- package/dist/src/runtime/resolve-hook.d.ts +2 -2
- package/dist/src/runtime/resolve-hook.js +1 -1
- package/dist/src/runtime/types.d.ts +3 -9
- package/dist/src/shared/dynamic-tool-definition.d.ts +20 -0
- package/dist/src/shared/dynamic-tool-definition.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { UserContent } from "ai";
|
|
2
2
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
3
3
|
import type { RunMode } from "#shared/run-mode.js";
|
|
4
4
|
import type { RuntimeActionResult } from "#runtime/actions/types.js";
|
|
@@ -70,7 +70,7 @@ export type EventEmitFn = (event: HandleMessageStreamEvent) => Promise<void>;
|
|
|
70
70
|
export interface DeliverPayload {
|
|
71
71
|
readonly inputResponses?: readonly InputResponse[];
|
|
72
72
|
readonly message?: string | UserContent;
|
|
73
|
-
readonly
|
|
73
|
+
readonly context?: readonly string[];
|
|
74
74
|
readonly outputSchema?: JsonObject;
|
|
75
75
|
readonly [key: string]: unknown;
|
|
76
76
|
}
|
|
@@ -215,7 +215,7 @@ export interface RunInput {
|
|
|
215
215
|
readonly initiatorAuth?: SessionAuthContext | null;
|
|
216
216
|
readonly input: {
|
|
217
217
|
readonly message: string | UserContent;
|
|
218
|
-
readonly
|
|
218
|
+
readonly context?: readonly string[];
|
|
219
219
|
readonly outputSchema?: JsonObject;
|
|
220
220
|
};
|
|
221
221
|
readonly mode: RunMode;
|
|
@@ -129,7 +129,7 @@ export interface CompiledDynamicInstructionsDefinition extends ModuleSourceRef {
|
|
|
129
129
|
/**
|
|
130
130
|
* Normalized authored hook entry preserved in the compiled manifest.
|
|
131
131
|
*
|
|
132
|
-
* Hook
|
|
132
|
+
* Hook event handlers are arbitrary functions — there is no static
|
|
133
133
|
* shape the compiler can validate beyond the source ref. Per-handler
|
|
134
134
|
* resolution happens at runtime via {@link resolveHookDefinition}.
|
|
135
135
|
*/
|
|
@@ -4,9 +4,9 @@ import type { CompiledHookDefinition } from "./manifest.js";
|
|
|
4
4
|
* Compiles one authored hook module into the manifest entry stored on
|
|
5
5
|
* the compiled agent node.
|
|
6
6
|
*
|
|
7
|
-
* Hook
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Hook event handlers are arbitrary functions and cannot be statically
|
|
8
|
+
* validated at compile time — the normalization step only derives the
|
|
9
|
+
* path-relative slug used for diagnostics and ordering. Per-handler
|
|
10
|
+
* validation lives in the runtime resolver.
|
|
11
11
|
*/
|
|
12
12
|
export declare function compileHookEntry(source: ModuleSourceRef): CompiledHookDefinition;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HarnessToolDefinition } from "#harness/execute-tool.js";
|
|
2
|
+
import type { ContextKey } from "#context/key.js";
|
|
3
|
+
/**
|
|
4
|
+
* Builds live dynamic tool definitions. Narrower scopes appear first
|
|
5
|
+
* so they win on name collision (the tool-loop uses `??=` for dedup).
|
|
6
|
+
*
|
|
7
|
+
* Step tools are live closures (re-resolved every step via
|
|
8
|
+
* `LiveStepToolsKey`). Session/turn tools are replayed from durable
|
|
9
|
+
* metadata via the bundler's registered step functions.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildDynamicTools(ctx: {
|
|
12
|
+
get<T>(key: ContextKey<T>): T | undefined;
|
|
13
|
+
}): readonly HarnessToolDefinition[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{LiveStepToolsKey,SessionDynamicToolMetadataKey,TurnDynamicToolMetadataKey}from"#context/keys.js";import{jsonSchema}from"ai";import{buildCallbackContext}from"#context/build-callback-context.js";const log=createLogger(`dynamic-tools`);function lookupStepFunction(e){try{let t=globalThis[Symbol.for(`@workflow/core//registeredSteps`)];return t===void 0?null:t.get(e)||null}catch{return null}}function replayTools(e){let t=[];for(let n of e){if(!n.executeStepFnName||!n.closureVars){log.warn(`Dynamic tool "${n.name}" has no registered step function — skipping on this step. The bundler transform may not have processed this tool file.`);continue}let e=lookupStepFunction(n.executeStepFnName);if(!e){log.warn(`Dynamic tool "${n.name}" references step function "${n.executeStepFnName}" which is not registered — skipping on this step.`);continue}t.push({description:n.description,execute:t=>e(n.closureVars,t,buildCallbackContext()),inputSchema:jsonSchema(n.inputSchema),name:n.name})}return t}function buildDynamicTools(e){let r=e.get(LiveStepToolsKey)??[],i=replayTools(e.get(TurnDynamicToolMetadataKey)??[]),a=replayTools(e.get(SessionDynamicToolMetadataKey)??[]);return[...r,...i,...a]}export{buildDynamicTools};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import type { ModelMessage } from "ai";
|
|
1
|
+
import type { ModelMessage, SystemModelMessage } from "ai";
|
|
2
2
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
3
3
|
import type { ResolvedDynamicInstructionsResolver } from "#runtime/types.js";
|
|
4
4
|
import type { ContextContainer } from "#context/container.js";
|
|
5
|
-
import { ContextKey } from "#context/key.js";
|
|
5
|
+
import type { ContextKey } from "#context/key.js";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* messages. Read by the tool-loop to inject into the next model call.
|
|
10
|
-
*
|
|
11
|
-
* Virtual keys are cleared at workflow step boundaries, so messages
|
|
12
|
-
* are never persisted or duplicated across steps.
|
|
7
|
+
* Builds the flattened system messages from session + turn durable keys.
|
|
8
|
+
* Session-scoped entries appear first.
|
|
13
9
|
*/
|
|
14
|
-
export declare
|
|
10
|
+
export declare function buildDynamicInstructionMessages(ctx: {
|
|
11
|
+
get<T>(key: ContextKey<T>): T | undefined;
|
|
12
|
+
}): SystemModelMessage[];
|
|
15
13
|
/**
|
|
16
|
-
* Dispatches a stream event to dynamic instruction resolvers.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* Dispatches a stream event to dynamic instruction resolvers.
|
|
15
|
+
*
|
|
16
|
+
* Each resolver's output replaces its own slot (keyed by slug) in the
|
|
17
|
+
* scope-appropriate durable key (session or turn). The tool-loop calls
|
|
18
|
+
* {@link buildDynamicInstructionMessages} to assemble the flattened
|
|
19
|
+
* result for the model call.
|
|
20
20
|
*/
|
|
21
21
|
export declare function dispatchDynamicInstructionEvent(input: {
|
|
22
22
|
readonly ctx: ContextContainer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{SessionDynamicInstructionsKey,TurnDynamicInstructionsKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_INSTRUCTION_EVENTS,isBrandedInstructionsEntry}from"#shared/dynamic-tool-definition.js";import{buildResolveContext}from"#context/dynamic-resolve-context.js";const log=createLogger(`dynamic-instructions`);function lowerToSystemMessage(e){let t=e.markdown.trim();if(t.length!==0)return{role:`system`,content:t}}function durableKeyForEvent(e){switch(e){case`session.started`:return SessionDynamicInstructionsKey;case`turn.started`:return TurnDynamicInstructionsKey;default:return}}function buildDynamicInstructionMessages(e){let r=e.get(SessionDynamicInstructionsKey)??{},i=e.get(TurnDynamicInstructionsKey)??{};return[...Object.values(r).flat(),...Object.values(i).flat()]}async function dispatchDynamicInstructionEvent(e){let{ctx:t,resolvers:n,event:a,messages:o}=e;if(!ALLOWED_DYNAMIC_INSTRUCTION_EVENTS.has(a.type))return;let s=n.filter(e=>e.eventNames.includes(a.type));if(s.length===0)return;let c=durableKeyForEvent(a.type);if(c===void 0)return;let l=buildResolveContext(t,o),u=await Promise.allSettled(s.map(async e=>{let t=e.events[a.type];if(t===void 0)return null;let n=await t(a,l);return n==null?{resolver:e,message:void 0}:isBrandedInstructionsEntry(n)?{resolver:e,message:lowerToSystemMessage(n)}:(log.error(`Dynamic instructions resolver "${e.slug}" returned an unbranded value — wrap with defineInstructions().`),null)})),d={...t.get(c)};for(let e of u){if(e.status===`rejected`){log.error(`Dynamic instructions resolver (${a.type}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}if(e.value===null)continue;let{resolver:t,message:n}=e.value;n===void 0?delete d[t.slug]:d[t.slug]=[n]}t.set(c,d)}export{buildDynamicInstructionMessages,dispatchDynamicInstructionEvent};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ModelMessage } from "ai";
|
|
2
|
+
import type { DynamicResolveContext } from "#shared/dynamic-tool-definition.js";
|
|
3
|
+
import type { AlsContext } from "#context/container.js";
|
|
4
|
+
type ReadableContext = Pick<AlsContext, "get">;
|
|
5
|
+
/**
|
|
6
|
+
* Builds the {@link DynamicResolveContext} from the active ALS context.
|
|
7
|
+
*
|
|
8
|
+
* Shared by all three dynamic lifecycle dispatchers (tools, skills,
|
|
9
|
+
* instructions) so resolver handlers receive a consistent context shape.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildResolveContext(ctx: ReadableContext, messages: readonly ModelMessage[]): DynamicResolveContext;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getAdapterKind}from"#channel/adapter.js";import{AuthKey,ContinuationTokenKey,InitiatorAuthKey,SessionIdKey}from"#context/keys.js";import{ChannelKey}from"#runtime/sessions/runtime-context-keys.js";function buildResolveContext(e,t){let n=e.get(SessionIdKey)??``,r=e.get(AuthKey)??null,i=e.get(InitiatorAuthKey)??null,a=e.get(ChannelKey),o=e.get(ContinuationTokenKey);return{session:{id:n,auth:{current:r,initiator:i}},channel:{kind:a===void 0?void 0:getAdapterKind(a),continuationToken:o},messages:t}}export{buildResolveContext};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{DynamicSkillManifestKey,SandboxKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_SKILL_EVENTS,isBrandedSkillEntry}from"#shared/dynamic-tool-definition.js";import{normalizeSkillPackage,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{ContextKey}from"#context/key.js";import{buildResolveContext}from"#context/dynamic-resolve-context.js";import{BundleKey}from"#runtime/sessions/runtime-context-keys.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";const log=createLogger(`dynamic-skills`);function qualifyDynamicSkillNames(e,t,n){let r=Object.keys(n),i=[];if(r.length===0)return i;if(t||r.length===1)return i.push({name:e,entryKey:r[0],entry:n[r[0]]}),i;for(let t of r)i.push({name:`${e}__${t}`,entryKey:t,entry:n[t]});return i}function formatDynamicSkillAnnouncement(e){return formatAvailableSkillsSection(Object.values(e).flat())??``}const PendingSkillAnnouncementKey=new ContextKey(`ash.pendingSkillAnnouncement`);async function dispatchDynamicSkillEvent(e){let{ctx:a,resolvers:o,event:s,messages:c}=e;if(a.get(PendingSkillAnnouncementKey)===void 0){let e=a.get(DynamicSkillManifestKey);e!==void 0&&Object.keys(e).length>0&&a.setVirtualContext(PendingSkillAnnouncementKey,formatDynamicSkillAnnouncement(e))}if(!ALLOWED_DYNAMIC_SKILL_EVENTS.has(s.type))return;let l=o.filter(e=>e.eventNames.includes(s.type));if(l.length===0)return;let u=buildResolveContext(a,c),d=new Set(a.require(BundleKey).resolvedAgent.skills.map(e=>e.name)),f=a.get(DynamicSkillManifestKey)??{},p=[],m=await Promise.allSettled(l.map(async e=>{let t=e.events[s.type];if(t===void 0)return null;let n=await t(s,u);if(n==null)return{resolver:e,named:[]};let r,i;return isBrandedSkillEntry(n)?(r={_single:n},i=!0):(r=n,i=!1),{resolver:e,named:qualifyDynamicSkillNames(e.slug,i,r)}}));for(let e of m){if(e.status===`rejected`){log.error(`Dynamic skill resolver (${s.type}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}e.value!==null&&p.push({resolver:e.value.resolver,skills:e.value.named.map(({name:e,entry:t})=>normalizeSkillPackage({...t,name:e}))})}if(p.length===0)return;let h={...f};for(let{resolver:e,skills:t}of p)t.length===0?delete h[e.slug]:h[e.slug]=t.map(e=>({description:e.description,name:e.name}));let g=new Map;for(let[e,t]of Object.entries(h))for(let{name:n}of t){if(d.has(n))throw Error(`Dynamic skill "${n}" from resolver "${e}" conflicts with an authored skill.`);let t=g.get(n);if(t!==void 0)throw Error(`Dynamic skill "${n}" from resolver "${e}" conflicts with dynamic resolver "${t}".`);g.set(n,e)}let _=await a.require(SandboxKey).get();if(_!==null)for(let{skills:e}of p)for(let t of e)await writeSkillPackageToSandbox({sandbox:_,skill:t});a.set(DynamicSkillManifestKey,h),a.setVirtualContext(PendingSkillAnnouncementKey,formatDynamicSkillAnnouncement(h))}export{PendingSkillAnnouncementKey,dispatchDynamicSkillEvent};
|
|
@@ -12,16 +12,10 @@ import type { DurableDynamicToolMetadata } from "#context/keys.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function replayDynamicSessionTools(metadata: readonly DurableDynamicToolMetadata[], _resolvers: readonly ResolvedDynamicToolResolver[]): readonly HarnessToolDefinition[];
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* the virtual key is empty (workflow step boundary crossed).
|
|
20
|
-
* 2. **Resolve phase**: run resolver handlers for the current event,
|
|
21
|
-
* capture closures as durable metadata, produce live tools.
|
|
22
|
-
*
|
|
23
|
-
* The tool-loop reads {@link DynamicToolsKey} right before each model
|
|
24
|
-
* call. Events update it; the tool-loop picks up whatever is current.
|
|
15
|
+
* Dispatches a stream event to dynamic tool resolvers. Each
|
|
16
|
+
* resolver's metadata replaces its slot (by slug) in the
|
|
17
|
+
* scope-appropriate durable key. The tool-loop calls
|
|
18
|
+
* {@link buildDynamicTools} to assemble the effective toolset.
|
|
25
19
|
*/
|
|
26
20
|
export declare function dispatchDynamicToolEvent(input: {
|
|
27
21
|
readonly ctx: ContextContainer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"#internal/logging.js";import{
|
|
1
|
+
import{createLogger}from"#internal/logging.js";import{LiveStepToolsKey,SessionDynamicToolMetadataKey,TurnDynamicToolMetadataKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_TOOL_EVENTS,isBrandedToolEntry}from"#shared/dynamic-tool-definition.js";import{jsonSchema,zodSchema}from"ai";import{buildCallbackContext}from"#context/build-callback-context.js";import{buildResolveContext}from"#context/dynamic-resolve-context.js";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";const log=createLogger(`dynamic-tools`);function toHarnessToolDefinition(e,t){return{description:t.description,execute:e=>t.execute(e,buildCallbackContext()),inputSchema:convertInputSchema(t.inputSchema),name:e,...t.toModelOutput===void 0?{}:{toModelOutput:t.toModelOutput}}}function convertInputSchema(e){return typeof e==`object`&&e&&`~standard`in e?zodSchema(e):jsonSchema(e)}function qualifyDynamicToolNames(e,t,n){let r=Object.keys(n),i=[];if(r.length===0)return i;if(t)return i.push({name:e,entryKey:r[0],entry:n[r[0]]}),i;for(let t of r)i.push({name:`${e}__${t}`,entryKey:t,entry:n[t]});return i}function replayDynamicSessionTools(e,t){let n=[];for(let t of e){if(!t.executeStepFnName||!t.closureVars){log.warn(`Dynamic tool "${t.name}" has no registered step function — skipping on this step. The bundler transform may not have processed this tool file.`);continue}let e=lookupStepFunction(t.executeStepFnName);if(!e){log.warn(`Dynamic tool "${t.name}" references step function "${t.executeStepFnName}" which is not registered — skipping on this step.`);continue}n.push({description:t.description,execute:n=>e(t.closureVars,n,buildCallbackContext()),inputSchema:jsonSchema(t.inputSchema),name:t.name})}return n}function lookupStepFunction(e){try{let t=globalThis[Symbol.for(`@workflow/core//registeredSteps`)];return t===void 0?null:t.get(e)||null}catch{return null}}function safeSerialize(e){try{return JSON.parse(JSON.stringify(e))}catch{return{}}}function durableKeyForEvent(e){switch(e){case`session.started`:return SessionDynamicToolMetadataKey;case`turn.started`:return TurnDynamicToolMetadataKey;default:return}}async function resolveToolsFromEvent(e,t,n,r){let a=await Promise.allSettled(t.map(async t=>{let i=t.events[n.type];if(i===void 0)return null;let a=await i(n,buildResolveContext(e,r));if(a==null)return null;let s,c;return isBrandedToolEntry(a)?(s={_single:a},c=!0):(s=a,c=!1),{resolver:t,entries:s,isSingle:c}})),s=[],c=[];for(let e of a){if(e.status===`rejected`){log.error(`Dynamic tool resolver (${n.type}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}if(e.value===null)continue;let{resolver:t,entries:r,isSingle:a}=e.value,o=qualifyDynamicToolNames(t.slug,a,r);for(let{name:e,entryKey:n,entry:r}of o){c.push(toHarnessToolDefinition(e,r));let i=`__executeStepFn`in r?r.__executeStepFn:void 0,a=`__closureVars`in r?r.__closureVars:void 0;s.push({name:e,description:r.description,inputSchema:normalizeJsonSchemaDefinition(r.inputSchema),resolverSlug:t.slug,entryKey:n,executeStepFnName:i?.stepId,closureVars:a===void 0?void 0:safeSerialize(a)})}}return{metadata:s,liveTools:c}}async function dispatchDynamicToolEvent(e){let{ctx:n,resolvers:r,event:i,messages:o}=e;if(!ALLOWED_DYNAMIC_TOOL_EVENTS.has(i.type))return;let s=r.filter(e=>e.eventNames.includes(i.type));if(s.length===0)return;let{metadata:c,liveTools:l}=await resolveToolsFromEvent(n,s,i,o);if(i.type===`step.started`){n.setVirtualContext(LiveStepToolsKey,l);return}let u=durableKeyForEvent(i.type);if(u===void 0)return;let d=new Set(s.map(e=>e.slug)),f=(n.get(u)??[]).filter(e=>!d.has(e.resolverSlug));n.set(u,[...f,...c])}export{dispatchDynamicToolEvent,replayDynamicSessionTools};
|
|
@@ -1,45 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { HandleMessageStreamEvent, RuntimeIdentity } from "#protocol/message.js";
|
|
3
|
-
import type { RunMode } from "#shared/run-mode.js";
|
|
1
|
+
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
4
2
|
import type { RuntimeHookRegistry } from "#runtime/hooks/registry.js";
|
|
5
3
|
import type { ContextContainer } from "./container.js";
|
|
6
|
-
/**
|
|
7
|
-
* Outcome of {@link dispatchHookLifecycle}. `proceed` carries the
|
|
8
|
-
* input augmented with merged `modelContext`. `turn-failed` means a
|
|
9
|
-
* `lifecycle.turn` hook threw; the recoverable `turn.failed` cascade
|
|
10
|
-
* has already been emitted.
|
|
11
|
-
*/
|
|
12
|
-
export type HookLifecycleOutcome = {
|
|
13
|
-
readonly kind: "proceed";
|
|
14
|
-
readonly input: StepInput;
|
|
15
|
-
readonly nextSession: HarnessSession;
|
|
16
|
-
} | {
|
|
17
|
-
readonly kind: "turn-failed";
|
|
18
|
-
readonly message: string;
|
|
19
|
-
readonly nextSession: HarnessSession;
|
|
20
|
-
};
|
|
21
|
-
export interface DispatchHookLifecycleInput {
|
|
22
|
-
readonly ctx: ContextContainer;
|
|
23
|
-
readonly registry: RuntimeHookRegistry;
|
|
24
|
-
readonly session: HarnessSession;
|
|
25
|
-
readonly input: StepInput;
|
|
26
|
-
readonly emit: HarnessEmitFn;
|
|
27
|
-
readonly mode: RunMode;
|
|
28
|
-
readonly runtimeIdentity?: RuntimeIdentity;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Runs the per-turn hook lifecycle inside the active ALS scope.
|
|
32
|
-
*
|
|
33
|
-
* - `lifecycle.session` runs once per session, gated by
|
|
34
|
-
* {@link SessionPreparedKey}. The flag is set **before** the chain
|
|
35
|
-
* runs so a thrown hook does not retry. A throw re-propagates and
|
|
36
|
-
* the runtime escalates to terminal `session.failed`.
|
|
37
|
-
* - `lifecycle.turn` runs once per fresh delivery. Each hook may
|
|
38
|
-
* return `{ modelContext }`; model-context contributions are
|
|
39
|
-
* concatenated in registry order. A thrown hook emits the recoverable
|
|
40
|
-
* `turn.failed` cascade and returns `kind: "turn-failed"`.
|
|
41
|
-
*/
|
|
42
|
-
export declare function dispatchHookLifecycle(input: DispatchHookLifecycleInput): Promise<HookLifecycleOutcome>;
|
|
43
4
|
/**
|
|
44
5
|
* Fans one runtime stream event out to every matching subscriber.
|
|
45
6
|
* Errors propagate — harness error paths convert them into the
|
|
@@ -51,9 +12,3 @@ export declare function dispatchStreamEventHooks(input: {
|
|
|
51
12
|
readonly registry: RuntimeHookRegistry;
|
|
52
13
|
readonly event: HandleMessageStreamEvent;
|
|
53
14
|
}): Promise<void>;
|
|
54
|
-
/**
|
|
55
|
-
* Runs the per-turn hook lifecycle, lowers a `turn-failed` outcome
|
|
56
|
-
* into a parking {@link StepResult}, and otherwise hands off to
|
|
57
|
-
* `body` with the (possibly augmented) input.
|
|
58
|
-
*/
|
|
59
|
-
export declare function runHookLifecycleStep(input: DispatchHookLifecycleInput, body: (session: HarnessSession, input: StepInput) => Promise<StepResult>): Promise<StepResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ContinuationTokenKey
|
|
1
|
+
import{ContinuationTokenKey}from"./keys.js";import{getAdapterKind}from"#channel/adapter.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";async function dispatchStreamEventHooks(e){let t=e.registry.streamEventsByType.get(e.event.type)??[],n=e.registry.streamEventsWildcard;if(t.length===0&&n.length===0)return;let r=buildHookContext(e.ctx);for(let n of t)await n.handler(e.event,r);for(let t of n)await t.handler(e.event,r)}function buildHookContext(i){let a=i.require(BundleKey),o=i.get(ChannelKey),s=i.get(ContinuationTokenKey),c=o===void 0?void 0:getAdapterKind(o);return{...buildCallbackContext(),agent:{name:a.resolvedAgent.config.name??`agent`,nodeId:a.nodeId},channel:{kind:c,continuationToken:s}}}export{dispatchStreamEventHooks};
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* tier. Codec-carrying keys (`ChannelKey`, `BundleKey`) live in
|
|
4
4
|
* `#runtime/sessions/runtime-context-keys.ts`.
|
|
5
5
|
*/
|
|
6
|
+
import type { SystemModelMessage } from "ai";
|
|
7
|
+
import type { JsonObject } from "#shared/json.js";
|
|
6
8
|
import type { ChannelInstrumentationProjection, SessionAuthContext, SessionCallback, SessionCapabilities, SessionParent, SessionTurn } from "#channel/types.js";
|
|
7
9
|
import { ContextKey } from "#context/key.js";
|
|
8
10
|
import type { SandboxAccess } from "#sandbox/state.js";
|
|
@@ -45,48 +47,34 @@ export declare const CapabilitiesKey: ContextKey<SessionCapabilities>;
|
|
|
45
47
|
* Optional framework-owned terminal callback metadata for this session.
|
|
46
48
|
*/
|
|
47
49
|
export declare const SessionCallbackKey: ContextKey<SessionCallback>;
|
|
48
|
-
/**
|
|
49
|
-
* Marker durable boolean set by the runtime **before** the
|
|
50
|
-
* `lifecycle.session` hook chain runs. The runtime checks this flag at
|
|
51
|
-
* the top of every turn so the chain never runs twice for the same
|
|
52
|
-
* session — including when one of the hooks throws. A thrown hook is
|
|
53
|
-
* a terminal session failure (`session.failed`); the next turn does
|
|
54
|
-
* not retry. See `research/active/hooks.md`.
|
|
55
|
-
*/
|
|
56
|
-
export declare const SessionPreparedKey: ContextKey<boolean>;
|
|
57
50
|
export declare const SessionKey: ContextKey<Session>;
|
|
58
51
|
export declare const SandboxKey: ContextKey<SandboxAccess>;
|
|
59
|
-
/**
|
|
60
|
-
* Virtual (non-serialized) live dynamic tool definitions. Updated by
|
|
61
|
-
* {@link dispatchDynamicToolEvent} whenever a subscribed stream event
|
|
62
|
-
* fires. Read by the tool-loop when building the effective toolset.
|
|
63
|
-
*/
|
|
64
|
-
export declare const DynamicToolsKey: ContextKey<import("#harness/execute-tool.js").HarnessToolDefinition[]>;
|
|
65
|
-
/**
|
|
66
|
-
* Durable (serialized) metadata for session-scoped dynamic tools.
|
|
67
|
-
* Set on the first step when resolvers run. Read on subsequent steps
|
|
68
|
-
* to reconstruct tool definitions with lazy execute wrappers.
|
|
69
|
-
*/
|
|
70
52
|
export interface DurableDynamicToolMetadata {
|
|
71
53
|
readonly name: string;
|
|
72
54
|
readonly description: string;
|
|
73
|
-
readonly inputSchema:
|
|
55
|
+
readonly inputSchema: JsonObject;
|
|
74
56
|
readonly resolverSlug: string;
|
|
75
57
|
readonly entryKey: string;
|
|
76
|
-
/**
|
|
77
|
-
* Name of the hoisted execute step function (e.g.
|
|
78
|
-
* `__ash_dynamic_exec_0`). Used to look up the registered step
|
|
79
|
-
* function via `getStepFunction` on replay.
|
|
80
|
-
*/
|
|
81
58
|
readonly executeStepFnName?: string;
|
|
82
|
-
/**
|
|
83
|
-
* Serialized closure variables captured from the resolver scope on
|
|
84
|
-
* the first run. Passed as the `__vars` parameter to the hoisted
|
|
85
|
-
* step function on replay.
|
|
86
|
-
*/
|
|
87
59
|
readonly closureVars?: Record<string, unknown>;
|
|
88
60
|
}
|
|
89
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Session-scoped dynamic tool metadata (from `session.started`).
|
|
63
|
+
* Persists for the session lifetime.
|
|
64
|
+
*/
|
|
65
|
+
export declare const SessionDynamicToolMetadataKey: ContextKey<readonly DurableDynamicToolMetadata[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Turn-scoped dynamic tool metadata (from `turn.started`).
|
|
68
|
+
* Replaced each turn.
|
|
69
|
+
*/
|
|
70
|
+
export declare const TurnDynamicToolMetadataKey: ContextKey<readonly DurableDynamicToolMetadata[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Virtual (non-serialized) live step-scoped tool definitions from
|
|
73
|
+
* `step.started` resolvers. Carries original execute closures so
|
|
74
|
+
* framework tools (which lack bundler step-function metadata) work.
|
|
75
|
+
* Re-resolved every step — no cross-step persistence needed.
|
|
76
|
+
*/
|
|
77
|
+
export declare const LiveStepToolsKey: ContextKey<import("#harness/execute-tool.js").HarnessToolDefinition[]>;
|
|
90
78
|
/**
|
|
91
79
|
* Durable metadata for one session-scoped dynamic skill.
|
|
92
80
|
*/
|
|
@@ -100,3 +88,13 @@ export interface DurableDynamicSkillMetadata {
|
|
|
100
88
|
* and rebuild the model-visible announcement across turns.
|
|
101
89
|
*/
|
|
102
90
|
export declare const DynamicSkillManifestKey: ContextKey<Record<string, readonly DurableDynamicSkillMetadata[]>>;
|
|
91
|
+
/**
|
|
92
|
+
* Durable session-scoped instruction messages (from `session.started`
|
|
93
|
+
* resolvers). Keyed by resolver slug. Persists for the session lifetime.
|
|
94
|
+
*/
|
|
95
|
+
export declare const SessionDynamicInstructionsKey: ContextKey<Record<string, readonly SystemModelMessage[]>>;
|
|
96
|
+
/**
|
|
97
|
+
* Durable turn-scoped instruction messages (from `turn.started`
|
|
98
|
+
* resolvers). Keyed by resolver slug. Replaced each turn.
|
|
99
|
+
*/
|
|
100
|
+
export declare const TurnDynamicInstructionsKey: ContextKey<Record<string, readonly SystemModelMessage[]>>;
|
package/dist/src/context/keys.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ContextKey}from"#context/key.js";const AuthKey=new ContextKey(`ash.auth`),InitiatorAuthKey=new ContextKey(`ash.initiatorAuth`),SessionIdKey=new ContextKey(`ash.sessionId`),ContinuationTokenKey=new ContextKey(`ash.continuationToken`),ChannelInstrumentationKey=new ContextKey(`ash.channelInstrumentation`),ModeKey=new ContextKey(`ash.mode`),ParentSessionKey=new ContextKey(`ash.parentSession`),CapabilitiesKey=new ContextKey(`ash.capabilities`),SessionCallbackKey=new ContextKey(`ash.sessionCallback`),
|
|
1
|
+
import{ContextKey}from"#context/key.js";const AuthKey=new ContextKey(`ash.auth`),InitiatorAuthKey=new ContextKey(`ash.initiatorAuth`),SessionIdKey=new ContextKey(`ash.sessionId`),ContinuationTokenKey=new ContextKey(`ash.continuationToken`),ChannelInstrumentationKey=new ContextKey(`ash.channelInstrumentation`),ModeKey=new ContextKey(`ash.mode`),ParentSessionKey=new ContextKey(`ash.parentSession`),CapabilitiesKey=new ContextKey(`ash.capabilities`),SessionCallbackKey=new ContextKey(`ash.sessionCallback`),SessionKey=new ContextKey(`ash.session`),SandboxKey=new ContextKey(`ash.sandbox`),SessionDynamicToolMetadataKey=new ContextKey(`ash.sessionDynamicToolMetadata`),TurnDynamicToolMetadataKey=new ContextKey(`ash.turnDynamicToolMetadata`),LiveStepToolsKey=new ContextKey(`ash.liveStepTools`),DynamicSkillManifestKey=new ContextKey(`ash.dynamicSkillManifest`),SessionDynamicInstructionsKey=new ContextKey(`ash.sessionDynamicInstructions`),TurnDynamicInstructionsKey=new ContextKey(`ash.turnDynamicInstructions`);export{AuthKey,CapabilitiesKey,ChannelInstrumentationKey,ContinuationTokenKey,DynamicSkillManifestKey,InitiatorAuthKey,LiveStepToolsKey,ModeKey,ParentSessionKey,SandboxKey,SessionCallbackKey,SessionDynamicInstructionsKey,SessionDynamicToolMetadataKey,SessionIdKey,SessionKey,TurnDynamicInstructionsKey,TurnDynamicToolMetadataKey};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RuntimeCompiledArtifactsSource } from "#runtime/compiled-artifacts-source.js";
|
|
2
|
-
import { type
|
|
2
|
+
import { type DurableSessionState } from "#execution/durable-session-store.js";
|
|
3
3
|
import type { JsonObject } from "#shared/json.js";
|
|
4
4
|
/**
|
|
5
5
|
* Result returned by {@link createSessionStep}.
|
|
@@ -11,8 +11,8 @@ export interface CreateSessionStepResult {
|
|
|
11
11
|
readonly state: DurableSessionState;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Creates the durable session and
|
|
15
|
-
*
|
|
14
|
+
* Creates the durable session and returns the initial snapshot-bearing
|
|
15
|
+
* state before the workflow enters its turn loop.
|
|
16
16
|
* `nodeId` targets a subagent node in the compiled graph; omitted for
|
|
17
17
|
* the root agent.
|
|
18
18
|
*
|
|
@@ -40,5 +40,4 @@ export declare function createSessionStep(input: {
|
|
|
40
40
|
*/
|
|
41
41
|
readonly serializedContext: Record<string, unknown>;
|
|
42
42
|
readonly sessionId: string;
|
|
43
|
-
readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
|
|
44
43
|
}): Promise<CreateSessionStepResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ROOT_RUNTIME_AGENT_NODE_ID}from"#runtime/graph.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{
|
|
1
|
+
import{ROOT_RUNTIME_AGENT_NODE_ID}from"#runtime/graph.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";import{createDurableSessionState}from"#execution/durable-session-store.js";import{createSession}from"#execution/session.js";import{buildSessionAttributes,buildSubagentRootAttributes,readParentSessionId}from"#execution/ash-workflow-attributes.js";import{setAshAttributes}from"#runtime/attributes/emit.js";async function createSessionStep(e){"use step";let t=await getCompiledRuntimeAgentBundle({compiledArtifactsSource:e.compiledArtifactsSource,nodeId:e.nodeId}),n=createDurableSessionState({session:createSession({compactionOverrides:{thresholdPercent:t.resolvedAgent.config.compaction?.thresholdPercent},continuationToken:e.continuationToken,outputSchema:e.outputSchema,rootSessionId:e.rootSessionId,sessionId:e.sessionId,turnAgent:t.turnAgent})}),r={nodeId:t.nodeId??ROOT_RUNTIME_AGENT_NODE_ID},i=readParentSessionId(e.serializedContext);return i===void 0?await setAshAttributes(buildSessionAttributes({inputMessage:e.inputMessage,serializedContext:e.serializedContext})):await setAshAttributes(buildSubagentRootAttributes({identity:r,parentSessionId:i,rootSessionId:e.rootSessionId??i,serializedContext:e.serializedContext})),{state:n}}export{createSessionStep};
|
|
@@ -4,16 +4,15 @@
|
|
|
4
4
|
* Each child run starts in task mode, emits a parent `subagent.called`
|
|
5
5
|
* control-plane event, and then runs independently on its own child
|
|
6
6
|
* stream. Records each child's continuation token on the parent
|
|
7
|
-
* session and
|
|
7
|
+
* session and returns the updated snapshot-bearing state.
|
|
8
8
|
*/
|
|
9
9
|
import type { RuntimeSubagentResultActionResult } from "#runtime/actions/types.js";
|
|
10
|
-
import { type
|
|
10
|
+
import { type DurableSessionState } from "#execution/durable-session-store.js";
|
|
11
11
|
export declare function dispatchRuntimeActionsStep(input: {
|
|
12
12
|
readonly callbackBaseUrl?: string;
|
|
13
13
|
readonly parentWritable: WritableStream<Uint8Array>;
|
|
14
14
|
readonly serializedContext: Record<string, unknown>;
|
|
15
15
|
readonly sessionState: DurableSessionState;
|
|
16
|
-
readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
|
|
17
16
|
}): Promise<{
|
|
18
17
|
readonly results: readonly RuntimeSubagentResultActionResult[];
|
|
19
18
|
readonly sessionState: DurableSessionState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,logError}from"#internal/logging.js";import{callAdapterEventHandler}from"#channel/adapter.js";import{AuthKey,CapabilitiesKey,InitiatorAuthKey}from"#context/keys.js";import{createSubagentCalledEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{readDurableSession
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{callAdapterEventHandler}from"#channel/adapter.js";import{AuthKey,CapabilitiesKey,InitiatorAuthKey}from"#context/keys.js";import{createSubagentCalledEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{toErrorMessage}from"#shared/errors.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{createDurableSessionState,readDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession}from"#execution/session.js";import{deserializeContext}from"#context/serialize.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{getPendingRuntimeActionBatch,recordPendingSubagentChildToken}from"#harness/runtime-actions.js";import{resolveRemoteAgentForAction,startRemoteAgentSession}from"#execution/remote-agent-dispatch.js";import{buildSubagentRunInput}from"#execution/subagent-tool.js";import{createWorkflowRuntime,workflowEntryReference}from"#execution/workflow-runtime.js";const log=createLogger(`execution.dispatch-runtime-actions`);async function dispatchRuntimeActionsStep(e){"use step";let s=await readDurableSession(e.sessionState),u=getPendingRuntimeActionBatch(s.state);if(u===void 0||u.actions.length===0)return{results:[],sessionState:e.sessionState};let d=await deserializeContext(e.serializedContext),f=d.require(BundleKey),p=hydrateDurableSession({compactionOverrides:{thresholdPercent:f.resolvedAgent.config.compaction?.thresholdPercent},durable:s,turnAgent:f.turnAgent}),m=d.require(ChannelKey),h=d.get(AuthKey)??null,g=d.get(CapabilitiesKey),_=d.get(InitiatorAuthKey)??null,v=e.parentWritable.getWriter(),y=buildAdapterContext(m,d),b=p,x=[];try{for(let r of u.actions){let i,a,s,c;switch(r.kind){case`subagent-call`:{let e=createWorkflowRuntime({compiledArtifactsSource:f.compiledArtifactsSource,nodeId:r.nodeId}),{childContinuationToken:t,runInput:n}=buildSubagentRunInput({action:r,auth:h,batchEvent:u.event,capabilities:g,initiatorAuth:_,session:p}),o=await e.run(n);b=recordPendingSubagentChildToken({callId:r.callId,childContinuationToken:t,session:b}),i=o.sessionId,a=r.name,c=r.subagentName;break}case`remote-agent-call`:{let n;try{n=resolveRemoteAgentForAction({nodeId:r.nodeId,remoteAgentName:r.remoteAgentName,registry:f.subagentRegistry.subagentsByNodeId}),i=await startRemoteAgentSession({action:r,callbackBaseUrl:e.callbackBaseUrl,remote:n,session:p})}catch(e){logError(log,`remote agent start failed`,e,{remoteAgentName:r.remoteAgentName,nodeId:r.nodeId,callId:r.callId}),x.push(createRemoteAgentStartFailureResult({action:r,error:e}));continue}a=r.name,s={url:n.url},c=r.remoteAgentName;break}default:throw Error(`Unsupported runtime action kind "${r.kind}" in workflow runtime.`)}let l=await callAdapterEventHandler(m,createSubagentCalledEvent({callId:r.callId,childSessionId:i,name:a,remote:s,sequence:u.event.sequence,sessionId:p.sessionId,toolName:c,turnId:u.event.turnId,workflowId:workflowEntryReference.workflowId}),y);await v.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(l)))}}finally{v.releaseLock()}return{results:x,sessionState:b===p?e.sessionState:createDurableSessionState({session:b})}}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}}export{dispatchRuntimeActionsStep};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Durable session storage.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Session-mutating steps return the current snapshot inside
|
|
5
|
+
* {@link DurableSessionState}; Workflow step results are the atomic
|
|
6
|
+
* persistence boundary for session program memory. The legacy
|
|
7
|
+
* `"ash.session"` stream remains as a fallback for old in-flight
|
|
8
|
+
* sessions that only carry a small state handle.
|
|
9
9
|
*
|
|
10
10
|
* The driver workflow run is pinned to the deployment that called
|
|
11
11
|
* `start()`; child turn workflows run on latest. Both
|
|
@@ -20,15 +20,14 @@ import { type HarnessEmissionState } from "#harness/emission.js";
|
|
|
20
20
|
import type { HarnessSession, SessionStateMap } from "#harness/types.js";
|
|
21
21
|
import type { SandboxState } from "#sandbox/state.js";
|
|
22
22
|
import type { JsonObject } from "#shared/json.js";
|
|
23
|
-
/** Workflow stream namespace where every session snapshot lands. */
|
|
24
|
-
export declare const ASH_SESSION_STREAM_NAMESPACE = "ash.session";
|
|
25
23
|
/** Current wire version for {@link DurableSessionState} and {@link DurableSessionSnapshot}. */
|
|
26
24
|
export declare const DURABLE_SESSION_VERSION = 1;
|
|
27
25
|
/**
|
|
28
26
|
* Serializable handle to a durable session.
|
|
29
27
|
*
|
|
30
|
-
* Carries
|
|
31
|
-
* taking a step boundary: identity, the
|
|
28
|
+
* Carries the current session snapshot plus the small projections the
|
|
29
|
+
* workflow body needs without taking a step boundary: identity, the
|
|
30
|
+
* hook continuation token,
|
|
32
31
|
* `hasProxyInputRequests` (a closed-contract short-circuit that lets
|
|
33
32
|
* the driver skip a per-delivery proxy-routing step when no
|
|
34
33
|
* descendant subagent is active), and `emissionState` (so workflow-body
|
|
@@ -36,6 +35,8 @@ export declare const DURABLE_SESSION_VERSION = 1;
|
|
|
36
35
|
* with `{ turnId, sequence, stepIndex }` without reading the full
|
|
37
36
|
* durable session). All other control-plane state travels via
|
|
38
37
|
* {@link import("#execution/next-driver-action.js").NextDriverAction}.
|
|
38
|
+
* `snapshot` is optional so old stream-backed states can still read
|
|
39
|
+
* from the legacy `ash.session` fallback.
|
|
39
40
|
*/
|
|
40
41
|
export interface DurableSessionState {
|
|
41
42
|
readonly version: typeof DURABLE_SESSION_VERSION;
|
|
@@ -43,10 +44,11 @@ export interface DurableSessionState {
|
|
|
43
44
|
readonly continuationToken: string;
|
|
44
45
|
readonly hasProxyInputRequests: boolean;
|
|
45
46
|
readonly emissionState: HarnessEmissionState;
|
|
47
|
+
readonly snapshot?: DurableSessionSnapshot;
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
|
-
* Durable projection of {@link HarnessSession}
|
|
49
|
-
* `ash.session` stream.
|
|
50
|
+
* Durable projection of {@link HarnessSession} embedded in state
|
|
51
|
+
* snapshots or legacy `ash.session` stream chunks.
|
|
50
52
|
*
|
|
51
53
|
* Omits `agent.modelReference`, `agent.tools`,
|
|
52
54
|
* `agent.compactionModelReference`, and the `compaction` thresholds —
|
|
@@ -87,13 +89,14 @@ export declare function projectSessionState(input: {
|
|
|
87
89
|
readonly session: HarnessSession;
|
|
88
90
|
}): DurableSessionState;
|
|
89
91
|
/**
|
|
90
|
-
* Reads the latest
|
|
91
|
-
*
|
|
92
|
+
* Reads the latest durable session snapshot and returns the
|
|
93
|
+
* {@link DurableSession} inside.
|
|
92
94
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* return; unknown
|
|
95
|
+
* New states carry the snapshot directly through Workflow step
|
|
96
|
+
* results. States without `snapshot` fall back to the legacy
|
|
97
|
+
* `ash.session` stream tail (`startIndex: -1`). The snapshot is
|
|
98
|
+
* migrated to {@link DURABLE_SESSION_VERSION} before return; unknown
|
|
99
|
+
* versions throw.
|
|
97
100
|
*
|
|
98
101
|
* Devalue handles encode/decode so rich types in the session (URL
|
|
99
102
|
* `FilePart.data`, Buffer, Date, Map, Set) round-trip structurally.
|
|
@@ -102,12 +105,9 @@ export declare function projectSessionState(input: {
|
|
|
102
105
|
*/
|
|
103
106
|
export declare function readDurableSession(state: DurableSessionState): Promise<DurableSession>;
|
|
104
107
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* MUST be called from inside a `"use step"` body.
|
|
108
|
+
* Creates the projected {@link DurableSessionState} with the current
|
|
109
|
+
* snapshot embedded in the Workflow step result.
|
|
109
110
|
*/
|
|
110
|
-
export declare function
|
|
111
|
+
export declare function createDurableSessionState(input: {
|
|
111
112
|
readonly session: HarnessSession;
|
|
112
|
-
|
|
113
|
-
}): Promise<DurableSessionState>;
|
|
113
|
+
}): DurableSessionState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{projectToDurableSession}from"#execution/session.js";import{getHarnessEmissionState}from"#harness/emission.js";import{hasProxyInputRequests}from"#harness/proxy-input-requests.js";import{migrateDurableSessionSnapshot}from"#execution/durable-session-migrations/snapshot.js";const ASH_SESSION_STREAM_NAMESPACE=`ash.session`,DURABLE_SESSION_VERSION=1,DURABLE_SESSION_READ_TIMEOUT_MS=1e4;function projectSessionState(e){return{continuationToken:e.session.continuationToken,emissionState:getHarnessEmissionState(e.session.state),hasProxyInputRequests:hasProxyInputRequests(e.session.state),sessionId:e.session.sessionId,version:1}}async function readDurableSession(e){if(e.snapshot!==void 0)return migrateDurableSessionSnapshot(e.snapshot).session;let{getRun:t}=await import(`#compiled/@workflow/core/runtime.js`),n=t(e.sessionId).getReadable({namespace:ASH_SESSION_STREAM_NAMESPACE,startIndex:-1}).getReader(),r=`ash durable session tail read failed`,i;try{let t=await Promise.race([n.read().then(e=>({kind:`read`,read:e})),new Promise(e=>{i=setTimeout(()=>e({kind:`timeout`}),DURABLE_SESSION_READ_TIMEOUT_MS)})]);if(t.kind===`timeout`)throw r=`ash durable session tail read timed out after ${DURABLE_SESSION_READ_TIMEOUT_MS}ms`,new DurableSessionReadTimeoutError(e);if(t.read.done||t.read.value===void 0)throw r=`ash durable session tail read returned no snapshot`,Error(`No durable session snapshot found in stream "${ASH_SESSION_STREAM_NAMESPACE}" for run ${e.sessionId}.`);return r=`ash durable session tail read complete`,migrateDurableSessionSnapshot(t.read.value).session}finally{i!==void 0&&clearTimeout(i),await n.cancel(r).catch(()=>{}),n.releaseLock()}}var DurableSessionReadTimeoutError=class extends Error{constructor(e){super(`Timed out reading durable session snapshot from stream "${ASH_SESSION_STREAM_NAMESPACE}" for run ${e.sessionId} after ${DURABLE_SESSION_READ_TIMEOUT_MS}ms.`),this.name=`DurableSessionReadTimeoutError`}};function createDurableSessionState(t){let n={session:projectToDurableSession(t.session),version:1};return{...projectSessionState({session:t.session}),snapshot:n}}export{DURABLE_SESSION_VERSION,createDurableSessionState,projectSessionState,readDurableSession};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HookPayload, SessionCapabilities } from "#channel/types.js";
|
|
2
2
|
import type { RunMode } from "#shared/run-mode.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { DurableSessionState } from "#execution/durable-session-store.js";
|
|
4
4
|
import type { NextDriverAction } from "#execution/next-driver-action.js";
|
|
5
5
|
/**
|
|
6
6
|
* Hook payload the turn child workflow delivers to the parent driver
|
|
@@ -23,14 +23,13 @@ export interface TurnWorkflowInput {
|
|
|
23
23
|
readonly parentWritable: WritableStream<Uint8Array>;
|
|
24
24
|
readonly serializedContext: Record<string, unknown>;
|
|
25
25
|
readonly sessionState: DurableSessionState;
|
|
26
|
-
readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
|
|
27
26
|
}
|
|
28
27
|
/**
|
|
29
28
|
* Short-lived workflow that owns one runtime turn for the driver.
|
|
30
29
|
*
|
|
31
|
-
* `parentWritable`
|
|
32
|
-
*
|
|
33
|
-
*
|
|
30
|
+
* `parentWritable` is threaded in from the driver run so event writes
|
|
31
|
+
* land on the driver's stream. Resolves the turn into a
|
|
32
|
+
* {@link NextDriverAction} and reports it back through
|
|
34
33
|
* {@link notifyDriverStep}.
|
|
35
34
|
*/
|
|
36
35
|
export declare function turnWorkflow(input: TurnWorkflowInput): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeSerializableError}from"#execution/workflow-errors.js";import{turnStep}from"#execution/workflow-steps.js";async function turnWorkflow(n){"use workflow";let r=n.sessionState,i=n.serializedContext,a=n.delivery,o=n.parentWritable
|
|
1
|
+
import{normalizeSerializableError}from"#execution/workflow-errors.js";import{turnStep}from"#execution/workflow-steps.js";async function turnWorkflow(n){"use workflow";let r=n.sessionState,i=n.serializedContext,a=n.delivery,o=n.parentWritable;try{for(;;){let e=await turnStep({input:a,parentWritable:o,serializedContext:i,sessionState:r});if(r=e.sessionState,i=e.serializedContext,e.action===`done`){await notifyDriverStep({completionToken:n.completionToken,payload:{action:{kind:`done`,output:e.output??``,isError:e.isError,serializedContext:i,sessionState:r},kind:`turn-result`}});return}if(e.action===`park`){let t=e.pendingRuntimeActionKeys;if(!(t!==void 0||e.hasPendingAuthorization||e.hasPendingInputBatch&&n.capabilities?.requestInput===!0||n.mode===`conversation`))throw Error("Task mode cannot wait for follow-up input (`next: null`).");let a=t===void 0?{kind:`park`,serializedContext:i,sessionState:r,authorizationNames:e.authorizationNames}:{kind:`dispatch-runtime-actions`,pendingActionKeys:t,serializedContext:i,sessionState:r};await notifyDriverStep({completionToken:n.completionToken,payload:{action:a,kind:`turn-result`}});return}a=void 0}}catch(t){throw await notifyDriverStep({completionToken:n.completionToken,payload:{error:normalizeSerializableError(t),kind:`turn-error`}}),t}}async function notifyDriverStep(e){"use step";let{resumeHook:t}=await import(`#compiled/@workflow/core/runtime.js`);await t(e.completionToken,e.payload)}export{notifyDriverStep,turnWorkflow};
|