experimental-ash 0.28.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +1 -0
- package/dist/docs/public/cli-build-and-debugging.md +2 -0
- package/dist/docs/public/getting-started.md +1 -0
- package/dist/src/cli/run.d.ts +9 -0
- package/dist/src/cli/run.js +1 -1
- package/dist/src/context/hook-lifecycle.d.ts +23 -67
- package/dist/src/context/hook-lifecycle.js +1 -1
- package/dist/src/execution/await-authorization-orchestrator.d.ts +18 -29
- package/dist/src/execution/await-authorization-orchestrator.js +1 -1
- package/dist/src/execution/connection-auth-steps.d.ts +28 -65
- package/dist/src/execution/connection-auth-steps.js +1 -1
- package/dist/src/execution/create-session-step.d.ts +15 -0
- package/dist/src/execution/create-session-step.js +1 -0
- package/dist/src/execution/delegated-parent-notification.d.ts +15 -0
- package/dist/src/execution/delegated-parent-notification.js +1 -0
- package/dist/src/execution/delegated-parent-result.d.ts +14 -0
- package/dist/src/execution/delegated-parent-result.js +1 -0
- package/dist/src/execution/dispatch-runtime-actions-step.d.ts +20 -0
- package/dist/src/execution/dispatch-runtime-actions-step.js +1 -0
- package/dist/src/execution/durable-session-migrations/chain.d.ts +31 -0
- package/dist/src/execution/durable-session-migrations/chain.js +1 -0
- package/dist/src/execution/durable-session-migrations/snapshot.d.ts +22 -0
- package/dist/src/execution/durable-session-migrations/snapshot.js +1 -0
- package/dist/src/execution/durable-session-store.d.ts +104 -0
- package/dist/src/execution/durable-session-store.js +1 -0
- package/dist/src/execution/next-driver-action.d.ts +30 -0
- package/dist/src/execution/next-driver-action.js +1 -0
- package/dist/src/execution/sandbox/prewarm.d.ts +12 -2
- package/dist/src/execution/sandbox/prewarm.js +1 -1
- package/dist/src/execution/session.d.ts +29 -28
- package/dist/src/execution/session.js +1 -1
- package/dist/src/execution/subagent-hitl-proxy.d.ts +8 -25
- package/dist/src/execution/subagent-hitl-proxy.js +1 -1
- package/dist/src/execution/subagent-tool.js +1 -1
- package/dist/src/execution/turn-workflow.d.ts +21 -21
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-entry.d.ts +12 -16
- package/dist/src/execution/workflow-entry.js +1 -1
- package/dist/src/execution/workflow-runtime.js +1 -1
- package/dist/src/execution/workflow-steps.d.ts +36 -91
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/harness/emission.d.ts +3 -5
- package/dist/src/harness/emission.js +1 -1
- package/dist/src/harness/input-requests.d.ts +3 -3
- package/dist/src/harness/input-requests.js +1 -1
- package/dist/src/harness/proxy-input-requests.d.ts +12 -16
- package/dist/src/harness/proxy-input-requests.js +1 -1
- package/dist/src/harness/runtime-actions.d.ts +17 -24
- package/dist/src/harness/runtime-actions.js +1 -1
- package/dist/src/harness/tool-loop.js +1 -1
- package/dist/src/harness/types.d.ts +3 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/nitro/host/start-production-server.d.ts +8 -0
- package/dist/src/internal/nitro/host/start-production-server.js +3 -0
- package/dist/src/internal/nitro/host/types.d.ts +8 -0
- package/dist/src/internal/nitro/host.d.ts +2 -1
- package/dist/src/internal/nitro/host.js +1 -1
- package/dist/src/internal/nitro/routes/agent-info/load-agent-info-data.js +1 -1
- package/dist/src/internal/nitro/routes/info.js +1 -1
- package/dist/src/internal/workflow-bundle/builder.d.ts +2 -0
- package/dist/src/internal/workflow-bundle/builder.js +1 -1
- package/dist/src/runtime/cache-key.js +1 -1
- package/dist/src/runtime/framework-channels/index.js +1 -1
- package/dist/src/runtime/loaders/bundled-artifacts.d.ts +12 -0
- package/dist/src/runtime/loaders/bundled-artifacts.js +1 -1
- package/dist/src/runtime/loaders/compile-metadata.js +1 -1
- package/dist/src/runtime/loaders/manifest.js +1 -1
- package/dist/src/runtime/loaders/module-map.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# experimental-ash
|
|
2
2
|
|
|
3
|
+
## 0.30.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e2bb3b9: Move durable `HarnessSession` snapshots out of every Workflow step
|
|
8
|
+
boundary into a per-driver-run `ash.session` stream wrapped in a
|
|
9
|
+
versioned `DurableSessionSnapshot`. Step inputs, step results, workflow
|
|
10
|
+
inputs, and hook payloads carry a small `DurableSessionState` — identity,
|
|
11
|
+
the closed-contract `hasProxyInputRequests` short-circuit, and the
|
|
12
|
+
`emissionState` projection workflow-body orchestrators (eg.
|
|
13
|
+
await-authorization) use to stamp protocol events without taking an
|
|
14
|
+
extra step boundary — and each session-mutating step returns a closed
|
|
15
|
+
`NextDriverAction` union (`done` | `park` | `dispatch-runtime-actions`)
|
|
16
|
+
the driver dispatches on without inspecting session shape. The persisted
|
|
17
|
+
snapshot is narrowed to the durable subset — `agent.modelReference`,
|
|
18
|
+
`agent.tools`, and compaction thresholds are rebuilt every turn from the
|
|
19
|
+
latest bundle rather than baked into every chunk. Together these changes
|
|
20
|
+
eliminate quadratic event-log growth, set up a stable cross-version
|
|
21
|
+
contract so a pinned older driver can dispatch sessions whose latest
|
|
22
|
+
code has added new optional fields, and reserve a `version` discriminator
|
|
23
|
+
(plus a version-walking migration framework) for future shape changes.
|
|
24
|
+
The harness `StepFn` contract is unchanged.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- e9052aa: Fix `ash dev` returning 401 when run inside `vercel dev`. The framework's
|
|
29
|
+
default auth resolvers for the ash channel and the `/__ash/info`
|
|
30
|
+
endpoint previously gated on `process.env.VERCEL` alone, but `vercel
|
|
31
|
+
dev` inherits `VERCEL=1` into child processes while the REPL strips
|
|
32
|
+
auth on loopback URLs, producing a 401. Both resolvers now also require
|
|
33
|
+
`VERCEL_ENV !== "development"` before defaulting to `vercelOidc()`, so
|
|
34
|
+
local dev under `vercel dev` falls through to `none()` and the REPL
|
|
35
|
+
talks to the dev server.
|
|
36
|
+
|
|
37
|
+
## 0.29.0
|
|
38
|
+
|
|
39
|
+
### Minor Changes
|
|
40
|
+
|
|
41
|
+
- 514f981: Add `ash start` for serving built Nitro output and switch smoke test helpers to run against `ash build` plus `ash start` by default.
|
|
42
|
+
|
|
3
43
|
## 0.28.1
|
|
4
44
|
|
|
5
45
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -116,6 +116,7 @@ CLI commands:
|
|
|
116
116
|
|
|
117
117
|
- `ash info` — discovery results and compiled artifacts
|
|
118
118
|
- `ash build` — compile `.ash/` and build the host output
|
|
119
|
+
- `ash start` — serve the built `.output/` app
|
|
119
120
|
- `ash dev` — start the local runtime and REPL
|
|
120
121
|
|
|
121
122
|
## Deploying
|
|
@@ -11,6 +11,7 @@ From your app root:
|
|
|
11
11
|
|
|
12
12
|
- `ash info` - inspect the discovered authored surface and runtime details
|
|
13
13
|
- `ash build` - compile `.ash/` artifacts and build the host output
|
|
14
|
+
- `ash start` - serve the built `.output/` app
|
|
14
15
|
- `ash dev` - start the local runtime and open the REPL
|
|
15
16
|
- `ash eval` - run eval suites against the current app or a remote target
|
|
16
17
|
|
|
@@ -22,6 +23,7 @@ For most work:
|
|
|
22
23
|
2. run `ash info` when you want to confirm discovery or diagnostics
|
|
23
24
|
3. run `ash dev` while iterating locally
|
|
24
25
|
4. run `ash build` before shipping
|
|
26
|
+
5. run `ash start` when you want to smoke-test the built output locally
|
|
25
27
|
|
|
26
28
|
When `ash build` fails due to discovery errors, the CLI now prints the full discovery diagnostics
|
|
27
29
|
report (severity, message, source path) and the diagnostics artifact path.
|
|
@@ -88,6 +88,7 @@ Useful commands:
|
|
|
88
88
|
|
|
89
89
|
- `ash info`: show the active routes and compiled artifacts
|
|
90
90
|
- `ash build`: compile the agent into `.ash/` and build the host output
|
|
91
|
+
- `ash start`: serve the built `.output/` app
|
|
91
92
|
- `ash dev`: start the local runtime and open the REPL
|
|
92
93
|
|
|
93
94
|
## Send A Message
|
package/dist/src/cli/run.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ interface DevelopmentServerHandle {
|
|
|
6
6
|
readonly url: string;
|
|
7
7
|
close(): Promise<void>;
|
|
8
8
|
}
|
|
9
|
+
interface ProductionServerHandle {
|
|
10
|
+
readonly url: string;
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
wait(): Promise<void>;
|
|
13
|
+
}
|
|
9
14
|
interface CliRuntimeDependencies {
|
|
10
15
|
buildHost(appRoot: string): Promise<string>;
|
|
11
16
|
printApplicationInfo(logger: CliLogger, appRoot: string): Promise<void>;
|
|
@@ -18,6 +23,10 @@ interface CliRuntimeDependencies {
|
|
|
18
23
|
port?: number;
|
|
19
24
|
schedules?: boolean;
|
|
20
25
|
}): Promise<DevelopmentServerHandle>;
|
|
26
|
+
startProductionHost(appRoot: string, options?: {
|
|
27
|
+
host?: string;
|
|
28
|
+
port?: number;
|
|
29
|
+
}): Promise<ProductionServerHandle>;
|
|
21
30
|
}
|
|
22
31
|
type CliRuntimeOverrides = Partial<CliRuntimeDependencies>;
|
|
23
32
|
interface EvalCliOptions {
|
package/dist/src/cli/run.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{Command,CommanderError,InvalidArgumentError}from"#compiled/commander/index.js";import{resolveApplicationRoot}from"#internal/application/paths.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{parseDevelopmentServerUrl}from"#cli/dev/url.js";async function loadBuildHost(){return(await import(`#internal/nitro/host.js`)).buildApplication}async function loadPrintApplicationInfo(){return(await import(`#cli/commands/info.js`)).printApplicationInfo}async function loadRunDevelopmentRepl(){return(await import(`#cli/dev/repl.js`)).runDevelopmentRepl}async function loadRunEvalCommand(){return(await import(`#evals/cli/eval.js`)).runEvalCommand}async function loadStartHost(){return(await import(`#internal/nitro/host.js`)).startDevelopmentServer}function shouldPrintCliBootBanner(e){return e.name()===`info`||e.name()===`dev`}async function waitForShutdownSignal(e){await new Promise((t,n)=>{let r=!1,cleanup=()=>{process.off(`SIGINT`,handleSignal),process.off(`SIGTERM`,handleSignal)},handleSignal=()=>{r||(r=!0,cleanup(),e.close().then(t,n))};process.once(`SIGINT`,handleSignal),process.once(`SIGTERM`,handleSignal)})}function parsePortOption(e){if(!/^-?\d+$/.test(e))throw new InvalidArgumentError(`Expected a numeric port, received "${e}".`);let t=Number(e);if(t<0||t>65535)throw new InvalidArgumentError(`Expected a port between 0 and 65535, received "${e}".`);return t}function hasInteractiveTerminal(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function rewriteDevelopmentUrlShorthand(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function resolveRemoteDevelopmentServerUrl(e){if(e.url){if(e.host!==void 0)throw new InvalidArgumentError(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new InvalidArgumentError(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new InvalidArgumentError(`The --no-repl option cannot be used with --url.`);return e.url}}function createCliProgram(r,i){let c=resolveApplicationRoot(),l=resolveInstalledPackageInfo().version,u=new Command,d=createCliTheme();u.name(`ash`).description(`Build and run an Ash application.`).version(l).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{shouldPrintCliBootBanner(t)&&r.log(`Ash (v${l})`)}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}});let f=u.command(`channels`).description(`Manage user-authored channels in the current project.`);return f.command(`add [kind]`).description(`Scaffold a channel (slack) into the current project.`).option(`-f, --force`,`Overwrite existing channel files`).action(async(e,t)=>{let{runChannelsAddCommand:n}=await import(`#cli/commands/channels.js`);await n(r,c,{kind:e,options:t})}),f.command(`list`).description(`List user-authored channels in the current project.`).option(`--json`,`Output as JSON`).action(async e=>{let{runChannelsListCommand:t}=await import(`#cli/commands/channels.js`);await t(r,c,e)}),u.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:e}=await import(`#cli/dev/environment.js`);e(c);let n=await(i.buildHost??await loadBuildHost())(c);r.log(renderCliTaggedLine(d,{message:`built output at ${n}`,tag:`build`,tone:`success`}))}),u.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).option(`--schedules`,`Run scheduled tasks during development (off by default)`).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,parseDevelopmentServerUrl).addHelpText(`after`,`
|
|
1
|
+
import{createCliTheme,renderCliTaggedLine}from"#cli/ui/output.js";import{Command,CommanderError,InvalidArgumentError}from"#compiled/commander/index.js";import{resolveApplicationRoot}from"#internal/application/paths.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";import{parseDevelopmentServerUrl}from"#cli/dev/url.js";async function loadBuildHost(){return(await import(`#internal/nitro/host.js`)).buildApplication}async function loadPrintApplicationInfo(){return(await import(`#cli/commands/info.js`)).printApplicationInfo}async function loadRunDevelopmentRepl(){return(await import(`#cli/dev/repl.js`)).runDevelopmentRepl}async function loadRunEvalCommand(){return(await import(`#evals/cli/eval.js`)).runEvalCommand}async function loadStartHost(){return(await import(`#internal/nitro/host.js`)).startDevelopmentServer}async function loadStartProductionHost(){return(await import(`#internal/nitro/host.js`)).startProductionServer}function shouldPrintCliBootBanner(e){return e.name()===`info`||e.name()===`dev`}async function waitForShutdownSignal(e){await new Promise((t,n)=>{let r=!1,cleanup=()=>{process.off(`SIGINT`,handleSignal),process.off(`SIGTERM`,handleSignal)},handleSignal=()=>{r||(r=!0,cleanup(),e.close().then(t,n))};process.once(`SIGINT`,handleSignal),process.once(`SIGTERM`,handleSignal)})}async function waitForProductionServer(e){await Promise.race([e.wait(),waitForShutdownSignal({close:()=>e.close()})])}function parsePortOption(e){if(!/^-?\d+$/.test(e))throw new InvalidArgumentError(`Expected a numeric port, received "${e}".`);let t=Number(e);if(t<0||t>65535)throw new InvalidArgumentError(`Expected a port between 0 and 65535, received "${e}".`);return t}function hasInteractiveTerminal(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function rewriteDevelopmentUrlShorthand(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function resolveRemoteDevelopmentServerUrl(e){if(e.url){if(e.host!==void 0)throw new InvalidArgumentError(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new InvalidArgumentError(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new InvalidArgumentError(`The --no-repl option cannot be used with --url.`);return e.url}}function createCliProgram(r,i){let c=resolveApplicationRoot(),l=resolveInstalledPackageInfo().version,u=new Command,d=createCliTheme();u.name(`ash`).description(`Build and run an Ash application.`).version(l).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{shouldPrintCliBootBanner(t)&&r.log(`Ash (v${l})`)}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}});let f=u.command(`channels`).description(`Manage user-authored channels in the current project.`);return f.command(`add [kind]`).description(`Scaffold a channel (slack) into the current project.`).option(`-f, --force`,`Overwrite existing channel files`).action(async(e,t)=>{let{runChannelsAddCommand:n}=await import(`#cli/commands/channels.js`);await n(r,c,{kind:e,options:t})}),f.command(`list`).description(`List user-authored channels in the current project.`).option(`--json`,`Output as JSON`).action(async e=>{let{runChannelsListCommand:t}=await import(`#cli/commands/channels.js`);await t(r,c,e)}),u.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:e}=await import(`#cli/dev/environment.js`);e(c);let n=await(i.buildHost??await loadBuildHost())(c);r.log(renderCliTaggedLine(d,{message:`built output at ${n}`,tag:`build`,tone:`success`}))}),u.command(`start`).description(`Start a built Ash application.`).option(`--host <host>`,`Host interface to bind`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).action(async e=>{let{loadDevelopmentEnvironmentFiles:n}=await import(`#cli/dev/environment.js`);n(c);let a=await(i.startProductionHost??await loadStartProductionHost())(c,{host:e.host,port:e.port});r.log(renderCliTaggedLine(d,{message:`server listening at ${a.url}`,tag:`start`,tone:`success`})),await waitForProductionServer(a)}),u.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,parsePortOption).option(`--schedules`,`Run scheduled tasks during development (off by default)`).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,parseDevelopmentServerUrl).addHelpText(`after`,`
|
|
2
2
|
You can also pass a bare URL as the only argument, for example: ash dev https://example.com
|
|
3
3
|
`).action(async e=>{let n=resolveRemoteDevelopmentServerUrl(e),{loadDevelopmentEnvironmentFiles:a}=await import(`#cli/dev/environment.js`);if(a(c),n){if(r.log(renderCliTaggedLine(d,{message:`REPL connecting to ${n}`,tag:`dev`,tone:`info`})),!hasInteractiveTerminal()){r.log(renderCliTaggedLine(d,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}r.log(``),await(i.runDevelopmentRepl??await loadRunDevelopmentRepl())({serverUrl:n});return}let o=await(i.startHost??await loadStartHost())(c,{host:e.host,port:e.port,schedules:e.schedules===!0}),s=!1,closeServer=async()=>{s||(s=!0,await o.close())};try{if(r.log(renderCliTaggedLine(d,{message:`server listening at ${o.url}`,tag:`dev`,tone:`success`})),e.repl===!1)return await waitForShutdownSignal({close:closeServer});if(!hasInteractiveTerminal())return r.log(renderCliTaggedLine(d,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await waitForShutdownSignal({close:closeServer});r.log(``),await(i.runDevelopmentRepl??await loadRunDevelopmentRepl())({serverUrl:o.url})}finally{await closeServer()}}),u.command(`info`).description(`Print resolved application information.`).action(async()=>{await(i.printApplicationInfo??await loadPrintApplicationInfo())(r,c)}),u.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await(i.runEvalCommand??await loadRunEvalCommand())(e,r)}),u}async function runCli(e=process.argv.slice(2),t=console,n={}){let i=createCliProgram(t,n),a=e.length===0?[`info`]:rewriteDevelopmentUrlShorthand(e);try{await i.parseAsync(a,{from:`user`})}catch(e){if(e instanceof CommanderError){if(e.exitCode===0)return;throw Error(e.message)}throw e}}export{runCli};
|
|
@@ -4,18 +4,11 @@ import type { RunMode } from "#shared/run-mode.js";
|
|
|
4
4
|
import type { RuntimeHookRegistry } from "#runtime/hooks/registry.js";
|
|
5
5
|
import type { ContextContainer } from "./container.js";
|
|
6
6
|
/**
|
|
7
|
-
* Outcome of
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* system-history announcements for hook-contributed skills.
|
|
13
|
-
*
|
|
14
|
-
* `turn-failed` indicates a `lifecycle.turn` hook threw. The dispatcher
|
|
15
|
-
* has already emitted the recoverable `turn.failed` cascade
|
|
16
|
-
* (`session.started` once → `turn.started` → `message.received` →
|
|
17
|
-
* `step.failed` → `turn.failed` → `session.waiting`); the caller
|
|
18
|
-
* lowers this outcome to a parking {@link StepResult}.
|
|
7
|
+
* Outcome of {@link dispatchHookLifecycle}. `proceed` carries the
|
|
8
|
+
* input augmented with merged `modelContext` and any hook-contributed
|
|
9
|
+
* skill announcements on the session. `turn-failed` means a
|
|
10
|
+
* `lifecycle.turn` hook threw; the recoverable `turn.failed` cascade
|
|
11
|
+
* has already been emitted.
|
|
19
12
|
*/
|
|
20
13
|
export type HookLifecycleOutcome = {
|
|
21
14
|
readonly kind: "proceed";
|
|
@@ -26,9 +19,6 @@ export type HookLifecycleOutcome = {
|
|
|
26
19
|
readonly message: string;
|
|
27
20
|
readonly nextSession: HarnessSession;
|
|
28
21
|
};
|
|
29
|
-
/**
|
|
30
|
-
* Input for {@link dispatchHookLifecycle}.
|
|
31
|
-
*/
|
|
32
22
|
export interface DispatchHookLifecycleInput {
|
|
33
23
|
readonly ctx: ContextContainer;
|
|
34
24
|
readonly registry: RuntimeHookRegistry;
|
|
@@ -39,51 +29,24 @@ export interface DispatchHookLifecycleInput {
|
|
|
39
29
|
readonly runtimeIdentity?: RuntimeIdentity;
|
|
40
30
|
}
|
|
41
31
|
/**
|
|
42
|
-
* Runs the per-turn hook lifecycle inside the active ALS scope
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* 2. `lifecycle.turn` — runs once per fresh delivery (the caller
|
|
55
|
-
* gates dispatch with `isHarnessBetweenTurns(session)`). Each
|
|
56
|
-
* hook may return `{ modelContext, skills }`; model-context
|
|
57
|
-
* contributions are concatenated in registry order and skills are
|
|
58
|
-
* materialized into the live sandbox before the model call. A
|
|
59
|
-
* thrown hook is caught here, the recoverable `turn.failed`
|
|
60
|
-
* cascade is emitted, and the dispatcher returns
|
|
61
|
-
* `kind: "turn-failed"` so the caller can park the session.
|
|
62
|
-
*
|
|
63
|
-
* Errors:
|
|
64
|
-
*
|
|
65
|
-
* - `lifecycle.session` throws → re-thrown. Runtime escalates to
|
|
66
|
-
* `session.failed`.
|
|
67
|
-
* - `lifecycle.turn` throws → caught, recoverable `turn.failed`
|
|
68
|
-
* emitted, outcome `kind: "turn-failed"`. Session keeps living.
|
|
69
|
-
*
|
|
70
|
-
* Stream-event hook errors are not handled here — they propagate
|
|
71
|
-
* through {@link dispatchStreamEventHooks} and are caught wherever the
|
|
72
|
-
* emit composer sits inside the harness.
|
|
32
|
+
* Runs the per-turn hook lifecycle inside the active ALS scope.
|
|
33
|
+
*
|
|
34
|
+
* - `lifecycle.session` runs once per session, gated by
|
|
35
|
+
* {@link SessionPreparedKey}. The flag is set **before** the chain
|
|
36
|
+
* runs so a thrown hook does not retry. A throw re-propagates and
|
|
37
|
+
* the runtime escalates to terminal `session.failed`.
|
|
38
|
+
* - `lifecycle.turn` runs once per fresh delivery. Each hook may
|
|
39
|
+
* return `{ modelContext, skills }`; model-context contributions
|
|
40
|
+
* are concatenated in registry order, skills are materialized into
|
|
41
|
+
* the sandbox. A thrown hook emits the recoverable `turn.failed`
|
|
42
|
+
* cascade and returns `kind: "turn-failed"`.
|
|
73
43
|
*/
|
|
74
44
|
export declare function dispatchHookLifecycle(input: DispatchHookLifecycleInput): Promise<HookLifecycleOutcome>;
|
|
75
45
|
/**
|
|
76
|
-
* Fans one runtime stream event out to every matching subscriber
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* is the natural caller; existing harness error paths catch propagated
|
|
81
|
-
* exceptions and emit the recoverable `turn.failed` cascade. If a
|
|
82
|
-
* hook subscribed to a failure-cascade event itself throws, the
|
|
83
|
-
* outer runtime escalates to `session.failed`.
|
|
84
|
-
*
|
|
85
|
-
* Caller is responsible for running this inside an active ALS scope so
|
|
86
|
-
* hook code can read the same context the rest of the step sees.
|
|
46
|
+
* Fans one runtime stream event out to every matching subscriber.
|
|
47
|
+
* Errors propagate — harness error paths convert them into the
|
|
48
|
+
* recoverable `turn.failed` cascade. Caller must hold an active ALS
|
|
49
|
+
* scope so hooks see the same context as the rest of the step.
|
|
87
50
|
*/
|
|
88
51
|
export declare function dispatchStreamEventHooks(input: {
|
|
89
52
|
readonly ctx: ContextContainer;
|
|
@@ -91,15 +54,8 @@ export declare function dispatchStreamEventHooks(input: {
|
|
|
91
54
|
readonly event: HandleMessageStreamEvent;
|
|
92
55
|
}): Promise<void>;
|
|
93
56
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* the (possibly augmented) input.
|
|
98
|
-
*
|
|
99
|
-
* The `Step` suffix signals the return type — both runtime entry points
|
|
100
|
-
* call this from inside their `runStep` callback so the
|
|
101
|
-
* recoverable-failure-to-`StepResult` mapping lives in one place.
|
|
102
|
-
*
|
|
103
|
-
* Caller must already be inside the active ALS scope.
|
|
57
|
+
* Runs the per-turn hook lifecycle, lowers a `turn-failed` outcome
|
|
58
|
+
* into a parking {@link StepResult}, and otherwise hands off to
|
|
59
|
+
* `body` with the (possibly augmented) input.
|
|
104
60
|
*/
|
|
105
61
|
export declare function runHookLifecycleStep(input: DispatchHookLifecycleInput, body: (session: HarnessSession, input: StepInput) => Promise<StepResult>): Promise<StepResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ContinuationTokenKey,SandboxKey,SessionIdKey,SessionPreparedKey}from"./keys.js";import{createLogger}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeSkillPackage,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{getAdapterKind}from"#channel/adapter.js";import{emitRecoverableFailedTurn,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";const log=createLogger(`hooks.lifecycle`);async function dispatchHookLifecycle(e){let{ctx:t,registry:n,emit:i}=e,o=getHarnessEmissionState(e.session),s=`turn_${o.sequence}`,c=o.sequence,l=buildHookContext(t),u={session:{sessionId:e.session.sessionId},turn:{sequence:c,turnId:s}},d=[],f=e.session;if(n.session.length>0&&t.get(SessionPreparedKey)!==!0){t.set(SessionPreparedKey,!0);let e=[];for(let r of n.session){let n=await r.handler(u,l);n?.modelContext!==void 0&&n.modelContext.length>0&&d.push(...n.modelContext),e.push(...normalizeLifecycleSkillResults(t,n))}f=await materializeLifecycleSkills(t,f,e)}let p=!1,m=o;try{let e=[];for(let r of n.turn){let n=await r.handler(u,l);n?.modelContext!==void 0&&n.modelContext.length>0&&d.push(...n.modelContext),e.push(...normalizeLifecycleSkillResults(t,n))}f=await materializeLifecycleSkills(t,f,e)}catch(t){let n=toErrorMessage(t);try{p||=(m=await emitTurnPreamble(i,{message:e.input.message},o,e.runtimeIdentity),!0);let t=await emitRecoverableFailedTurn(i,m,{code:`HOOK_TURN_FAILED`,message:n});return{kind:`turn-failed`,message:n,nextSession:setHarnessEmissionState(f,t)}}catch(e){throw log.error(`Event hook threw while emitting the turn.failed cascade for a lifecycle.turn failure — escalating to session.failed.`,{error:toErrorMessage(e)}),e}}let h=e.input.modelContext??[],g=h.length===0?d:[...h,...d];return{kind:`proceed`,input:g.length>0?{...e.input,modelContext:g}:e.input,nextSession:f}}function normalizeLifecycleSkillResults(e,t){if(t?.skills===void 0||t.skills.length===0)return[];let n=new Set(e.require(BundleKey).resolvedAgent.skills.map(e=>e.name)),r=new Map;for(let e of t.skills){let t=normalizeSkillPackage(e);if(n.has(t.name))throw Error(`Hook-contributed skill "${t.name}" conflicts with an authored skill.`);r.set(t.name,t)}return[...r.values()]}async function materializeLifecycleSkills(e,n,r){if(r.length===0)return n;let i=new Map(r.map(e=>[e.name,e])),a=await e.require(SandboxKey).get();if(a===null)throw Error(`Dynamic skills require a sandbox for the current agent.`);for(let e of i.values())await writeSkillPackageToSandbox({sandbox:a,skill:e});let o=formatAvailableSkillsSection([...i.values()]);return o===null?n:{...n,history:[...n.history,{role:`system`,content:o}]}}async function dispatchStreamEventHooks(e){let t=e.registry.streamEventsByType.get(e.event.type)??[],n=e.registry.streamEventsWildcard;if(t.length===0&&n.length===0)return;let r=buildHookContext(e.ctx);for(let n of t)await n.handler(e.event,r);for(let t of n)await t.handler(e.event,r)}function buildHookContext(t){let r=t.require(BundleKey),i=t.get(ChannelKey),a=t.get(ContinuationTokenKey),o=i===void 0?void 0:getAdapterKind(i);return{agent:{name:r.resolvedAgent.config.name??`agent`,nodeId:r.nodeId},channel:{kind:o,continuationToken:a},session:{sessionId:t.get(SessionIdKey)??``},ash:t}}async function runHookLifecycleStep(e,t){let n=await dispatchHookLifecycle(e);return n.kind===`turn-failed`?{next:e.mode===`conversation`?null:{done:!0,output:n.message},session:n.nextSession}:t(n.nextSession,n.input)}export{dispatchHookLifecycle,dispatchStreamEventHooks,runHookLifecycleStep};
|
|
1
|
+
import{ContinuationTokenKey,SandboxKey,SessionIdKey,SessionPreparedKey}from"./keys.js";import{createLogger}from"#internal/logging.js";import{toErrorMessage}from"#shared/errors.js";import{normalizeSkillPackage,writeSkillPackageToSandbox}from"#shared/skill-package.js";import{getAdapterKind}from"#channel/adapter.js";import{emitRecoverableFailedTurn,emitTurnPreamble,getHarnessEmissionState,setHarnessEmissionState}from"#harness/emission.js";import{formatAvailableSkillsSection}from"#execution/skills/instructions.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";const log=createLogger(`hooks.lifecycle`);async function dispatchHookLifecycle(e){let{ctx:t,registry:n,emit:i}=e,o=getHarnessEmissionState(e.session.state),s=`turn_${o.sequence}`,c=o.sequence,l=buildHookContext(t),u={session:{sessionId:e.session.sessionId},turn:{sequence:c,turnId:s}},d=[],f=e.session;if(n.session.length>0&&t.get(SessionPreparedKey)!==!0){t.set(SessionPreparedKey,!0);let e=[];for(let r of n.session){let n=await r.handler(u,l);n?.modelContext!==void 0&&n.modelContext.length>0&&d.push(...n.modelContext),e.push(...normalizeLifecycleSkillResults(t,n))}f=await materializeLifecycleSkills(t,f,e)}let p=!1,m=o;try{let e=[];for(let r of n.turn){let n=await r.handler(u,l);n?.modelContext!==void 0&&n.modelContext.length>0&&d.push(...n.modelContext),e.push(...normalizeLifecycleSkillResults(t,n))}f=await materializeLifecycleSkills(t,f,e)}catch(t){let n=toErrorMessage(t);try{p||=(m=await emitTurnPreamble(i,{message:e.input.message},o,e.runtimeIdentity),!0);let t=await emitRecoverableFailedTurn(i,m,{code:`HOOK_TURN_FAILED`,message:n});return{kind:`turn-failed`,message:n,nextSession:setHarnessEmissionState(f,t)}}catch(e){throw log.error(`Event hook threw while emitting the turn.failed cascade for a lifecycle.turn failure — escalating to session.failed.`,{error:toErrorMessage(e)}),e}}let h=e.input.modelContext??[],g=h.length===0?d:[...h,...d];return{kind:`proceed`,input:g.length>0?{...e.input,modelContext:g}:e.input,nextSession:f}}function normalizeLifecycleSkillResults(e,t){if(t?.skills===void 0||t.skills.length===0)return[];let n=new Set(e.require(BundleKey).resolvedAgent.skills.map(e=>e.name)),r=new Map;for(let e of t.skills){let t=normalizeSkillPackage(e);if(n.has(t.name))throw Error(`Hook-contributed skill "${t.name}" conflicts with an authored skill.`);r.set(t.name,t)}return[...r.values()]}async function materializeLifecycleSkills(e,n,r){if(r.length===0)return n;let i=new Map(r.map(e=>[e.name,e])),a=await e.require(SandboxKey).get();if(a===null)throw Error(`Dynamic skills require a sandbox for the current agent.`);for(let e of i.values())await writeSkillPackageToSandbox({sandbox:a,skill:e});let o=formatAvailableSkillsSection([...i.values()]);return o===null?n:{...n,history:[...n.history,{role:`system`,content:o}]}}async function dispatchStreamEventHooks(e){let t=e.registry.streamEventsByType.get(e.event.type)??[],n=e.registry.streamEventsWildcard;if(t.length===0&&n.length===0)return;let r=buildHookContext(e.ctx);for(let n of t)await n.handler(e.event,r);for(let t of n)await t.handler(e.event,r)}function buildHookContext(t){let r=t.require(BundleKey),i=t.get(ChannelKey),a=t.get(ContinuationTokenKey),o=i===void 0?void 0:getAdapterKind(i);return{agent:{name:r.resolvedAgent.config.name??`agent`,nodeId:r.nodeId},channel:{kind:o,continuationToken:a},session:{sessionId:t.get(SessionIdKey)??``},ash:t}}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,37 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workflow-body orchestrator for the interactive OAuth
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* The workflow body owns hook creation and waiting; model-facing side
|
|
6
|
-
* effects stay inside durable steps.
|
|
2
|
+
* Workflow-body orchestrator for the interactive OAuth flow.
|
|
3
|
+
* Owns hook creation and waiting; side effects stay inside steps.
|
|
7
4
|
*/
|
|
8
|
-
import type {
|
|
5
|
+
import type { DurableSessionSnapshot, DurableSessionState } from "#execution/durable-session-store.js";
|
|
9
6
|
import type { PendingConnectionToolCall } from "#runtime/framework-tools/pending-connection-tool-calls.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* so the next turn step sees the spliced history and
|
|
14
|
-
* cached tokens.
|
|
15
|
-
*/
|
|
16
|
-
export interface AwaitAuthorizationResolveResult {
|
|
7
|
+
export interface AwaitAuthorizationInput {
|
|
8
|
+
readonly parentWritable: WritableStream<Uint8Array>;
|
|
9
|
+
readonly pendingToolCalls: readonly PendingConnectionToolCall[];
|
|
17
10
|
readonly serializedContext: Record<string, unknown>;
|
|
18
|
-
readonly
|
|
11
|
+
readonly sessionState: DurableSessionState;
|
|
12
|
+
readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
|
|
13
|
+
}
|
|
14
|
+
export interface AwaitAuthorizationResult {
|
|
15
|
+
readonly serializedContext: Record<string, unknown>;
|
|
16
|
+
readonly sessionState: DurableSessionState;
|
|
19
17
|
}
|
|
20
18
|
/**
|
|
21
|
-
* Orchestrates one
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* surface via `drainPendingConnectionAuthorizations` on the harness
|
|
25
|
-
* step boundary and rely on out-of-band auth.
|
|
19
|
+
* Orchestrates one authorization cycle for every interactive
|
|
20
|
+
* connection with pending tool calls. `getToken`-only connections
|
|
21
|
+
* are handled elsewhere via `drainPendingConnectionAuthorizations`.
|
|
26
22
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* `completeAuthorization` surface a structured error result on the
|
|
30
|
-
* spliced tool call rather than an exception in the workflow body.
|
|
23
|
+
* One connection's failure surfaces as a structured error result on
|
|
24
|
+
* the spliced tool call; it never aborts the workflow body.
|
|
31
25
|
*/
|
|
32
|
-
export declare function awaitAuthorizationAndResolve(input:
|
|
33
|
-
readonly parentWritable: WritableStream<Uint8Array>;
|
|
34
|
-
readonly pendingToolCalls: readonly PendingConnectionToolCall[];
|
|
35
|
-
readonly serializedContext: Record<string, unknown>;
|
|
36
|
-
readonly session: HarnessSession;
|
|
37
|
-
}): Promise<AwaitAuthorizationResolveResult>;
|
|
26
|
+
export declare function awaitAuthorizationAndResolve(input: AwaitAuthorizationInput): Promise<AwaitAuthorizationResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createAshConnectionCallbackRoutePath}from"#protocol/routes.js";import{
|
|
1
|
+
import{createAshConnectionCallbackRoutePath}from"#protocol/routes.js";import{createHook,getWorkflowMetadata}from"#compiled/@workflow/core/index.js";import{completeAuthorizationForConnectionStep,emitConnectionAuthorizationPendingStep,resolvePendingToolCallsStep,startAuthorizationForConnectionStep}from"#execution/connection-auth-steps.js";async function awaitAuthorizationAndResolve(s){let c=uniqueConnectionNames(s.pendingToolCalls);if(c.length===0)return{serializedContext:s.serializedContext,sessionState:s.sessionState};let l=s.sessionState.emissionState,u=trimTrailingSlash(getWorkflowMetadata().url),d=c.map(n=>{let r=createHook();return{connectionName:n,hook:r,webhookUrl:`${u}${createAshConnectionCallbackRoutePath(n,r.token)}`}}),f=await Promise.all(d.map(async({connectionName:e,hook:t,webhookUrl:n})=>({connectionName:e,hook:t,webhookUrl:n,start:await startAuthorizationForConnectionStep({connectionName:e,emissionState:l,parentWritable:s.parentWritable,serializedContext:s.serializedContext,webhookUrl:n})}))),p=f.flatMap(e=>e.start.ok?[{connectionName:e.connectionName,hook:e.hook,principal:e.start.principal,serializedContext:e.start.serializedContext,state:e.start.state,webhookUrl:e.webhookUrl}]:[]);p.length>0&&await emitConnectionAuthorizationPendingStep({connectionNames:p.map(e=>e.connectionName),emissionState:l,parentWritable:s.parentWritable,serializedContext:s.serializedContext});let m=await Promise.all(p.map(async({connectionName:e,hook:t,principal:n,serializedContext:i,state:a,webhookUrl:o})=>{try{let c=await awaitHookRequest(t);return{complete:await completeAuthorizationForConnectionStep({connectionName:e,emissionState:l,parentWritable:s.parentWritable,principal:n,request:c,serializedContext:i,state:a,webhookUrl:o}),connectionName:e,principal:n}}finally{t.dispose()}})),h={},g={},_={},v=[];for(let e of f)e.start.ok||(_[e.connectionName]={reason:e.start.reason,retryable:!1});for(let e of m)e.complete.ok?(h[e.connectionName]=e.complete.token,g[e.connectionName]=e.principal,v.push(e.connectionName)):_[e.connectionName]={reason:e.complete.reason,retryable:e.complete.retryable};return resolvePendingToolCallsStep({failedConnections:_,parentWritable:s.parentWritable,pendingCalls:s.pendingToolCalls,principals:g,resolvedConnectionNames:v,serializedContext:s.serializedContext,sessionState:s.sessionState,sessionWritable:s.sessionWritable,tokens:h})}function uniqueConnectionNames(e){let t=new Set;for(let n of e){let e=n.kind===`connection-execute`?[n.connectionName]:n.connectionNames;for(let n of e)t.add(n)}return[...t]}async function awaitHookRequest(e){let t=await e[Symbol.asyncIterator]().next();if(t.done===!0||t.value===void 0)throw Error(`Connection callback hook closed before delivering a request.`);return t.value}function trimTrailingSlash(e){return e.endsWith(`/`)?e.slice(0,-1):e}export{awaitAuthorizationAndResolve};
|
|
@@ -2,32 +2,21 @@
|
|
|
2
2
|
* Durable step boundaries that make up the interactive OAuth
|
|
3
3
|
* await-authorization-and-splice flow.
|
|
4
4
|
*/
|
|
5
|
+
import { type DurableSessionSnapshot, type DurableSessionState } from "#execution/durable-session-store.js";
|
|
5
6
|
import type { HarnessEmissionState } from "#harness/emission.js";
|
|
6
|
-
import type { HarnessSession } from "#harness/types.js";
|
|
7
7
|
import type { JsonValue } from "#public/types/json.js";
|
|
8
8
|
import type { AuthorizationCallbackRequest, ConnectionPrincipal, TokenResult } from "#runtime/connections/types.js";
|
|
9
9
|
import { type PendingConnectionToolCall } from "#runtime/framework-tools/pending-connection-tool-calls.js";
|
|
10
10
|
/**
|
|
11
11
|
* Result of one `startAuthorization` step.
|
|
12
12
|
*
|
|
13
|
-
* On success the
|
|
14
|
-
*
|
|
15
|
-
* `
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* step. The `serializedContext` carries any channel-state mutations
|
|
21
|
-
* the `connection.authorization_required` handler made (e.g. the
|
|
22
|
-
* Slack base layer's tracked status-message ts) forward into the
|
|
23
|
-
* matching `completeAuthorization` step so the handler can edit that
|
|
24
|
-
* same surface in place.
|
|
25
|
-
*
|
|
26
|
-
* On failure the runtime journals `{ ok: false, reason }` and the
|
|
27
|
-
* workflow body skips webhook provisioning for this connection. The
|
|
28
|
-
* workflow still emits a corresponding
|
|
29
|
-
* `connection.authorization_completed` event with
|
|
30
|
-
* `outcome: "failed"` so channels clean up their UI.
|
|
13
|
+
* On success carries the resolved `principal`, the IdP `state`, and
|
|
14
|
+
* the post-emit `serializedContext` (so channel-state mutations made
|
|
15
|
+
* by the `connection.authorization_required` handler — eg. tracked
|
|
16
|
+
* status-message ts — survive into the matching
|
|
17
|
+
* `completeAuthorization` step). Failures emit a
|
|
18
|
+
* `connection.authorization_completed` event with `outcome: "failed"`
|
|
19
|
+
* so channels clean up their UI.
|
|
31
20
|
*/
|
|
32
21
|
export type StartAuthorizationStepResult = {
|
|
33
22
|
readonly ok: true;
|
|
@@ -39,13 +28,10 @@ export type StartAuthorizationStepResult = {
|
|
|
39
28
|
readonly reason: string;
|
|
40
29
|
};
|
|
41
30
|
/**
|
|
42
|
-
* Runs one connection's `startAuthorization` callback
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* Failures are captured as {@link StartAuthorizationStepResult.ok} =
|
|
47
|
-
* `false` instead of thrown, so one connection's misconfigured
|
|
48
|
-
* authorization does not abort the entire authorization cycle.
|
|
31
|
+
* Runs one connection's `startAuthorization` callback and emits the
|
|
32
|
+
* matching `connection.authorization_required` event. Failures are
|
|
33
|
+
* captured as `ok: false` so one connection's misconfig does not
|
|
34
|
+
* abort the entire cycle.
|
|
49
35
|
*/
|
|
50
36
|
export declare function startAuthorizationForConnectionStep(input: {
|
|
51
37
|
readonly connectionName: string;
|
|
@@ -54,11 +40,7 @@ export declare function startAuthorizationForConnectionStep(input: {
|
|
|
54
40
|
readonly serializedContext: Record<string, unknown>;
|
|
55
41
|
readonly webhookUrl: string;
|
|
56
42
|
}): Promise<StartAuthorizationStepResult>;
|
|
57
|
-
/**
|
|
58
|
-
* Emits the `connection.authorization_pending` event once per
|
|
59
|
-
* authorization cycle, after every `authorization_required` has been
|
|
60
|
-
* written.
|
|
61
|
-
*/
|
|
43
|
+
/** Emits the cycle's single `connection.authorization_pending` event. */
|
|
62
44
|
export declare function emitConnectionAuthorizationPendingStep(input: {
|
|
63
45
|
readonly connectionNames: readonly string[];
|
|
64
46
|
readonly emissionState: HarnessEmissionState;
|
|
@@ -68,8 +50,10 @@ export declare function emitConnectionAuthorizationPendingStep(input: {
|
|
|
68
50
|
/**
|
|
69
51
|
* Result of one `completeAuthorization` step.
|
|
70
52
|
*
|
|
71
|
-
* `
|
|
72
|
-
*
|
|
53
|
+
* Failures default to `retryable: true` so the model can re-prompt for
|
|
54
|
+
* authorization; only explicit
|
|
55
|
+
* `ConnectionAuthorizationFailedError({ retryable: false })` opts out
|
|
56
|
+
* (canonically: user clicked "Cancel").
|
|
73
57
|
*/
|
|
74
58
|
export type CompleteAuthorizationStepResult = {
|
|
75
59
|
readonly ok: true;
|
|
@@ -77,27 +61,12 @@ export type CompleteAuthorizationStepResult = {
|
|
|
77
61
|
} | {
|
|
78
62
|
readonly ok: false;
|
|
79
63
|
readonly reason: string;
|
|
80
|
-
/**
|
|
81
|
-
* When `true`, downstream pending tool calls for this connection
|
|
82
|
-
* are spliced with a soft `authorization_required` result so the
|
|
83
|
-
* model can prompt the user to retry authorization rather than
|
|
84
|
-
* treating the turn as terminally failed. This is the default
|
|
85
|
-
* for any `completeAuthorization` throw — the runtime only marks
|
|
86
|
-
* a failure as non-retryable when the author explicitly sets
|
|
87
|
-
* `ConnectionAuthorizationFailedError({ retryable: false })`
|
|
88
|
-
* (canonically: user clicked "Cancel", `reason: "access_denied"`).
|
|
89
|
-
*/
|
|
90
64
|
readonly retryable: boolean;
|
|
91
65
|
};
|
|
92
66
|
/**
|
|
93
|
-
* Runs one connection's `completeAuthorization` callback
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* Any error surfaces as a failure result with an appropriate
|
|
98
|
-
* {@link ConnectionAuthorizationOutcome}. The workflow body continues
|
|
99
|
-
* even when one connection fails, so the remainder of the authorization
|
|
100
|
-
* cycle still resolves.
|
|
67
|
+
* Runs one connection's `completeAuthorization` callback and emits
|
|
68
|
+
* the matching `connection.authorization_completed` event. One
|
|
69
|
+
* connection's failure does not abort the cycle.
|
|
101
70
|
*/
|
|
102
71
|
export declare function completeAuthorizationForConnectionStep(input: {
|
|
103
72
|
readonly connectionName: string;
|
|
@@ -110,15 +79,9 @@ export declare function completeAuthorizationForConnectionStep(input: {
|
|
|
110
79
|
readonly webhookUrl: string;
|
|
111
80
|
}): Promise<CompleteAuthorizationStepResult>;
|
|
112
81
|
/**
|
|
113
|
-
* Structured failure
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* `retryable: true` (the default for any `completeAuthorization`
|
|
117
|
-
* throw) means the model should be prompted to retry authorization.
|
|
118
|
-
* `retryable: false` means the failure is terminal for this turn
|
|
119
|
-
* (canonically: user clicked "Cancel" on the IdP consent screen, so
|
|
120
|
-
* the author threw `ConnectionAuthorizationFailedError` with
|
|
121
|
-
* `{ reason: "access_denied", retryable: false }`).
|
|
82
|
+
* Structured failure for one connection's authorization round-trip.
|
|
83
|
+
* `retryable: true` lets the model re-prompt; `retryable: false` is
|
|
84
|
+
* terminal for this turn (canonically: user clicked "Cancel").
|
|
122
85
|
*/
|
|
123
86
|
export interface ConnectionAuthorizationFailure {
|
|
124
87
|
readonly reason: string;
|
|
@@ -126,9 +89,8 @@ export interface ConnectionAuthorizationFailure {
|
|
|
126
89
|
}
|
|
127
90
|
/**
|
|
128
91
|
* Retries every pending tool call whose connection has a freshly
|
|
129
|
-
* minted token, splices the real results over the
|
|
130
|
-
*
|
|
131
|
-
* the new session + serialized context.
|
|
92
|
+
* minted token, splices the real results over the placeholders in
|
|
93
|
+
* history, and clears the resolved cycle's context entries.
|
|
132
94
|
*/
|
|
133
95
|
export declare function resolvePendingToolCallsStep(input: {
|
|
134
96
|
readonly failedConnections: Readonly<Record<string, ConnectionAuthorizationFailure>>;
|
|
@@ -137,9 +99,10 @@ export declare function resolvePendingToolCallsStep(input: {
|
|
|
137
99
|
readonly principals: Readonly<Record<string, ConnectionPrincipal>>;
|
|
138
100
|
readonly resolvedConnectionNames: readonly string[];
|
|
139
101
|
readonly serializedContext: Record<string, unknown>;
|
|
140
|
-
readonly
|
|
102
|
+
readonly sessionState: DurableSessionState;
|
|
103
|
+
readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
|
|
141
104
|
readonly tokens: Readonly<Record<string, TokenResult>>;
|
|
142
105
|
}): Promise<{
|
|
143
106
|
readonly serializedContext: Record<string, unknown>;
|
|
144
|
-
readonly
|
|
107
|
+
readonly sessionState: DurableSessionState;
|
|
145
108
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createConnectionAuthorizationCompletedEvent,createConnectionAuthorizationPendingEvent,createConnectionAuthorizationRequiredEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{contextStorage}from"#context/container.js";import{callAdapterEventHandler}from"#channel/adapter.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{ConnectionRegistryImpl}from"#runtime/connections/registry.js";import{ConnectionRegistryKey,executeConnectionSearch}from"#runtime/framework-tools/connection-search.js";import{getActiveRuntimeNode}from"#context/node.js";import{PendingConnectionToolCallsKey,isConnectionAuthorizationPlaceholder}from"#runtime/framework-tools/pending-connection-tool-calls.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{withConnectionPrincipalOverride}from"#runtime/connections/principal-context.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{withDefaultAuthorizationInstructions}from"#execution/authorization-challenge-defaults.js";import{splicePendingToolResults}from"#execution/await-authorization-splice.js";async function startAuthorizationForConnectionStep(t){"use step";let r=await deserializeContext(t.serializedContext),i=findConnection(r,t.connectionName);if(i?.authorization?.startAuthorization===void 0){let n=`Connection "${t.connectionName}" does not define startAuthorization.`;return await emitAuthorizationEvent(r,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:n,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:n}}let a=i.description??t.connectionName,o,s,c;try{o=resolveConnectionPrincipal(t.connectionName,i.authorization,r);let e=await i.authorization.startAuthorization({principal:o,connection:{url:i.url},callbackUrl:t.webhookUrl});s=withDefaultAuthorizationInstructions(e.challenge,t.connectionName),c=e.state}catch(n){let i=n instanceof Error?n.message:String(n);return await emitAuthorizationEvent(r,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:i,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:i}}return await emitAuthorizationEvent(r,createConnectionAuthorizationRequiredEvent({authorization:s,connectionName:t.connectionName,description:a,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId,webhookUrl:t.webhookUrl}),t.parentWritable),{ok:!0,principal:o,serializedContext:serializeContext(r),state:c}}async function emitConnectionAuthorizationPendingStep(e){"use step";e.connectionNames.length!==0&&await emitAuthorizationEvent(await deserializeContext(e.serializedContext),createConnectionAuthorizationPendingEvent({connectionNames:e.connectionNames,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}),e.parentWritable)}async function completeAuthorizationForConnectionStep(t){"use step";let n=await deserializeContext(t.serializedContext),r=findConnection(n,t.connectionName);if(r?.authorization?.completeAuthorization===void 0){let r=`Connection "${t.connectionName}" does not define completeAuthorization.`;return await emitAuthorizationEvent(n,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:r,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:r,retryable:!1}}try{let i=await r.authorization.completeAuthorization({principal:t.principal,connection:{url:r.url},request:t.request,state:t.state,callbackUrl:t.webhookUrl});return await emitAuthorizationEvent(n,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`authorized`,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!0,token:i}}catch(r){let i=isConnectionAuthorizationFailedError(r)?r:null,a=i?.retryable??!0,o=i?.reason??(r instanceof Error?r.message:String(r));return await emitAuthorizationEvent(n,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:o,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:o,retryable:a}}}async function resolvePendingToolCallsStep(e){"use step";let t=await deserializeContext(e.serializedContext),n=new ConnectionRegistryImpl(getActiveRuntimeNode(
|
|
1
|
+
import{createConnectionAuthorizationCompletedEvent,createConnectionAuthorizationPendingEvent,createConnectionAuthorizationRequiredEvent,encodeMessageStreamEvent,timestampHandleMessageStreamEvent}from"#protocol/message.js";import{contextStorage}from"#context/container.js";import{callAdapterEventHandler}from"#channel/adapter.js";import{BundleKey,ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{ConnectionRegistryImpl}from"#runtime/connections/registry.js";import{ConnectionRegistryKey,executeConnectionSearch}from"#runtime/framework-tools/connection-search.js";import{getActiveRuntimeNode}from"#context/node.js";import{PendingConnectionToolCallsKey,isConnectionAuthorizationPlaceholder}from"#runtime/framework-tools/pending-connection-tool-calls.js";import{buildAdapterContext}from"#channel/adapter-context.js";import{deserializeContext,serializeContext}from"#context/serialize.js";import{readDurableSession,writeDurableSession}from"#execution/durable-session-store.js";import{hydrateDurableSession}from"#execution/session.js";import{isConnectionAuthorizationFailedError,isConnectionAuthorizationRequiredError}from"#public/connections/errors.js";import{writeCachedToken}from"#runtime/connections/authorization-tokens.js";import{withConnectionPrincipalOverride}from"#runtime/connections/principal-context.js";import{principalKey,resolveConnectionPrincipal}from"#runtime/connections/principal.js";import{withDefaultAuthorizationInstructions}from"#execution/authorization-challenge-defaults.js";import{splicePendingToolResults}from"#execution/await-authorization-splice.js";async function startAuthorizationForConnectionStep(t){"use step";let r=await deserializeContext(t.serializedContext),i=findConnection(r,t.connectionName);if(i?.authorization?.startAuthorization===void 0){let n=`Connection "${t.connectionName}" does not define startAuthorization.`;return await emitAuthorizationEvent(r,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:n,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:n}}let a=i.description??t.connectionName,o,s,c;try{o=resolveConnectionPrincipal(t.connectionName,i.authorization,r);let e=await i.authorization.startAuthorization({principal:o,connection:{url:i.url},callbackUrl:t.webhookUrl});s=withDefaultAuthorizationInstructions(e.challenge,t.connectionName),c=e.state}catch(n){let i=n instanceof Error?n.message:String(n);return await emitAuthorizationEvent(r,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:i,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:i}}return await emitAuthorizationEvent(r,createConnectionAuthorizationRequiredEvent({authorization:s,connectionName:t.connectionName,description:a,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId,webhookUrl:t.webhookUrl}),t.parentWritable),{ok:!0,principal:o,serializedContext:serializeContext(r),state:c}}async function emitConnectionAuthorizationPendingStep(e){"use step";e.connectionNames.length!==0&&await emitAuthorizationEvent(await deserializeContext(e.serializedContext),createConnectionAuthorizationPendingEvent({connectionNames:e.connectionNames,sequence:e.emissionState.sequence,stepIndex:e.emissionState.stepIndex,turnId:e.emissionState.turnId}),e.parentWritable)}async function completeAuthorizationForConnectionStep(t){"use step";let n=await deserializeContext(t.serializedContext),r=findConnection(n,t.connectionName);if(r?.authorization?.completeAuthorization===void 0){let r=`Connection "${t.connectionName}" does not define completeAuthorization.`;return await emitAuthorizationEvent(n,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:r,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:r,retryable:!1}}try{let i=await r.authorization.completeAuthorization({principal:t.principal,connection:{url:r.url},request:t.request,state:t.state,callbackUrl:t.webhookUrl});return await emitAuthorizationEvent(n,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`authorized`,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!0,token:i}}catch(r){let i=isConnectionAuthorizationFailedError(r)?r:null,a=i?.retryable??!0,o=i?.reason??(r instanceof Error?r.message:String(r));return await emitAuthorizationEvent(n,createConnectionAuthorizationCompletedEvent({connectionName:t.connectionName,outcome:`failed`,reason:o,sequence:t.emissionState.sequence,stepIndex:t.emissionState.stepIndex,turnId:t.emissionState.turnId}),t.parentWritable),{ok:!1,reason:o,retryable:a}}}async function resolvePendingToolCallsStep(e){"use step";let t=await readDurableSession(e.sessionState),n=await deserializeContext(e.serializedContext),r=n.require(BundleKey),i=hydrateDurableSession({compactionOverrides:{thresholdPercent:r.resolvedAgent.config.compaction?.thresholdPercent},durable:t,turnAgent:r.turnAgent}),o=new ConnectionRegistryImpl(getActiveRuntimeNode(n).agent?.connections??[]);n.setVirtualContext(ConnectionRegistryKey,o);for(let[t,r]of Object.entries(e.tokens)){let i=e.principals[t];if(i===void 0)throw Error(`Internal error: missing resolved principal for connection "${t}".`);writeCachedToken(n,t,principalKey(i),r)}let c={},d=new Set(e.resolvedConnectionNames);await contextStorage.run(n,()=>withConnectionPrincipalOverride(n,e.principals,async()=>{for(let t of e.pendingCalls){let n=t.kind===`connection-execute`?[t.connectionName]:t.connectionNames,r;for(let t of n){let n=e.failedConnections[t];if(n!==void 0){r={connectionName:t,...n};break}}if(r!==void 0&&t.kind===`connection-execute`){c[t.toolCallId]=r.retryable?{error:`authorization_required`,retryable:!0}:{connectionName:r.connectionName,error:`authorization_failed`,message:r.reason,retryable:!1};continue}if(!n.every(e=>d.has(e))&&t.kind===`connection-execute`){c[t.toolCallId]={error:`authorization_required`,retryable:!0};continue}try{if(t.kind===`connection-execute`){let e=await o.getClient(t.connectionName).executeTool(t.toolName,t.args);c[t.toolCallId]=normalizeToolResult(e)}else{let e=await executeConnectionSearch(t.args,{toolCallId:t.toolCallId});isConnectionAuthorizationPlaceholder(e)?c[t.toolCallId]={error:`authorization_required`,retryable:!0}:c[t.toolCallId]=normalizeToolResult(e)}}catch(e){if(isConnectionAuthorizationRequiredError(e)){c[t.toolCallId]={error:`authorization_required`,retryable:!0};continue}let n=e instanceof Error?e.message:String(e);c[t.toolCallId]={error:`tool_execution_failed`,message:n,retryable:!1}}}}));let f=splicePendingToolResults(i,c),p=e.pendingCalls.filter(t=>(t.kind===`connection-execute`?[t.connectionName]:t.connectionNames).some(t=>!d.has(t)&&e.failedConnections[t]===void 0));n.set(PendingConnectionToolCallsKey,p);let m=await writeDurableSession({session:f,writable:e.sessionWritable});return{serializedContext:serializeContext(n),sessionState:m}}function findConnection(e,t){return e.get(BundleKey)===void 0?void 0:(getActiveRuntimeNode(e).agent?.connections??[]).find(e=>e.connectionName===t)}async function emitAuthorizationEvent(e,t,n){let a=e.require(ChannelKey),s=buildAdapterContext(a,e),l=await callAdapterEventHandler(a,t,s);e.set(ChannelKey,{...a,state:{...s.state}});let u=n.getWriter();try{await u.write(encodeMessageStreamEvent(timestampHandleMessageStreamEvent(l)))}finally{u.releaseLock()}}function normalizeToolResult(e){if(e===void 0)return null;try{return JSON.parse(JSON.stringify(e))}catch{return{error:`tool_result_not_serializable`,retryable:!1}}}export{completeAuthorizationForConnectionStep,emitConnectionAuthorizationPendingStep,resolvePendingToolCallsStep,startAuthorizationForConnectionStep};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RuntimeCompiledArtifactsSource } from "#runtime/compiled-artifacts-source.js";
|
|
2
|
+
import { type DurableSessionSnapshot, type DurableSessionState } from "#execution/durable-session-store.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates the durable session and writes the initial snapshot to the
|
|
5
|
+
* `ash.session` stream before the workflow enters its turn loop.
|
|
6
|
+
* `nodeId` targets a subagent node in the compiled graph; omitted for
|
|
7
|
+
* the root agent.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createSessionStep(input: {
|
|
10
|
+
readonly compiledArtifactsSource: RuntimeCompiledArtifactsSource;
|
|
11
|
+
readonly continuationToken: string;
|
|
12
|
+
readonly nodeId?: string;
|
|
13
|
+
readonly sessionId: string;
|
|
14
|
+
readonly sessionWritable: WritableStream<DurableSessionSnapshot>;
|
|
15
|
+
}): Promise<DurableSessionState>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{writeDurableSession}from"#execution/durable-session-store.js";import{createSession}from"#execution/session.js";import{getCompiledRuntimeAgentBundle}from"#runtime/sessions/compiled-agent-cache.js";async function createSessionStep(e){"use step";let t=await getCompiledRuntimeAgentBundle({compiledArtifactsSource:e.compiledArtifactsSource,nodeId:e.nodeId});return await writeDurableSession({session:createSession({compactionOverrides:{thresholdPercent:t.resolvedAgent.config.compaction?.thresholdPercent},continuationToken:e.continuationToken,sessionId:e.sessionId,turnAgent:t.turnAgent}),writable:e.sessionWritable})}export{createSessionStep};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges a delegated subagent's terminal outcome back to its parent
|
|
3
|
+
* driver via the subagent-result hook. Pure projection helpers live
|
|
4
|
+
* in `delegated-parent-result.ts` so the workflow step-proxy transform
|
|
5
|
+
* doesn't strip them from this file.
|
|
6
|
+
*/
|
|
7
|
+
import type { RuntimeSubagentResultActionResult } from "#runtime/actions/types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Resumes the parent driver's hook with a delegated subagent result.
|
|
10
|
+
* No-op for root sessions.
|
|
11
|
+
*/
|
|
12
|
+
export declare function notifyDelegatedParentStep(input: {
|
|
13
|
+
readonly result: RuntimeSubagentResultActionResult | undefined;
|
|
14
|
+
readonly serializedContext: Record<string, unknown>;
|
|
15
|
+
}): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ChannelKey}from"#runtime/sessions/runtime-context-keys.js";import{deserializeContext}from"#context/serialize.js";import{SUBAGENT_ADAPTER_KIND}from"#execution/subagent-adapter.js";async function notifyDelegatedParentStep(e){"use step";if(e.result===void 0)return;let t=(await deserializeContext(e.serializedContext)).get(ChannelKey);if(t?.kind!==SUBAGENT_ADAPTER_KIND)return;let n=String(t.state?.parentContinuationToken??``);if(n===``)return;let{resumeHook:r}=await import(`#compiled/@workflow/core/runtime.js`);await r(n,{kind:`runtime-action-result`,results:[e.result]})}export{notifyDelegatedParentStep};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers that project a delegated subagent's terminal output
|
|
3
|
+
* into the runtime-action result shape its parent driver expects.
|
|
4
|
+
* Lives in its own (non-directive) file to escape the workflow
|
|
5
|
+
* step-proxy transform.
|
|
6
|
+
*/
|
|
7
|
+
import type { RuntimeSubagentResultActionResult } from "#runtime/actions/types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Builds the success-shaped {@link RuntimeSubagentResultActionResult}.
|
|
10
|
+
* Returns `undefined` for root sessions (no parent to notify).
|
|
11
|
+
*/
|
|
12
|
+
export declare function createDelegatedSubagentSuccessResult(serializedContext: Record<string, unknown>, output: string): RuntimeSubagentResultActionResult | undefined;
|
|
13
|
+
/** Failure-path mirror of {@link createDelegatedSubagentSuccessResult}. */
|
|
14
|
+
export declare function createDelegatedSubagentErrorResult(serializedContext: Record<string, unknown>, error: unknown): RuntimeSubagentResultActionResult | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{toErrorMessage}from"#shared/errors.js";import{SUBAGENT_ADAPTER_KIND}from"#execution/subagent-adapter.js";function createDelegatedSubagentSuccessResult(e,n){let r=e[`ash.channel`];if(r?.kind===SUBAGENT_ADAPTER_KIND)return{callId:String(r.state?.callId??``),kind:`subagent-result`,output:n,subagentName:String(r.state?.subagentName??``)}}function createDelegatedSubagentErrorResult(t,n){let r=createDelegatedSubagentSuccessResult(t,``);if(r!==void 0)return{...r,isError:!0,output:{code:`SUBAGENT_EXECUTION_FAILED`,message:toErrorMessage(n)}}}export{createDelegatedSubagentErrorResult,createDelegatedSubagentSuccessResult};
|