@bastani/atomic 0.8.29-alpha.3 → 0.8.29
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 +9 -6
- package/dist/builtin/cursor/CHANGELOG.md +2 -0
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +3 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +3 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +5 -4
- package/dist/builtin/subagents/README.md +4 -4
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.ts +14 -0
- package/dist/builtin/subagents/src/extension/schemas.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +1 -6
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +1 -6
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +0 -1
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +0 -1
- package/dist/builtin/subagents/src/runs/shared/structured-output.ts +16 -285
- package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
- package/dist/builtin/subagents/src/shared/types.ts +0 -1
- package/dist/builtin/subagents/src/slash/saved-chain-mapping.ts +3 -18
- package/dist/builtin/web-access/CHANGELOG.md +3 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +6 -5
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/builtin/goal.ts +8 -52
- package/dist/builtin/workflows/builtin/open-claude-design.ts +15 -38
- package/dist/builtin/workflows/builtin/ralph.ts +11 -50
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.ts +17 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +2 -29
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +1 -5
- package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +1 -1
- package/dist/builtin/workflows/src/shared/types.ts +1 -1
- package/dist/core/sdk.d.ts +3 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +0 -36
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/structured-output.d.ts +7 -18
- package/dist/core/tools/structured-output.d.ts.map +1 -1
- package/dist/core/tools/structured-output.js +9 -89
- package/dist/core/tools/structured-output.js.map +1 -1
- package/dist/core/tools/todos.d.ts +1 -0
- package/dist/core/tools/todos.d.ts.map +1 -1
- package/dist/core/tools/todos.js +4 -0
- package/dist/core/tools/todos.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/docs/extensions.md +1 -1
- package/docs/sdk.md +1 -1
- package/docs/subagents.md +4 -6
- package/docs/usage.md +1 -1
- package/docs/workflows.md +5 -5
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.8.29] - 2026-06-15
|
|
6
|
+
|
|
5
7
|
### Added
|
|
6
8
|
|
|
7
9
|
- Added support for local `-e <dir>` extension sources to borrow project-local Atomic resources from `<dir>/.atomic`, legacy `<dir>/.pi`, and `<dir>/.agents/skills` after resolving trust for that extension source, preserving package-manager provenance and explicit-path workflow forwarding while avoiding untrusted borrowed resources ([#1354](https://github.com/bastani-inc/atomic/issues/1354)).
|
|
8
10
|
- Added a prototype Rust/N-API Cursor HTTP/2 native transport binding through the generated `@bastani/atomic-natives` NAPI-RS package, so Atomic can use an in-process native HTTP/2 client without requiring Node.js on `PATH`.
|
|
9
11
|
- Added the experimental bundled `@bastani/cursor` provider scaffold so `/login` can offer Cursor OAuth, estimated fallback exposes `cursor/composer-2`, and Cursor model mapping/streaming hooks are available behind an isolated HTTP/2 Connect transport boundary with production-default protobuf decoding, buffered Connect frames, write-before-headers Run streaming, stable Cursor conversation ids, schema-correct Cursor MCP tool advertisement, Cursor MCP tool-call decoding with protobuf `Value` or raw UTF-8/JSON arguments and exec-id metadata, same-stream MCP tool-result resume, abort/idle cleanup for paused tool streams, Connect end-stream error classification, exact live model id fidelity without static default injection, fast/thinking catalog grouping, and usage-delta accumulation.
|
|
10
|
-
- Added the opt-in `createStructuredOutputTool({ schema, capture, output, name })` factory for terminating machine-readable final answers with schema-as-parameters
|
|
12
|
+
- Added the opt-in `createStructuredOutputTool({ schema, capture, output, name })` factory for terminating machine-readable final answers with direct schema-as-parameters capture, flat `details`, in-process capture, configurable private file capture via `output.outputPath`, and the concise two-line `structured_output` prompt guidance from `pi-dynamic-workflows` without registering `structured_output` in normal agent sessions by default ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
11
13
|
|
|
12
14
|
### Changed
|
|
13
15
|
|
|
@@ -15,17 +17,18 @@
|
|
|
15
17
|
- Changed bundled `goal`, `ralph`, and `open-claude-design` decision gates to use schema-backed workflow `structured_output` stages instead of registering bespoke terminating custom tools.
|
|
16
18
|
- Changed bundled `goal` worker/reviewer prompts and `ralph` orchestrator/reviewer prompts to request end-to-end verification when practical, using browser-skilled subagents for web/frontend flows that may depend on backend/API behavior and tmux-skilled subagents for TUI or terminal-app scenarios.
|
|
17
19
|
- Bumped the bundled upstream pi runtime libraries `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` from `^0.79.1` to `^0.79.3`, bringing the latest upstream provider, model, agent-core, and TUI compatibility fixes into `@bastani/atomic`.
|
|
18
|
-
- Updated the structured-output extension example and SDK/workflow/extension docs to use the canonical factory instead of hand-rolled `terminate: true` wrappers, and documented that
|
|
19
|
-
-
|
|
20
|
+
- Updated the structured-output extension example and SDK/workflow/extension docs to use the canonical factory instead of hand-rolled `terminate: true` wrappers, and documented that Atomic passes the supplied schema directly to the tool without additional structured-output parsing, object-root restrictions, or sidecar validation ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
21
|
+
- Made custom-named factory tools advertise the configured name in concise prompt metadata, and documented that text print mode recognizes factory-created custom structured-output tools without treating every terminating tool as printable ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
22
|
+
- Changed factory-created structured-output tool descriptions and prompt guidance to use context-neutral final-result wording for SDK, extension, workflow, and subagent registrations ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
20
23
|
- Clarified SDK, extension, and workflow guidance that structured-output tools are opt-in custom tools, with workflow stages/tasks/chains/parallel items receiving `structured_output` only when they declare a `schema` and subagent children receiving it only when `outputSchema` is enabled ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
21
24
|
|
|
22
25
|
### Fixed
|
|
23
26
|
|
|
24
27
|
- Fixed the root `@bastani/atomic` package export to include a `default` condition alongside the ESM import target, improving compatibility with loaders that select default export conditions.
|
|
25
28
|
- Fixed extension custom UI focus deferral so full-screen overlays can keep keyboard focus while a parent/main-chat inline custom UI is pending, then focus that pending UI when the overlay is hidden; already-aborted custom UI calls no longer invoke factories or emit host custom-UI state changes ([#1353](https://github.com/bastani-inc/atomic/issues/1353)).
|
|
26
|
-
- Fixed text print mode to emit trailing terminating JSON from factory-created structured-output tools, including custom tool names such as `final_decision`, instead of only recognizing the canonical `structured_output` name ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
27
|
-
- Fixed terminating `structured_output` results to opt out of oversized-result persistence
|
|
28
|
-
- Fixed cross-process structured-output file capture to preserve flat
|
|
29
|
+
- Fixed text print mode to emit trailing terminating JSON from factory-created structured-output tools, including custom tool names such as `final_decision`, instead of only recognizing the canonical `structured_output` name; the same structured value remains available through `details`, capture sinks, and workflow/subagent structured result fields ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
30
|
+
- Fixed terminating `structured_output` results to opt out of oversized-result persistence ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
31
|
+
- Fixed cross-process structured-output file capture to preserve flat tool arguments in `output.json` without sidecar metadata or transcript-finality parsing ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
29
32
|
- Fixed bundled subagent handling so explicit empty `tools: []` plus `outputSchema` grants only the schema-backed `structured_output` runtime tool instead of restoring default tools ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
30
33
|
- Fixed prerelease publishing for native Atomic artifacts by allowing the `@bastani/atomic-natives` package metadata in release-preparation verification, running native artifact builds on architecture-matched Blacksmith and macOS runners, and documenting the two-package publish flow while keeping npm provenance publishing on GitHub-hosted Ubuntu.
|
|
31
34
|
- Fixed the bundled experimental Cursor provider to honor per-request stream deadlines across open/read/resume writes, reset timed-out or aborted streams, clean up replaced paused turns safely, catch cleanup cancellation failures, tolerate non-MCP Cursor exec protocol messages without ending assistant turns, and align Run requests with Cursor's private CLI protocol by using blob/KV conversation state plus request-context tool-definition responses without the unsupported custom system-prompt field ([#1286](https://github.com/bastani-inc/atomic/issues/1286)).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/cursor",
|
|
3
|
-
"version": "0.8.29
|
|
3
|
+
"version": "0.8.29",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Experimental first-party Atomic extension for Cursor OAuth, model discovery, and streaming provider registration.",
|
|
6
6
|
"contributors": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@bastani/atomic-natives": "0.8.29
|
|
43
|
+
"@bastani/atomic-natives": "0.8.29",
|
|
44
44
|
"@bufbuild/protobuf": "^2.0.0"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -4,9 +4,11 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.29] - 2026-06-15
|
|
8
|
+
|
|
7
9
|
### Changed
|
|
8
10
|
|
|
9
|
-
- Published a synchronized Atomic 0.8.29
|
|
11
|
+
- Published a synchronized Atomic 0.8.29 stable release with the upstream pi TUI dependency aligned to `^0.79.3`; no functional changes were made in the intercom extension.
|
|
10
12
|
|
|
11
13
|
## [0.8.28] - 2026-06-11
|
|
12
14
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.8.29
|
|
3
|
+
"version": "0.8.29",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
|
|
6
6
|
"contributors": [
|
|
@@ -7,9 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.29] - 2026-06-15
|
|
11
|
+
|
|
10
12
|
### Changed
|
|
11
13
|
|
|
12
|
-
- Published a synchronized Atomic 0.8.29
|
|
14
|
+
- Published a synchronized Atomic 0.8.29 stable release with upstream pi AI/TUI dependencies aligned to `^0.79.3`; no functional changes were made in the MCP extension.
|
|
13
15
|
|
|
14
16
|
## [0.8.28] - 2026-06-11
|
|
15
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/mcp",
|
|
3
|
-
"version": "0.8.29
|
|
3
|
+
"version": "0.8.29",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
|
|
6
6
|
"contributors": [
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.8.29] - 2026-06-15
|
|
6
|
+
|
|
5
7
|
### Changed
|
|
6
8
|
|
|
7
9
|
- Aligned the subagents extension with upstream pi `^0.79.3` runtime packages (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui`) so child sessions inherit the latest shared agent, provider, and TUI compatibility fixes.
|
|
8
|
-
- Changed `outputSchema` child runs to use Atomic's shared `structured_output` factory with
|
|
9
|
-
-
|
|
10
|
-
- Kept dynamic fanout `collect.outputSchema` as a general JSON Schema validator so saved chains and direct runs can validate aggregate arrays with array-root schemas while child `outputSchema` tool contracts remain object-rooted ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
10
|
+
- Changed `outputSchema` child runs to use Atomic's shared `structured_output` factory with direct schema parameters, concise two-line prompt guidance, parent-side `structuredOutput` capture from `output.json`, and auto-allowing the required tool for explicit child tool allowlists ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
11
|
+
- Removed parent-side top-level-object restrictions from child `outputSchema`; any plain JSON Schema object can be passed directly to the `structured_output` tool ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
11
12
|
|
|
12
13
|
### Fixed
|
|
13
14
|
|
|
14
|
-
- Fixed subagent `outputSchema` readback to
|
|
15
|
+
- Fixed subagent `outputSchema` readback to use the captured `output.json` directly without sidecar metadata, transcript-finality parsing, duplicate-call checks, or parent-side schema revalidation ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
15
16
|
- Fixed explicit empty child `tools: []` allowlists with `outputSchema` to pass only `--tools structured_output`, keeping the restricted child from regaining default tools while still enabling the required final-answer channel ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
|
|
16
17
|
- Fixed workflow-stage subagent depth handling so bundled workflow stages inherit the main-chat two-hop subagent nesting budget while preserving stricter configured limits, and updated the nested-depth rejection message to describe the maximum-depth condition ([#1372](https://github.com/bastani-inc/atomic/pull/1372)).
|
|
17
18
|
|
|
@@ -531,15 +531,15 @@ Create an implementation plan based on {outputs.context}
|
|
|
531
531
|
|
|
532
532
|
Each `.chain.md` `## agent-name` section is a step. Config lines such as `phase`, `label`, `as`, `outputSchema`, `output`, `outputMode`, `reads`, `model`, `skills`, and `progress` go immediately after the header. A blank line separates config from task text. In saved `.chain.md` files, `outputSchema` is a path to a JSON Schema file; direct tool calls and `.chain.json` files can pass the schema object inline.
|
|
533
533
|
|
|
534
|
-
When `outputSchema` is present, the child receives a schema-specific `structured_output` tool backed by Atomic's shared factory. The schema
|
|
534
|
+
When `outputSchema` is present, the child receives a schema-specific `structured_output` tool backed by Atomic's shared factory. The schema is passed directly to the tool. The child writes the tool arguments to `output.json`, and the parent reads that JSON back as `structuredOutput`; Atomic no longer adds object-root restrictions, sidecar metadata, transcript-finality checks, or duplicate-call guards.
|
|
535
535
|
|
|
536
|
-
Children without `outputSchema` do not receive `structured_output` from Atomic's default tool registry. They can still use a custom extension-provided terminating tool if you explicitly add one
|
|
536
|
+
Children without `outputSchema` do not receive `structured_output` from Atomic's default tool registry. They can still use a custom extension-provided terminating tool if you explicitly add one.
|
|
537
537
|
|
|
538
538
|
For `output`, `reads`, `skills`, and `progress`, chain behavior is three-state: omitted inherits from the agent, a value overrides, and `false` disables.
|
|
539
539
|
|
|
540
540
|
Use `phase` to group related work in status output, `label` for a readable step name, and `as` to store a successful step or parallel task result for later `{outputs.name}` references. Duplicate `as` names, invalid identifiers, and unknown output references fail before child execution.
|
|
541
541
|
|
|
542
|
-
Dynamic fanout is available only through direct `subagent({ chain: [...] })` JSON or saved `.chain.json` files. It expands an array from a prior structured named output, runs one child template per item, and stores the ordered collection under `collect.as`. The source must be structured output; prose is never parsed. `expand.maxItems` is required, over-limit arrays fail, nested fanout and arbitrary expressions are not supported, and `.chain.md` has no dynamic syntax in this release. `collect.outputSchema` validates the collected array after child execution
|
|
542
|
+
Dynamic fanout is available only through direct `subagent({ chain: [...] })` JSON or saved `.chain.json` files. It expands an array from a prior structured named output, runs one child template per item, and stores the ordered collection under `collect.as`. The source must be structured output; prose is never parsed. `expand.maxItems` is required, over-limit arrays fail, nested fanout and arbitrary expressions are not supported, and `.chain.md` has no dynamic syntax in this release. `collect.outputSchema` validates the collected array after child execution.
|
|
543
543
|
|
|
544
544
|
```json
|
|
545
545
|
{
|
|
@@ -820,7 +820,7 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
820
820
|
|
|
821
821
|
Use `outputMode: "file-only"` when a saved output may be large and the parent only needs a pointer. The returned text is a compact reference like `Output saved to: /abs/report.md (48.2 KB, 2847 lines). Read this file if needed.` Failed runs and save errors still return normal inline output for debugging. In chains, later `{previous}` steps receive the same compact reference when the prior step used file-only mode.
|
|
822
822
|
|
|
823
|
-
Sequential and parallel chain tasks accept `agent`, `task`, `phase`, `label`, `as`, `outputSchema`, `cwd`, `output`, `outputMode`, `reads`, `progress`, `skill`, and `model`. Parallel tasks also accept `count`. Parallel step groups accept `parallel`, `concurrency`, `failFast`, and `worktree`. If child `outputSchema` is present,
|
|
823
|
+
Sequential and parallel chain tasks accept `agent`, `task`, `phase`, `label`, `as`, `outputSchema`, `cwd`, `output`, `outputMode`, `reads`, `progress`, `skill`, and `model`. Parallel tasks also accept `count`. Parallel step groups accept `parallel`, `concurrency`, `failFast`, and `worktree`. If child `outputSchema` is present, Atomic injects `structured_output`, writes the child tool arguments to `output.json`, and preserves the parsed value on the step result; `as` also exposes a compact text representation through `{outputs.name}`. Without `outputSchema`, Atomic does not inject `structured_output` into the child.
|
|
824
824
|
|
|
825
825
|
Status and control actions:
|
|
826
826
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.8.29
|
|
3
|
+
"version": "0.8.29",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification. Fork of: https://github.com/nicobailon/pi-subagents",
|
|
6
6
|
"contributors": [
|
|
@@ -213,6 +213,19 @@ class SubagentControlNoticeComponent implements Component {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
export const DEFAULT_PROMPT_GUIDANCE: string[] = [
|
|
217
|
+
`**Subagent Orchestration**:
|
|
218
|
+
- To avoid draining your context window, prefer to use subagents for complex tasks all non-trivial operations should be delegated to subagents.
|
|
219
|
+
- You should delegate running bash commands (particularly ones that are likely to produce lots of output) such as investigating with the \`aws\` CLI, using the \`gh\` CLI, digging through logs to \`bash\` subagents.
|
|
220
|
+
- You should use separate subagents for separate tasks, and you may launch them in parallel, but do not delegate multiple tasks that are likely to have significant overlap to separate subagents.
|
|
221
|
+
- Sometimes subagents will take a long time. DO NOT attempt to do the job yourself while waiting for the subagent to respond Instead, use the time to plan out your next steps.
|
|
222
|
+
- **Debugging**: When a user asks about debugging, spawn a debugger subagent first.
|
|
223
|
+
- Do not attempt to debug or analyze code yourself without first consulting the debugger subagent.
|
|
224
|
+
- Explain the debugger's insights to the user clearly and concisely.
|
|
225
|
+
- Once the user confirms, implement the necessary code changes based on those insights.
|
|
226
|
+
- If the user has follow-up questions, spawn additional debugger and research subagents as needed.`,
|
|
227
|
+
];
|
|
228
|
+
|
|
216
229
|
export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
217
230
|
if (getEnvValue(SUBAGENT_CHILD_ENV) === "1") {
|
|
218
231
|
if (getEnvValue(SUBAGENT_FANOUT_CHILD_ENV) === "1") registerFanoutChildSubagentExtension(pi);
|
|
@@ -431,6 +444,7 @@ CONTROL:
|
|
|
431
444
|
DIAGNOSTICS:
|
|
432
445
|
• { action: "doctor" } - read-only report for runtime paths, discovery, sessions, and intercom`,
|
|
433
446
|
parameters: SubagentParams,
|
|
447
|
+
promptGuidelines: DEFAULT_PROMPT_GUIDANCE,
|
|
434
448
|
|
|
435
449
|
execute(id, params, signal, onUpdate, ctx) {
|
|
436
450
|
return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
|
|
@@ -38,7 +38,7 @@ const ReadsOverride = Type.Unsafe({
|
|
|
38
38
|
const JsonSchemaObject = Type.Unsafe({
|
|
39
39
|
type: "object",
|
|
40
40
|
additionalProperties: true,
|
|
41
|
-
description: "JSON Schema object for
|
|
41
|
+
description: "Plain JSON Schema object for structured output.",
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
const AcceptanceEvidenceKind = Type.String({
|
|
@@ -748,12 +748,7 @@ async function runSingleStep(
|
|
|
748
748
|
let structuredOutput: unknown;
|
|
749
749
|
let structuredError: string | undefined;
|
|
750
750
|
if (effectiveStructuredOutput && run.exitCode === 0 && !run.error && !hiddenError?.hasError) {
|
|
751
|
-
const structured = readStructuredOutput(
|
|
752
|
-
schema: effectiveStructuredOutput.schema,
|
|
753
|
-
schemaPath: effectiveStructuredOutput.schemaPath,
|
|
754
|
-
outputPath: effectiveStructuredOutput.outputPath,
|
|
755
|
-
metadataPath: effectiveStructuredOutput.metadataPath,
|
|
756
|
-
}, { messages: run.messages });
|
|
751
|
+
const structured = readStructuredOutput(effectiveStructuredOutput);
|
|
757
752
|
if (structured.error) structuredError = structured.error;
|
|
758
753
|
else structuredOutput = structured.value;
|
|
759
754
|
}
|
|
@@ -748,12 +748,7 @@ async function runSingleAttempt(
|
|
|
748
748
|
}
|
|
749
749
|
}
|
|
750
750
|
if (options.structuredOutput && result.exitCode === 0 && !result.error) {
|
|
751
|
-
const structured = readStructuredOutput(
|
|
752
|
-
schema: options.structuredOutput.schema,
|
|
753
|
-
schemaPath: options.structuredOutput.schemaPath,
|
|
754
|
-
outputPath: options.structuredOutput.outputPath,
|
|
755
|
-
metadataPath: options.structuredOutput.metadataPath,
|
|
756
|
-
}, { messages: result.messages ?? [] });
|
|
751
|
+
const structured = readStructuredOutput(options.structuredOutput);
|
|
757
752
|
result.structuredOutputSchemaPath = options.structuredOutput.schemaPath;
|
|
758
753
|
result.structuredOutputPath = options.structuredOutput.outputPath;
|
|
759
754
|
if (structured.error) {
|
|
@@ -33,7 +33,6 @@ export interface RunnerSubagentStep {
|
|
|
33
33
|
schema: import("../../shared/types.ts").JsonSchemaObject;
|
|
34
34
|
schemaPath: string;
|
|
35
35
|
outputPath: string;
|
|
36
|
-
metadataPath: string;
|
|
37
36
|
};
|
|
38
37
|
structuredOutputSchema?: import("../../shared/types.ts").JsonSchemaObject;
|
|
39
38
|
effectiveAcceptance?: import("../../shared/types.ts").ResolvedAcceptanceConfig;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { APP_NAME
|
|
4
|
+
import { APP_NAME } from "@bastani/atomic";
|
|
5
5
|
import { Compile } from "typebox/compile";
|
|
6
6
|
import type { JsonSchemaObject } from "../../shared/types.ts";
|
|
7
7
|
|
|
@@ -9,239 +9,10 @@ const ENV_PREFIX = APP_NAME.toUpperCase();
|
|
|
9
9
|
export const STRUCTURED_OUTPUT_SCHEMA_ENV = `${ENV_PREFIX}_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA`;
|
|
10
10
|
export const STRUCTURED_OUTPUT_CAPTURE_ENV = `${ENV_PREFIX}_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE`;
|
|
11
11
|
|
|
12
|
-
type JsonPrimitive = string | number | boolean | null;
|
|
13
|
-
type JsonArray = readonly JsonValue[];
|
|
14
|
-
type JsonRecord = { readonly [key: string]: JsonValue };
|
|
15
|
-
type JsonValue = JsonPrimitive | JsonArray | JsonRecord;
|
|
16
|
-
|
|
17
12
|
export interface StructuredOutputRuntime {
|
|
18
13
|
schema: JsonSchemaObject;
|
|
19
14
|
schemaPath: string;
|
|
20
15
|
outputPath: string;
|
|
21
|
-
metadataPath: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface StructuredOutputCaptureMetadata {
|
|
25
|
-
toolName: string;
|
|
26
|
-
toolCallId: string;
|
|
27
|
-
success: true;
|
|
28
|
-
terminate: true;
|
|
29
|
-
capturedAt?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface StructuredOutputTranscriptContent {
|
|
33
|
-
readonly type?: string;
|
|
34
|
-
readonly id?: string;
|
|
35
|
-
readonly name?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface StructuredOutputTranscriptMessage {
|
|
39
|
-
readonly role: string;
|
|
40
|
-
readonly content?: string | readonly StructuredOutputTranscriptContent[];
|
|
41
|
-
readonly toolCallId?: string;
|
|
42
|
-
readonly toolName?: string;
|
|
43
|
-
readonly isError?: boolean;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface ReadStructuredOutputOptions {
|
|
47
|
-
messages?: readonly StructuredOutputTranscriptMessage[];
|
|
48
|
-
toolName?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface CompiledJsonSchema {
|
|
52
|
-
Check(value: unknown): boolean;
|
|
53
|
-
Errors(value: unknown): Iterable<{ instancePath?: string; message?: string }>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type JsonSchemaRootDescriptor = {
|
|
57
|
-
readonly type?: string | readonly string[];
|
|
58
|
-
readonly anyOf?: readonly JsonSchemaObject[];
|
|
59
|
-
readonly oneOf?: readonly JsonSchemaObject[];
|
|
60
|
-
readonly allOf?: readonly JsonSchemaObject[];
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
type ToolCallBlock = {
|
|
64
|
-
readonly type: "toolCall";
|
|
65
|
-
readonly id?: string;
|
|
66
|
-
readonly name?: string;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
function schemaTypeIsObjectOnly(type: JsonSchemaRootDescriptor["type"]): boolean {
|
|
70
|
-
if (type === "object") return true;
|
|
71
|
-
return Array.isArray(type) && type.length === 1 && type[0] === "object";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function isTopLevelObjectOutputSchema(schema: JsonSchemaObject): boolean {
|
|
75
|
-
const descriptor = schema as JsonSchemaRootDescriptor;
|
|
76
|
-
if (schemaTypeIsObjectOnly(descriptor.type)) return true;
|
|
77
|
-
if (descriptor.type !== undefined) return false;
|
|
78
|
-
if (Array.isArray(descriptor.anyOf) || Array.isArray(descriptor.oneOf)) return false;
|
|
79
|
-
if (Array.isArray(descriptor.allOf)) {
|
|
80
|
-
return descriptor.allOf.length > 0 && descriptor.allOf.every((member) => isTopLevelObjectOutputSchema(member));
|
|
81
|
-
}
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function isJsonRecord(value: JsonValue): value is JsonRecord {
|
|
86
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function stringField(record: JsonRecord, key: string): string | undefined {
|
|
90
|
-
const value = record[key];
|
|
91
|
-
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function booleanField(record: JsonRecord, key: string): boolean | undefined {
|
|
95
|
-
const value = record[key];
|
|
96
|
-
return typeof value === "boolean" ? value : undefined;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function roleOf(message: StructuredOutputTranscriptMessage): string {
|
|
100
|
-
return message.role;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function toolCallBlocks(message: StructuredOutputTranscriptMessage): ToolCallBlock[] {
|
|
104
|
-
if (roleOf(message) !== "assistant" || !Array.isArray(message.content)) return [];
|
|
105
|
-
return message.content
|
|
106
|
-
.filter((block): block is ToolCallBlock => block.type === "toolCall")
|
|
107
|
-
.map((block) => ({ type: "toolCall", id: block.id, name: block.name }));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function isFinalityRelevantMessage(message: StructuredOutputTranscriptMessage): boolean {
|
|
111
|
-
const role = roleOf(message);
|
|
112
|
-
// `custom` entries are host/runtime annotations (for example display/status
|
|
113
|
-
// messages) rather than additional child model output, so they should not make
|
|
114
|
-
// an otherwise-final structured_output capture look stale.
|
|
115
|
-
return role === "assistant" || role === "toolResult";
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function finalityInvalid(message: string): { status: "invalid"; message: string } {
|
|
119
|
-
return { status: "invalid", message };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function parseCaptureMetadata(value: JsonValue): { metadata?: StructuredOutputCaptureMetadata; error?: string } {
|
|
123
|
-
if (!isJsonRecord(value)) {
|
|
124
|
-
return { error: "Structured output metadata sidecar must contain an object with call metadata." };
|
|
125
|
-
}
|
|
126
|
-
const toolName = stringField(value, "toolName");
|
|
127
|
-
const toolCallId = stringField(value, "toolCallId");
|
|
128
|
-
if (!toolName || !toolCallId) {
|
|
129
|
-
return { error: "Structured output metadata sidecar is missing toolName or toolCallId metadata." };
|
|
130
|
-
}
|
|
131
|
-
if (booleanField(value, "success") !== true) {
|
|
132
|
-
return { error: "Structured output capture was not marked successful." };
|
|
133
|
-
}
|
|
134
|
-
if (booleanField(value, "terminate") !== true) {
|
|
135
|
-
return { error: "Structured output capture was not marked as a terminating action." };
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
metadata: {
|
|
139
|
-
toolName,
|
|
140
|
-
toolCallId,
|
|
141
|
-
success: true,
|
|
142
|
-
terminate: true,
|
|
143
|
-
...(typeof value.capturedAt === "string" ? { capturedAt: value.capturedAt } : {}),
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function verifyStructuredOutputFinality(
|
|
149
|
-
messages: readonly StructuredOutputTranscriptMessage[],
|
|
150
|
-
metadata: StructuredOutputCaptureMetadata,
|
|
151
|
-
expectedToolName: string,
|
|
152
|
-
): { status: "valid" } | { status: "invalid"; message: string } {
|
|
153
|
-
if (metadata.toolName !== expectedToolName) {
|
|
154
|
-
return finalityInvalid(
|
|
155
|
-
`Captured structured output tool name ${JSON.stringify(metadata.toolName)} did not match expected ${JSON.stringify(expectedToolName)}.`,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
if (messages.length === 0) {
|
|
159
|
-
return finalityInvalid("Structured output finality could not be verified because the child transcript is empty.");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let structuredOutputCallCount = 0;
|
|
163
|
-
let assistantIndex = -1;
|
|
164
|
-
let matchingAssistantToolCalls: ToolCallBlock[] = [];
|
|
165
|
-
for (let index = 0; index < messages.length; index++) {
|
|
166
|
-
const calls = toolCallBlocks(messages[index]);
|
|
167
|
-
if (calls.length === 0) continue;
|
|
168
|
-
for (const call of calls) {
|
|
169
|
-
if (call.name === metadata.toolName) {
|
|
170
|
-
structuredOutputCallCount += 1;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
const idMatch = calls.find((call) => call.id === metadata.toolCallId);
|
|
174
|
-
if (!idMatch) continue;
|
|
175
|
-
if (idMatch.name !== metadata.toolName) {
|
|
176
|
-
return finalityInvalid(
|
|
177
|
-
`Captured structured output tool call ${JSON.stringify(metadata.toolCallId)} used tool name ${JSON.stringify(idMatch.name)} instead of ${JSON.stringify(metadata.toolName)}.`,
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
assistantIndex = index;
|
|
181
|
-
matchingAssistantToolCalls = calls;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (structuredOutputCallCount > 1) {
|
|
185
|
-
return finalityInvalid(
|
|
186
|
-
`Captured structured output call ${JSON.stringify(metadata.toolCallId)} was not exactly once; another ${metadata.toolName} tool call appeared in the child transcript.`,
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
if (assistantIndex === -1) {
|
|
190
|
-
return finalityInvalid(
|
|
191
|
-
`No assistant tool call matched captured structured output toolCallId ${JSON.stringify(metadata.toolCallId)}.`,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
if (matchingAssistantToolCalls.length !== 1) {
|
|
195
|
-
return finalityInvalid(
|
|
196
|
-
`Captured structured output call ${JSON.stringify(metadata.toolCallId)} was accompanied by sibling tool calls in the same assistant message.`,
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
let resultIndex = -1;
|
|
201
|
-
let resultMessage: StructuredOutputTranscriptMessage | undefined;
|
|
202
|
-
for (let index = assistantIndex + 1; index < messages.length; index++) {
|
|
203
|
-
const message = messages[index];
|
|
204
|
-
if (roleOf(message) !== "toolResult") continue;
|
|
205
|
-
if (message.toolCallId !== metadata.toolCallId) continue;
|
|
206
|
-
resultIndex = index;
|
|
207
|
-
resultMessage = message;
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (!resultMessage) {
|
|
212
|
-
return finalityInvalid(
|
|
213
|
-
`No tool result matched captured structured output toolCallId ${JSON.stringify(metadata.toolCallId)}.`,
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
if (resultMessage.toolName !== metadata.toolName) {
|
|
217
|
-
return finalityInvalid(
|
|
218
|
-
`Structured output tool result for ${JSON.stringify(metadata.toolCallId)} used tool name ${JSON.stringify(resultMessage.toolName)} instead of ${JSON.stringify(metadata.toolName)}.`,
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
if (resultMessage.isError !== false) {
|
|
222
|
-
return finalityInvalid(
|
|
223
|
-
`Structured output tool result for ${JSON.stringify(metadata.toolCallId)} was an error or did not prove success.`,
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
for (let index = assistantIndex + 1; index < resultIndex; index++) {
|
|
228
|
-
const message = messages[index];
|
|
229
|
-
if (isFinalityRelevantMessage(message)) {
|
|
230
|
-
return finalityInvalid(
|
|
231
|
-
`Structured output call ${JSON.stringify(metadata.toolCallId)} was not final; another ${roleOf(message)} message appeared before its matching tool result.`,
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
for (let index = resultIndex + 1; index < messages.length; index++) {
|
|
236
|
-
const message = messages[index];
|
|
237
|
-
if (isFinalityRelevantMessage(message)) {
|
|
238
|
-
return finalityInvalid(
|
|
239
|
-
`Structured output call ${JSON.stringify(metadata.toolCallId)} was not final; a later ${roleOf(message)} message followed the successful tool result.`,
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return { status: "valid" };
|
|
245
16
|
}
|
|
246
17
|
|
|
247
18
|
export function assertJsonSchemaDescriptor(schema: unknown, label = "outputSchema"): asserts schema is JsonSchemaObject {
|
|
@@ -250,84 +21,44 @@ export function assertJsonSchemaDescriptor(schema: unknown, label = "outputSchem
|
|
|
250
21
|
}
|
|
251
22
|
}
|
|
252
23
|
|
|
253
|
-
export function assertStructuredOutputParameterSchema(schema: unknown, label = "outputSchema"): asserts schema is JsonSchemaObject {
|
|
254
|
-
assertJsonSchemaDescriptor(schema, label);
|
|
255
|
-
if (!isTopLevelObjectOutputSchema(schema)) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`${label} must be a top-level object tool-argument schema. `
|
|
258
|
-
+ "Wrap array or primitive outputs in an object field, for example `{ items: [...] }` or `{ value: ... }`.",
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
24
|
export function createStructuredOutputRuntime(schema: JsonSchemaObject, baseDir?: string): StructuredOutputRuntime {
|
|
264
|
-
assertStructuredOutputParameterSchema(schema);
|
|
265
25
|
const rootDir = baseDir ?? os.tmpdir();
|
|
266
26
|
fs.mkdirSync(rootDir, { recursive: true });
|
|
267
27
|
const dir = fs.mkdtempSync(path.join(rootDir, "pi-subagent-structured-"));
|
|
268
28
|
const schemaPath = path.join(dir, "schema.json");
|
|
269
29
|
const outputPath = path.join(dir, "output.json");
|
|
270
|
-
const metadataPath = path.join(dir, "output.meta.json");
|
|
271
30
|
fs.writeFileSync(schemaPath, JSON.stringify(schema), { mode: 0o600 });
|
|
272
|
-
return { schema, schemaPath, outputPath
|
|
31
|
+
return { schema, schemaPath, outputPath };
|
|
273
32
|
}
|
|
274
33
|
|
|
275
34
|
export function validateStructuredOutputValue(schema: JsonSchemaObject, value: unknown): { status: "valid" } | { status: "invalid"; message: string } {
|
|
276
|
-
let validator: CompiledJsonSchema;
|
|
277
35
|
try {
|
|
278
|
-
validator = (Compile as (schema: unknown) =>
|
|
36
|
+
const validator = (Compile as (schema: unknown) => {
|
|
37
|
+
Check(value: unknown): boolean;
|
|
38
|
+
Errors(value: unknown): Iterable<{ instancePath?: string; message?: string }>;
|
|
39
|
+
})(schema);
|
|
40
|
+
if (validator.Check(value)) return { status: "valid" };
|
|
41
|
+
const errors = [...validator.Errors(value)]
|
|
42
|
+
.slice(0, 8)
|
|
43
|
+
.map((error) => {
|
|
44
|
+
const pathText = error.instancePath ? error.instancePath.replace(/^\//, "").replace(/\//g, ".") : "root";
|
|
45
|
+
return `${pathText}: ${error.message}`;
|
|
46
|
+
});
|
|
47
|
+
return { status: "invalid", message: errors.join("; ") || "schema validation failed" };
|
|
279
48
|
} catch (error) {
|
|
280
49
|
return { status: "invalid", message: `invalid outputSchema: ${error instanceof Error ? error.message : String(error)}` };
|
|
281
50
|
}
|
|
282
|
-
if (validator.Check(value)) return { status: "valid" };
|
|
283
|
-
const errors = [...validator.Errors(value)]
|
|
284
|
-
.slice(0, 8)
|
|
285
|
-
.map((error) => {
|
|
286
|
-
const pathText = error.instancePath ? error.instancePath.replace(/^\//, "").replace(/\//g, ".") : "root";
|
|
287
|
-
return `${pathText}: ${error.message}`;
|
|
288
|
-
});
|
|
289
|
-
return { status: "invalid", message: errors.join("; ") || "schema validation failed" };
|
|
290
51
|
}
|
|
291
52
|
|
|
292
|
-
export function readStructuredOutput(
|
|
293
|
-
runtime: StructuredOutputRuntime,
|
|
294
|
-
options: ReadStructuredOutputOptions = {},
|
|
295
|
-
): { value?: unknown; error?: string } {
|
|
53
|
+
export function readStructuredOutput(runtime: StructuredOutputRuntime): { value?: unknown; error?: string } {
|
|
296
54
|
if (!fs.existsSync(runtime.outputPath)) {
|
|
297
55
|
return { error: "Missing structured_output call; this step has outputSchema and must finish by calling structured_output." };
|
|
298
56
|
}
|
|
299
|
-
const metadataPath = runtime.metadataPath ?? getStructuredOutputMetadataPath(runtime.outputPath);
|
|
300
|
-
if (!fs.existsSync(metadataPath)) {
|
|
301
|
-
return { error: "Missing structured_output metadata sidecar; this step must finish with a verified structured_output call." };
|
|
302
|
-
}
|
|
303
|
-
let payload: JsonValue;
|
|
304
57
|
try {
|
|
305
|
-
|
|
58
|
+
return { value: JSON.parse(fs.readFileSync(runtime.outputPath, "utf-8")) as unknown };
|
|
306
59
|
} catch (error) {
|
|
307
60
|
return { error: `Failed to read structured output: ${error instanceof Error ? error.message : String(error)}` };
|
|
308
61
|
}
|
|
309
|
-
let rawMetadata: JsonValue;
|
|
310
|
-
try {
|
|
311
|
-
rawMetadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8")) as JsonValue;
|
|
312
|
-
} catch (error) {
|
|
313
|
-
return { error: `Failed to read structured output metadata: ${error instanceof Error ? error.message : String(error)}` };
|
|
314
|
-
}
|
|
315
|
-
const parsed = parseCaptureMetadata(rawMetadata);
|
|
316
|
-
if (parsed.error || !parsed.metadata) {
|
|
317
|
-
return { error: parsed.error ?? "Structured output metadata sidecar is invalid." };
|
|
318
|
-
}
|
|
319
|
-
const validation = validateStructuredOutputValue(runtime.schema, payload);
|
|
320
|
-
if (validation.status === "invalid") return { error: `Structured output validation failed: ${validation.message}` };
|
|
321
|
-
const expectedToolName = options.toolName ?? STRUCTURED_OUTPUT_TOOL_NAME;
|
|
322
|
-
if (options.messages) {
|
|
323
|
-
const finality = verifyStructuredOutputFinality(options.messages, parsed.metadata, expectedToolName);
|
|
324
|
-
if (finality.status === "invalid") return { error: finality.message };
|
|
325
|
-
} else if (parsed.metadata.toolName !== expectedToolName) {
|
|
326
|
-
return {
|
|
327
|
-
error: `Captured structured output tool name ${JSON.stringify(parsed.metadata.toolName)} did not match expected ${JSON.stringify(expectedToolName)}.`,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
return { value: payload };
|
|
331
62
|
}
|
|
332
63
|
|
|
333
64
|
export function cleanupStructuredOutputRuntime(runtime: StructuredOutputRuntime | undefined): void {
|