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.
Files changed (91) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/docs/internals/context.md +6 -12
  3. package/dist/docs/internals/hooks.md +15 -74
  4. package/dist/docs/internals/mechanical-invariants.md +3 -4
  5. package/dist/docs/internals/message-runtime.md +2 -3
  6. package/dist/docs/public/advanced/hooks.mdx +39 -76
  7. package/dist/docs/public/advanced/project-layout.md +1 -2
  8. package/dist/docs/public/advanced/typescript-api.md +1 -1
  9. package/dist/docs/public/channels/index.md +2 -2
  10. package/dist/docs/public/channels/slack.mdx +12 -17
  11. package/dist/docs/public/frontend/use-ash-agent.md +13 -17
  12. package/dist/src/channel/adapter.js +1 -1
  13. package/dist/src/channel/routes.d.ts +5 -7
  14. package/dist/src/channel/send.js +1 -1
  15. package/dist/src/channel/types.d.ts +3 -3
  16. package/dist/src/compiler/manifest.d.ts +1 -1
  17. package/dist/src/compiler/normalize-hook.d.ts +4 -4
  18. package/dist/src/context/build-dynamic-tools.d.ts +13 -0
  19. package/dist/src/context/build-dynamic-tools.js +1 -0
  20. package/dist/src/context/dynamic-instruction-lifecycle.d.ts +13 -13
  21. package/dist/src/context/dynamic-instruction-lifecycle.js +1 -1
  22. package/dist/src/context/dynamic-resolve-context.d.ts +12 -0
  23. package/dist/src/context/dynamic-resolve-context.js +1 -0
  24. package/dist/src/context/dynamic-skill-lifecycle.js +1 -1
  25. package/dist/src/context/dynamic-tool-lifecycle.d.ts +4 -10
  26. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  27. package/dist/src/context/hook-lifecycle.d.ts +1 -46
  28. package/dist/src/context/hook-lifecycle.js +1 -1
  29. package/dist/src/context/keys.d.ts +30 -32
  30. package/dist/src/context/keys.js +1 -1
  31. package/dist/src/execution/create-session-step.d.ts +3 -4
  32. package/dist/src/execution/create-session-step.js +1 -1
  33. package/dist/src/execution/dispatch-runtime-actions-step.d.ts +2 -3
  34. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  35. package/dist/src/execution/durable-session-store.d.ts +24 -24
  36. package/dist/src/execution/durable-session-store.js +1 -1
  37. package/dist/src/execution/turn-workflow.d.ts +4 -5
  38. package/dist/src/execution/turn-workflow.js +1 -1
  39. package/dist/src/execution/workflow-entry.js +1 -1
  40. package/dist/src/execution/workflow-runtime.d.ts +1 -1
  41. package/dist/src/execution/workflow-steps.d.ts +1 -3
  42. package/dist/src/execution/workflow-steps.js +1 -1
  43. package/dist/src/harness/code-mode.js +1 -1
  44. package/dist/src/harness/compaction.js +1 -1
  45. package/dist/src/harness/messages.js +1 -1
  46. package/dist/src/harness/prompt-cache.d.ts +11 -1
  47. package/dist/src/harness/prompt-cache.js +1 -1
  48. package/dist/src/harness/step-hooks.js +1 -1
  49. package/dist/src/harness/tool-loop.js +1 -1
  50. package/dist/src/harness/types.d.ts +4 -5
  51. package/dist/src/internal/application/package.js +1 -1
  52. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  53. package/dist/src/public/channels/ash.js +2 -2
  54. package/dist/src/public/channels/discord/discordChannel.d.ts +1 -2
  55. package/dist/src/public/channels/discord/discordChannel.js +1 -1
  56. package/dist/src/public/channels/discord/inbound.d.ts +0 -3
  57. package/dist/src/public/channels/discord/inbound.js +1 -1
  58. package/dist/src/public/channels/discord/index.d.ts +1 -1
  59. package/dist/src/public/channels/discord/index.js +1 -1
  60. package/dist/src/public/channels/slack/inbound.d.ts +4 -13
  61. package/dist/src/public/channels/slack/inbound.js +1 -1
  62. package/dist/src/public/channels/slack/slackChannel.d.ts +3 -7
  63. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  64. package/dist/src/public/channels/teams/inbound.d.ts +0 -3
  65. package/dist/src/public/channels/teams/inbound.js +2 -2
  66. package/dist/src/public/channels/teams/index.d.ts +1 -1
  67. package/dist/src/public/channels/teams/index.js +1 -1
  68. package/dist/src/public/channels/teams/teamsChannel.d.ts +1 -2
  69. package/dist/src/public/channels/teams/teamsChannel.js +1 -1
  70. package/dist/src/public/channels/telegram/inbound.d.ts +0 -3
  71. package/dist/src/public/channels/telegram/inbound.js +1 -1
  72. package/dist/src/public/channels/telegram/index.d.ts +1 -1
  73. package/dist/src/public/channels/telegram/index.js +1 -1
  74. package/dist/src/public/channels/telegram/telegramChannel.d.ts +1 -2
  75. package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
  76. package/dist/src/public/channels/twilio/inbound.d.ts +0 -3
  77. package/dist/src/public/channels/twilio/inbound.js +1 -1
  78. package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
  79. package/dist/src/public/definitions/hook.d.ts +6 -22
  80. package/dist/src/public/definitions/instructions.d.ts +7 -12
  81. package/dist/src/public/definitions/skill.js +1 -1
  82. package/dist/src/public/hooks/index.d.ts +4 -5
  83. package/dist/src/runtime/graph.d.ts +2 -3
  84. package/dist/src/runtime/hooks/registry.d.ts +5 -20
  85. package/dist/src/runtime/hooks/registry.js +1 -1
  86. package/dist/src/runtime/resolve-hook.d.ts +2 -2
  87. package/dist/src/runtime/resolve-hook.js +1 -1
  88. package/dist/src/runtime/types.d.ts +3 -9
  89. package/dist/src/shared/dynamic-tool-definition.d.ts +20 -0
  90. package/dist/src/shared/dynamic-tool-definition.js +1 -1
  91. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import type { ModelMessage, UserContent } from "ai";
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 modelContext?: readonly ModelMessage[];
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 modelContext?: readonly ModelMessage[];
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 lifecycle handlers are arbitrary functions — there is no static
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 lifecycle handlers are arbitrary functions and cannot be
8
- * statically validated at compile time — the normalization step only
9
- * derives the path-relative slug used for diagnostics and ordering.
10
- * Per-handler validation lives in the runtime resolver.
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
- * Virtual (non-serialized) pending dynamic instruction messages. Set by
8
- * {@link dispatchDynamicInstructionEvent} when resolvers produce
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 const PendingDynamicInstructionMessagesKey: ContextKey<ModelMessage[]>;
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. On a
17
- * matching event: runs handlers, validates branded returns, lowers
18
- * results to messages, and stores them on a virtual context key for
19
- * the tool-loop to inject.
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{getAdapterKind}from"#channel/adapter.js";import{AuthKey,ContinuationTokenKey,InitiatorAuthKey,SessionIdKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_TOOL_EVENTS,isBrandedInstructionsEntry}from"#shared/dynamic-tool-definition.js";import{ContextKey}from"#context/key.js";import{ChannelKey}from"#runtime/sessions/runtime-context-keys.js";const log=createLogger(`dynamic-instructions`),PendingDynamicInstructionMessagesKey=new ContextKey(`ash.pendingDynamicInstructionMessages`);function buildResolveContext(e,o){let s=e.get(SessionIdKey)??``,c=e.get(AuthKey)??null,l=e.get(InitiatorAuthKey)??null,d=e.get(ChannelKey),f=e.get(ContinuationTokenKey);return{session:{id:s,auth:{current:c,initiator:l}},channel:{kind:d===void 0?void 0:getAdapterKind(d),continuationToken:f},messages:o}}function lowerToMessages(e){let t=[];return e.markdown!==void 0&&e.markdown.trim().length>0&&t.push({role:`system`,content:e.markdown}),e.modelContext!==void 0&&t.push(...e.modelContext),t}async function dispatchDynamicInstructionEvent(e){let{ctx:t,resolvers:n,event:r,messages:i}=e;if(!ALLOWED_DYNAMIC_TOOL_EVENTS.has(r.type))return;let a=n.filter(e=>e.eventNames.includes(r.type));if(a.length===0)return;let l=buildResolveContext(t,i),u=[];for(let e of a){let t=e.events[r.type];if(t!==void 0)try{let n=await t(r,l);if(n==null)continue;if(!isBrandedInstructionsEntry(n)){log.error(`Dynamic instructions resolver "${e.slug}" returned an unbranded value — wrap with defineInstructions().`);continue}u.push(...lowerToMessages(n))}catch(t){log.error(`Dynamic instructions resolver "${e.slug}" (${r.type}) threw — skipping.`,{error:toErrorMessage(t)})}}if(u.length>0){let e=t.get(PendingDynamicInstructionMessagesKey)??[];t.setVirtualContext(PendingDynamicInstructionMessagesKey,[...e,...u])}}export{PendingDynamicInstructionMessagesKey,dispatchDynamicInstructionEvent};
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{getAdapterKind}from"#channel/adapter.js";import{AuthKey,ContinuationTokenKey,DynamicSkillManifestKey,InitiatorAuthKey,SandboxKey,SessionIdKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_TOOL_EVENTS}from"#shared/dynamic-tool-definition.js";import{normalizeSkillPackage,removeSkillPackageFromSandbox,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{ContextKey}from"#context/key.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";const log=createLogger(`dynamic-skills`);function buildResolveContext(e,i){let o=e.get(SessionIdKey)??``,c=e.get(AuthKey)??null,l=e.get(InitiatorAuthKey)??null,u=e.get(ChannelKey),d=e.get(ContinuationTokenKey);return{session:{id:o,auth:{current:c,initiator:l}},channel:{kind:u===void 0?void 0:getAdapterKind(u),continuationToken:d},messages:i}}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())??``}function isSkillEntry(e){return typeof e==`object`&&!!e&&typeof e.markdown==`string`&&typeof e.description==`string`}const PendingSkillAnnouncementKey=new ContextKey(`ash.pendingSkillAnnouncement`);async function dispatchDynamicSkillEvent(e){let{ctx:t,resolvers:n,event:r,messages:a}=e;if(!ALLOWED_DYNAMIC_TOOL_EVENTS.has(r.type))return;let s=n.filter(e=>e.eventNames.includes(r.type));if(s.length===0)return;let f=buildResolveContext(t,a),p=new Set(t.require(BundleKey).resolvedAgent.skills.map(e=>e.name)),m=t.get(DynamicSkillManifestKey)??{},h=[],g=await Promise.allSettled(s.map(async e=>{let t=e.events[r.type];if(t===void 0)return null;let n=await t(r,f);if(n==null)return{resolver:e,named:[]};let i,a;return isSkillEntry(n)?(i={_single:n},a=!0):(i=n,a=!1),{resolver:e,named:qualifyDynamicSkillNames(e.slug,a,i)}}));for(let e of g){if(e.status===`rejected`){log.error(`Dynamic skill resolver (${r.type}) threw — skipping.`,{error:toErrorMessage(e.reason)});continue}e.value!==null&&h.push({resolver:e.value.resolver,skills:e.value.named.map(({name:e,entry:t})=>normalizeSkillPackage({...t,name:e}))})}if(h.length===0)return;let _={...m};for(let{resolver:e,skills:t}of h)t.length===0?delete _[e.slug]:_[e.slug]=t.map(e=>({description:e.description,name:e.name}));let v=new Map;for(let[e,t]of Object.entries(_))for(let{name:n}of t){if(p.has(n))throw Error(`Dynamic skill "${n}" from resolver "${e}" conflicts with an authored skill.`);let t=v.get(n);if(t!==void 0)throw Error(`Dynamic skill "${n}" from resolver "${e}" conflicts with dynamic resolver "${t}".`);v.set(n,e)}let y=await t.require(SandboxKey).get();if(y!==null)for(let{resolver:e,skills:t}of h){let n=new Set((m[e.slug]??[]).map(e=>e.name)),r=new Set(t.map(e=>e.name));for(let e of n)r.has(e)||await removeSkillPackageFromSandbox({sandbox:y,name:e});for(let e of t)await writeSkillPackageToSandbox({sandbox:y,skill:e})}t.set(DynamicSkillManifestKey,_),t.set(PendingSkillAnnouncementKey,formatDynamicSkillAnnouncement(_))}export{PendingSkillAnnouncementKey,dispatchDynamicSkillEvent};
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
- * Unified dynamic tool event dispatch. Called by `handleEvent` for
16
- * every stream event. Two phases:
17
- *
18
- * 1. **Build phase**: reconstruct tools from durable metadata when
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{getAdapterKind}from"#channel/adapter.js";import{AuthKey,ContinuationTokenKey,DynamicSessionToolMetadataKey,DynamicToolsKey,InitiatorAuthKey,SessionIdKey}from"#context/keys.js";import{toErrorMessage}from"#shared/errors.js";import{ALLOWED_DYNAMIC_TOOL_EVENTS,isBrandedToolEntry}from"#shared/dynamic-tool-definition.js";import{ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{jsonSchema,zodSchema}from"ai";import{normalizeJsonSchemaDefinition}from"#internal/json-schema.js";import{buildCallbackContext}from"#context/build-callback-context.js";const log=createLogger(`dynamic-tools`);function buildResolveContext(e,i){let a=e.get(SessionIdKey)??``,c=e.get(AuthKey)??null,l=e.get(InitiatorAuthKey)??null,u=e.get(ChannelKey),f=e.get(ContinuationTokenKey);return{session:{id:a,auth:{current:c,initiator:l}},channel:{kind:u===void 0?void 0:getAdapterKind(u),continuationToken:f},messages:i}}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 matchesAnySlug(e,t){for(let n of t)if(e===n||e.startsWith(`${n}__`))return!0;return!1}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 o,s;return isBrandedToolEntry(a)?(o={_single:a},s=!0):(o=a,s=!1),{resolver:t,entries:o,isSingle:s}})),o=[],s=[];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:i}=e.value,a=qualifyDynamicToolNames(t.slug,i,r);for(let{name:e,entryKey:n,entry:r}of a){o.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)})}}if(s.length>0){let t=e.get(DynamicSessionToolMetadataKey)??[],n=new Set(s.map(e=>e.resolverSlug)),r=t.filter(e=>!n.has(e.resolverSlug));e.set(DynamicSessionToolMetadataKey,[...r,...s])}return o}async function dispatchDynamicToolEvent(e){let{ctx:t,resolvers:n,event:r,messages:o}=e;if(t.get(DynamicToolsKey)===void 0){let e=t.get(DynamicSessionToolMetadataKey);if(e!==void 0&&e.length>0){let r=replayDynamicSessionTools(e,n);r.length>0&&t.setVirtualContext(DynamicToolsKey,[...r])}}if(!ALLOWED_DYNAMIC_TOOL_EVENTS.has(r.type))return;let s=n.filter(e=>e.eventNames.includes(r.type));if(s.length===0)return;let c=await resolveToolsFromEvent(t,s,r,o);if(c.length===0)return;let u=new Set(s.map(e=>e.slug)),d=(t.get(DynamicToolsKey)??[]).filter(e=>!matchesAnySlug(e.name,u));t.setVirtualContext(DynamicToolsKey,[...d,...c])}export{dispatchDynamicToolEvent,replayDynamicSessionTools};
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 { HarnessEmitFn, HarnessSession, StepInput, StepResult } from "#harness/types.js";
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,SessionPreparedKey}from"./keys.js";import{createLogger}from"#internal/logging.js";import{getAdapterKind}from"#channel/adapter.js";import{toErrorMessage}from"#shared/errors.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{buildCallbackContext}from"#context/build-callback-context.js";import{emitRecoverableFailedTurn,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";const log=createLogger(`hooks.lifecycle`);async function dispatchHookLifecycle(e){let{ctx:n,registry:r,emit:a}=e,o=getHarnessEmissionState(e.session.state),s=buildHookContext(n),u=e.session;if(r.session.length>0&&n.get(SessionPreparedKey)!==!0){n.set(SessionPreparedKey,!0);for(let e of r.session)await e.handler(s)}let d=!1,f=o;try{for(let e of r.turn)await e.handler(s)}catch(t){let n=toErrorMessage(t);try{return d||=(f=await emitTurnPreamble(a,{message:e.input.message},o,e.runtimeIdentity),!0),{kind:`turn-failed`,message:n,nextSession:setHarnessEmissionState(u,await emitRecoverableFailedTurn(a,f,{code:`HOOK_TURN_FAILED`,message:n}))}}catch(e){throw log.error(`Event hook threw while emitting the turn.failed cascade for a lifecycle.turn failure — escalating to session.failed.`,{error:toErrorMessage(e)}),e}}return{kind:`proceed`,input:e.input,nextSession:u}}async function dispatchStreamEventHooks(e){let t=e.registry.streamEventsByType.get(e.event.type)??[],n=e.registry.streamEventsWildcard;if(t.length===0&&n.length===0)return;let r=buildHookContext(e.ctx);for(let n of t)await n.handler(e.event,r);for(let t of n)await t.handler(e.event,r)}function buildHookContext(t){let n=t.require(BundleKey),i=t.get(ChannelKey),c=t.get(ContinuationTokenKey),l=i===void 0?void 0:getAdapterKind(i);return{...buildCallbackContext(),agent:{name:n.resolvedAgent.config.name??`agent`,nodeId:n.nodeId},channel:{kind:l,continuationToken:c}}}async function runHookLifecycleStep(e,t){let n=await dispatchHookLifecycle(e);return n.kind===`turn-failed`?{next:e.mode===`conversation`?null:{done:!0,output:n.message},session:n.nextSession}:t(n.nextSession,n.input)}export{dispatchHookLifecycle,dispatchStreamEventHooks,runHookLifecycleStep};
1
+ import{ContinuationTokenKey}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: import("#shared/json.js").JsonObject;
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
- export declare const DynamicSessionToolMetadataKey: ContextKey<readonly DurableDynamicToolMetadata[]>;
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[]>>;
@@ -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`),SessionPreparedKey=new ContextKey(`ash.sessionPrepared`),SessionKey=new ContextKey(`ash.session`),SandboxKey=new ContextKey(`ash.sandbox`),DynamicToolsKey=new ContextKey(`ash.dynamicTools`),DynamicSessionToolMetadataKey=new ContextKey(`ash.dynamicSessionToolMetadata`),DynamicSkillManifestKey=new ContextKey(`ash.dynamicSkillManifest`);export{AuthKey,CapabilitiesKey,ChannelInstrumentationKey,ContinuationTokenKey,DynamicSessionToolMetadataKey,DynamicSkillManifestKey,DynamicToolsKey,InitiatorAuthKey,ModeKey,ParentSessionKey,SandboxKey,SessionCallbackKey,SessionIdKey,SessionKey,SessionPreparedKey};
1
+ import{ContextKey}from"#context/key.js";const AuthKey=new ContextKey(`ash.auth`),InitiatorAuthKey=new ContextKey(`ash.initiatorAuth`),SessionIdKey=new ContextKey(`ash.sessionId`),ContinuationTokenKey=new ContextKey(`ash.continuationToken`),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 DurableSessionSnapshot, type DurableSessionState } from "#execution/durable-session-store.js";
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 writes the initial snapshot to the
15
- * `ash.session` stream before the workflow enters its turn loop.
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{writeDurableSession}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=await writeDurableSession({session:createSession({compactionOverrides:{thresholdPercent:t.resolvedAgent.config.compaction?.thresholdPercent},continuationToken:e.continuationToken,outputSchema:e.outputSchema,rootSessionId:e.rootSessionId,sessionId:e.sessionId,turnAgent:t.turnAgent}),writable:e.sessionWritable}),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};
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 appends the updated snapshot to `ash.session`.
7
+ * session and returns the updated snapshot-bearing state.
8
8
  */
9
9
  import type { RuntimeSubagentResultActionResult } from "#runtime/actions/types.js";
10
- import { type DurableSessionSnapshot, type DurableSessionState } from "#execution/durable-session-store.js";
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,writeDurableSession}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:await writeDurableSession({session:b,writable:e.sessionWritable})}}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
+ 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
- * Each driver workflow run owns one stream at namespace `"ash.session"`.
5
- * Session-mutating steps append one snapshot per mutation; reads pull
6
- * the tail (`startIndex: -1`). Step inputs, step results, and hook
7
- * payloads carry a small {@link DurableSessionState} value never the
8
- * full {@link HarnessSession}.
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 only the small projections the workflow body needs without
31
- * taking a step boundary: identity, the hook continuation token,
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} written to the
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 snapshot from the `ash.session` stream and returns
91
- * the {@link DurableSession} inside.
92
+ * Reads the latest durable session snapshot and returns the
93
+ * {@link DurableSession} inside.
92
94
  *
93
- * Always pulls the tail chunk (`startIndex: -1`); the
94
- * single-writer-per-session-stream invariant makes that safe. The
95
- * snapshot is migrated to {@link DURABLE_SESSION_VERSION} before
96
- * return; unknown versions throw.
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
- * Appends one snapshot to the `ash.session` stream and returns the
106
- * projected {@link DurableSessionState}.
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 writeDurableSession(input: {
111
+ export declare function createDurableSessionState(input: {
111
112
  readonly session: HarnessSession;
112
- readonly writable: WritableStream<DurableSessionSnapshot>;
113
- }): Promise<DurableSessionState>;
113
+ }): DurableSessionState;
@@ -1 +1 @@
1
- import{getHarnessEmissionState}from"#harness/emission.js";import{projectToDurableSession}from"#execution/session.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(t){return{continuationToken:t.session.continuationToken,emissionState:getHarnessEmissionState(t.session.state),hasProxyInputRequests:hasProxyInputRequests(t.session.state),sessionId:t.session.sessionId,version:1}}async function readDurableSession(e){let{getRun:t}=await import(`#compiled/@workflow/core/runtime.js`),n=t(e.sessionId).getReadable({namespace:ASH_SESSION_STREAM_NAMESPACE,startIndex:-1}).getReader(),i=`ash durable session tail read failed`,a;try{let t=await Promise.race([n.read().then(e=>({kind:`read`,read:e})),new Promise(e=>{a=setTimeout(()=>e({kind:`timeout`}),DURABLE_SESSION_READ_TIMEOUT_MS)})]);if(t.kind===`timeout`)throw i=`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 i=`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 i=`ash durable session tail read complete`,migrateDurableSessionSnapshot(t.read.value).session}finally{a!==void 0&&clearTimeout(a),await n.cancel(i).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`}};async function writeDurableSession(e){let n={session:projectToDurableSession(e.session),version:1},r=e.writable.getWriter();try{await r.write(n)}finally{r.releaseLock()}return projectSessionState({session:e.session})}export{ASH_SESSION_STREAM_NAMESPACE,DURABLE_SESSION_VERSION,projectSessionState,readDurableSession,writeDurableSession};
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 { DurableSessionSnapshot, DurableSessionState } from "#execution/durable-session-store.js";
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` and `sessionWritable` are threaded in from the
32
- * driver run so step writes land on the driver's streams. Resolves the
33
- * turn into a {@link NextDriverAction} and reports it back through
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,s=n.sessionWritable;try{for(;;){let e=await turnStep({input:a,parentWritable:o,serializedContext:i,sessionState:r,sessionWritable:s});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};
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};