@armadra/agent 0.0.0-stage → 0.3.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 +37 -0
- package/LICENSE +21 -0
- package/README.md +470 -2
- package/dist/agent/agent.d.ts +56 -0
- package/dist/agent/agent.js +129 -0
- package/dist/agent/loop.d.ts +51 -0
- package/dist/agent/loop.js +158 -0
- package/dist/agent/queue.d.ts +25 -0
- package/dist/agent/queue.js +55 -0
- package/dist/agent/retry.d.ts +22 -0
- package/dist/agent/retry.js +92 -0
- package/dist/agent/schema.d.ts +27 -0
- package/dist/agent/schema.js +186 -0
- package/dist/agent/session-cache.d.ts +100 -0
- package/dist/agent/session-cache.js +514 -0
- package/dist/agent/session-compaction.d.ts +49 -0
- package/dist/agent/session-compaction.js +251 -0
- package/dist/agent/session-core.d.ts +103 -0
- package/dist/agent/session-core.js +7 -0
- package/dist/agent/session-run.d.ts +55 -0
- package/dist/agent/session-run.js +246 -0
- package/dist/agent/session-state.d.ts +39 -0
- package/dist/agent/session-state.js +119 -0
- package/dist/agent/session-subagent.d.ts +54 -0
- package/dist/agent/session-subagent.js +157 -0
- package/dist/agent/session-sync.d.ts +20 -0
- package/dist/agent/session-sync.js +111 -0
- package/dist/agent/session-tools.d.ts +24 -0
- package/dist/agent/session-tools.js +277 -0
- package/dist/agent/session.d.ts +117 -0
- package/dist/agent/session.js +485 -0
- package/dist/agent/system-prompt.d.ts +49 -0
- package/dist/agent/system-prompt.js +121 -0
- package/dist/agent/tool-runner.d.ts +64 -0
- package/dist/agent/tool-runner.js +309 -0
- package/dist/agent/transform.d.ts +26 -0
- package/dist/agent/transform.js +171 -0
- package/dist/agent/types.d.ts +378 -0
- package/dist/agent/types.js +21 -0
- package/dist/ai/apis/anthropic-messages.d.ts +26 -0
- package/dist/ai/apis/anthropic-messages.js +214 -0
- package/dist/ai/apis/anthropic-request.d.ts +42 -0
- package/dist/ai/apis/anthropic-request.js +282 -0
- package/dist/ai/apis/api.d.ts +28 -0
- package/dist/ai/apis/api.js +102 -0
- package/dist/ai/apis/cache-params.d.ts +48 -0
- package/dist/ai/apis/cache-params.js +163 -0
- package/dist/ai/apis/google-generative-ai.d.ts +32 -0
- package/dist/ai/apis/google-generative-ai.js +249 -0
- package/dist/ai/apis/google-request.d.ts +40 -0
- package/dist/ai/apis/google-request.js +255 -0
- package/dist/ai/apis/openai-compat.d.ts +35 -0
- package/dist/ai/apis/openai-compat.js +142 -0
- package/dist/ai/apis/openai-completions.d.ts +28 -0
- package/dist/ai/apis/openai-completions.js +246 -0
- package/dist/ai/apis/openai-request.d.ts +35 -0
- package/dist/ai/apis/openai-request.js +303 -0
- package/dist/ai/apis/openai-responses-request.d.ts +52 -0
- package/dist/ai/apis/openai-responses-request.js +266 -0
- package/dist/ai/apis/openai-responses.d.ts +28 -0
- package/dist/ai/apis/openai-responses.js +304 -0
- package/dist/ai/apis/shared.d.ts +56 -0
- package/dist/ai/apis/shared.js +179 -0
- package/dist/ai/cache/economics.d.ts +24 -0
- package/dist/ai/cache/economics.js +69 -0
- package/dist/ai/cache/fingerprint.d.ts +17 -0
- package/dist/ai/cache/fingerprint.js +39 -0
- package/dist/ai/cache/miss.d.ts +49 -0
- package/dist/ai/cache/miss.js +96 -0
- package/dist/ai/cache/reporting.d.ts +39 -0
- package/dist/ai/cache/reporting.js +103 -0
- package/dist/ai/cache/types.d.ts +94 -0
- package/dist/ai/cache/types.js +19 -0
- package/dist/ai/cache/warmer.d.ts +94 -0
- package/dist/ai/cache/warmer.js +224 -0
- package/dist/ai/context.d.ts +35 -0
- package/dist/ai/context.js +114 -0
- package/dist/ai/cost.d.ts +21 -0
- package/dist/ai/cost.js +60 -0
- package/dist/ai/event-stream.d.ts +33 -0
- package/dist/ai/event-stream.js +101 -0
- package/dist/ai/fake/fake-provider.d.ts +68 -0
- package/dist/ai/fake/fake-provider.js +278 -0
- package/dist/ai/fake/fake-script.d.ts +69 -0
- package/dist/ai/fake/fake-script.js +142 -0
- package/dist/ai/http.d.ts +54 -0
- package/dist/ai/http.js +212 -0
- package/dist/ai/json-partial.d.ts +17 -0
- package/dist/ai/json-partial.js +243 -0
- package/dist/ai/overflow.d.ts +22 -0
- package/dist/ai/overflow.js +61 -0
- package/dist/ai/providers/auth.d.ts +71 -0
- package/dist/ai/providers/auth.js +202 -0
- package/dist/ai/providers/builtin.d.ts +23 -0
- package/dist/ai/providers/builtin.js +148 -0
- package/dist/ai/providers/catalog-data.d.ts +5 -0
- package/dist/ai/providers/catalog-data.js +19 -0
- package/dist/ai/providers/catalog.d.ts +28 -0
- package/dist/ai/providers/catalog.js +166 -0
- package/dist/ai/providers/channels.d.ts +51 -0
- package/dist/ai/providers/channels.js +96 -0
- package/dist/ai/providers/enrich.d.ts +34 -0
- package/dist/ai/providers/enrich.js +86 -0
- package/dist/ai/providers/models-dev-cache.d.ts +53 -0
- package/dist/ai/providers/models-dev-cache.js +147 -0
- package/dist/ai/providers/models-dev.d.ts +99 -0
- package/dist/ai/providers/models-dev.js +315 -0
- package/dist/ai/providers/registry.d.ts +114 -0
- package/dist/ai/providers/registry.js +458 -0
- package/dist/ai/sse.d.ts +32 -0
- package/dist/ai/sse.js +140 -0
- package/dist/ai/thinking.d.ts +42 -0
- package/dist/ai/thinking.js +101 -0
- package/dist/ai/types.d.ts +448 -0
- package/dist/ai/types.js +19 -0
- package/dist/bundle/ama-sandbox.cjs +324 -0
- package/dist/bundle/ama.cjs +27837 -0
- package/dist/bundle.d.ts +9 -0
- package/dist/bundle.js +15 -0
- package/dist/cli/args.d.ts +85 -0
- package/dist/cli/args.js +408 -0
- package/dist/cli/bootstrap.d.ts +18 -0
- package/dist/cli/bootstrap.js +399 -0
- package/dist/cli/compose-providers.d.ts +37 -0
- package/dist/cli/compose-providers.js +107 -0
- package/dist/cli/compose-session.d.ts +72 -0
- package/dist/cli/compose-session.js +370 -0
- package/dist/cli/compose-store.d.ts +24 -0
- package/dist/cli/compose-store.js +125 -0
- package/dist/cli/compose.d.ts +108 -0
- package/dist/cli/compose.js +221 -0
- package/dist/cli/default-model.d.ts +16 -0
- package/dist/cli/default-model.js +24 -0
- package/dist/cli/deps.d.ts +207 -0
- package/dist/cli/deps.js +12 -0
- package/dist/cli/exit-codes.d.ts +29 -0
- package/dist/cli/exit-codes.js +40 -0
- package/dist/cli/main.d.ts +27 -0
- package/dist/cli/main.js +177 -0
- package/dist/cli/runtime.d.ts +90 -0
- package/dist/cli/runtime.js +8 -0
- package/dist/cli/startup-screen.d.ts +15 -0
- package/dist/cli/startup-screen.js +56 -0
- package/dist/cli/startup-steps.d.ts +35 -0
- package/dist/cli/startup-steps.js +202 -0
- package/dist/cli/subcommands/auth.d.ts +12 -0
- package/dist/cli/subcommands/auth.js +91 -0
- package/dist/cli/subcommands/config.d.ts +53 -0
- package/dist/cli/subcommands/config.js +277 -0
- package/dist/cli/subcommands/context.d.ts +21 -0
- package/dist/cli/subcommands/context.js +42 -0
- package/dist/cli/subcommands/doctor.d.ts +11 -0
- package/dist/cli/subcommands/doctor.js +246 -0
- package/dist/cli/subcommands/init.d.ts +6 -0
- package/dist/cli/subcommands/init.js +22 -0
- package/dist/cli/subcommands/model-meta.d.ts +16 -0
- package/dist/cli/subcommands/model-meta.js +54 -0
- package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
- package/dist/cli/subcommands/models-cache-probe.js +190 -0
- package/dist/cli/subcommands/models-discover.d.ts +46 -0
- package/dist/cli/subcommands/models-discover.js +232 -0
- package/dist/cli/subcommands/models.d.ts +40 -0
- package/dist/cli/subcommands/models.js +152 -0
- package/dist/cli/subcommands/providers-list.d.ts +8 -0
- package/dist/cli/subcommands/providers-list.js +97 -0
- package/dist/cli/subcommands/providers-plan.d.ts +79 -0
- package/dist/cli/subcommands/providers-plan.js +215 -0
- package/dist/cli/subcommands/providers.d.ts +27 -0
- package/dist/cli/subcommands/providers.js +433 -0
- package/dist/cli/subcommands/sessions.d.ts +11 -0
- package/dist/cli/subcommands/sessions.js +100 -0
- package/dist/codemode/capability.d.ts +37 -0
- package/dist/codemode/capability.js +55 -0
- package/dist/codemode/declarations.d.ts +44 -0
- package/dist/codemode/declarations.js +111 -0
- package/dist/codemode/host-side.d.ts +59 -0
- package/dist/codemode/host-side.js +253 -0
- package/dist/codemode/modes.d.ts +23 -0
- package/dist/codemode/modes.js +40 -0
- package/dist/codemode/protocol.d.ts +91 -0
- package/dist/codemode/protocol.js +114 -0
- package/dist/codemode/sandbox-entry.d.ts +25 -0
- package/dist/codemode/sandbox-entry.js +323 -0
- package/dist/codemode/store.d.ts +26 -0
- package/dist/codemode/store.js +53 -0
- package/dist/codemode/tool.d.ts +105 -0
- package/dist/codemode/tool.js +283 -0
- package/dist/compaction/branch-summary.d.ts +30 -0
- package/dist/compaction/branch-summary.js +79 -0
- package/dist/compaction/breaker.d.ts +32 -0
- package/dist/compaction/breaker.js +65 -0
- package/dist/compaction/cut-point.d.ts +23 -0
- package/dist/compaction/cut-point.js +70 -0
- package/dist/compaction/estimate.d.ts +30 -0
- package/dist/compaction/estimate.js +114 -0
- package/dist/compaction/prune-tier.d.ts +31 -0
- package/dist/compaction/prune-tier.js +83 -0
- package/dist/compaction/serialize.d.ts +20 -0
- package/dist/compaction/serialize.js +115 -0
- package/dist/compaction/summarize-tier.d.ts +111 -0
- package/dist/compaction/summarize-tier.js +305 -0
- package/dist/config/auth-file.d.ts +39 -0
- package/dist/config/auth-file.js +92 -0
- package/dist/config/context-files.d.ts +30 -0
- package/dist/config/context-files.js +77 -0
- package/dist/config/init.d.ts +30 -0
- package/dist/config/init.js +92 -0
- package/dist/config/json-schema.d.ts +14 -0
- package/dist/config/json-schema.js +182 -0
- package/dist/config/load.d.ts +45 -0
- package/dist/config/load.js +97 -0
- package/dist/config/merge.d.ts +73 -0
- package/dist/config/merge.js +248 -0
- package/dist/config/paths.d.ts +38 -0
- package/dist/config/paths.js +108 -0
- package/dist/config/profile.d.ts +27 -0
- package/dist/config/profile.js +65 -0
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.js +476 -0
- package/dist/config/trust.d.ts +42 -0
- package/dist/config/trust.js +122 -0
- package/dist/config/types.d.ts +190 -0
- package/dist/config/types.js +29 -0
- package/dist/config/write.d.ts +10 -0
- package/dist/config/write.js +25 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.js +30 -0
- package/dist/hooks/config.d.ts +49 -0
- package/dist/hooks/config.js +102 -0
- package/dist/hooks/dispatcher.d.ts +41 -0
- package/dist/hooks/dispatcher.js +104 -0
- package/dist/hooks/matcher.d.ts +22 -0
- package/dist/hooks/matcher.js +108 -0
- package/dist/hooks/protocol.d.ts +33 -0
- package/dist/hooks/protocol.js +240 -0
- package/dist/hooks/runner.d.ts +30 -0
- package/dist/hooks/runner.js +143 -0
- package/dist/hooks/types.d.ts +147 -0
- package/dist/hooks/types.js +20 -0
- package/dist/host/api-impl.d.ts +75 -0
- package/dist/host/api-impl.js +173 -0
- package/dist/host/loader.d.ts +31 -0
- package/dist/host/loader.js +147 -0
- package/dist/host/types.d.ts +169 -0
- package/dist/host/types.js +14 -0
- package/dist/host.d.ts +5 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +28 -0
- package/dist/modes/commands-core.d.ts +44 -0
- package/dist/modes/commands-core.js +171 -0
- package/dist/modes/image-input.d.ts +27 -0
- package/dist/modes/image-input.js +78 -0
- package/dist/modes/interactive/approval-dialog.d.ts +48 -0
- package/dist/modes/interactive/approval-dialog.js +202 -0
- package/dist/modes/interactive/commands.d.ts +41 -0
- package/dist/modes/interactive/commands.js +201 -0
- package/dist/modes/interactive/completion.d.ts +53 -0
- package/dist/modes/interactive/completion.js +157 -0
- package/dist/modes/interactive/interactive-mode.d.ts +46 -0
- package/dist/modes/interactive/interactive-mode.js +491 -0
- package/dist/modes/interactive/key-dispatch.d.ts +29 -0
- package/dist/modes/interactive/key-dispatch.js +107 -0
- package/dist/modes/interactive/line/line-editor.d.ts +50 -0
- package/dist/modes/interactive/line/line-editor.js +232 -0
- package/dist/modes/interactive/line/line-mode.d.ts +20 -0
- package/dist/modes/interactive/line/line-mode.js +218 -0
- package/dist/modes/interactive/line/line-render.d.ts +35 -0
- package/dist/modes/interactive/line/line-render.js +171 -0
- package/dist/modes/interactive/line/paste-state.d.ts +26 -0
- package/dist/modes/interactive/line/paste-state.js +72 -0
- package/dist/modes/interactive/message-view.d.ts +77 -0
- package/dist/modes/interactive/message-view.js +269 -0
- package/dist/modes/interactive/pickers.d.ts +42 -0
- package/dist/modes/interactive/pickers.js +115 -0
- package/dist/modes/interactive/startup-ui.d.ts +33 -0
- package/dist/modes/interactive/startup-ui.js +268 -0
- package/dist/modes/interactive/status-bar.d.ts +48 -0
- package/dist/modes/interactive/status-bar.js +139 -0
- package/dist/modes/interactive/tool-view.d.ts +95 -0
- package/dist/modes/interactive/tool-view.js +344 -0
- package/dist/modes/print/json-event.d.ts +15 -0
- package/dist/modes/print/json-event.js +39 -0
- package/dist/modes/print/print-mode.d.ts +17 -0
- package/dist/modes/print/print-mode.js +107 -0
- package/dist/modes/rpc/commands.d.ts +40 -0
- package/dist/modes/rpc/commands.js +263 -0
- package/dist/modes/rpc/jsonl.d.ts +16 -0
- package/dist/modes/rpc/jsonl.js +55 -0
- package/dist/modes/rpc/rpc-mode.d.ts +22 -0
- package/dist/modes/rpc/rpc-mode.js +153 -0
- package/dist/modes/session-report.d.ts +55 -0
- package/dist/modes/session-report.js +278 -0
- package/dist/modes/shared.d.ts +13 -0
- package/dist/modes/shared.js +52 -0
- package/dist/modes/startup-ui-text.d.ts +16 -0
- package/dist/modes/startup-ui-text.js +103 -0
- package/dist/permissions/broker.d.ts +47 -0
- package/dist/permissions/broker.js +107 -0
- package/dist/permissions/dangerous.d.ts +47 -0
- package/dist/permissions/dangerous.js +370 -0
- package/dist/permissions/pipeline.d.ts +51 -0
- package/dist/permissions/pipeline.js +181 -0
- package/dist/permissions/preview.d.ts +43 -0
- package/dist/permissions/preview.js +463 -0
- package/dist/permissions/rules.d.ts +58 -0
- package/dist/permissions/rules.js +249 -0
- package/dist/permissions/types.d.ts +100 -0
- package/dist/permissions/types.js +18 -0
- package/dist/rpc.d.ts +173 -0
- package/dist/rpc.js +13 -0
- package/dist/sdk.d.ts +98 -0
- package/dist/sdk.js +195 -0
- package/dist/session/manager.d.ts +70 -0
- package/dist/session/manager.js +278 -0
- package/dist/session/migrate.d.ts +19 -0
- package/dist/session/migrate.js +53 -0
- package/dist/session/projection.d.ts +50 -0
- package/dist/session/projection.js +184 -0
- package/dist/session/store.d.ts +47 -0
- package/dist/session/store.js +210 -0
- package/dist/session/tree.d.ts +21 -0
- package/dist/session/tree.js +88 -0
- package/dist/session/types.d.ts +201 -0
- package/dist/session/types.js +18 -0
- package/dist/skills/discover.d.ts +60 -0
- package/dist/skills/discover.js +160 -0
- package/dist/skills/expand.d.ts +35 -0
- package/dist/skills/expand.js +46 -0
- package/dist/skills/frontmatter.d.ts +27 -0
- package/dist/skills/frontmatter.js +157 -0
- package/dist/skills/index-prompt.d.ts +13 -0
- package/dist/skills/index-prompt.js +42 -0
- package/dist/skills/templates.d.ts +52 -0
- package/dist/skills/templates.js +177 -0
- package/dist/tools/bash.d.ts +49 -0
- package/dist/tools/bash.js +222 -0
- package/dist/tools/edit-fuzzy.d.ts +36 -0
- package/dist/tools/edit-fuzzy.js +129 -0
- package/dist/tools/edit.d.ts +38 -0
- package/dist/tools/edit.js +241 -0
- package/dist/tools/file-mutex.d.ts +8 -0
- package/dist/tools/file-mutex.js +29 -0
- package/dist/tools/glob.d.ts +44 -0
- package/dist/tools/glob.js +217 -0
- package/dist/tools/grep.d.ts +39 -0
- package/dist/tools/grep.js +179 -0
- package/dist/tools/ignore.d.ts +40 -0
- package/dist/tools/ignore.js +174 -0
- package/dist/tools/image-file.d.ts +31 -0
- package/dist/tools/image-file.js +114 -0
- package/dist/tools/ls.d.ts +21 -0
- package/dist/tools/ls.js +96 -0
- package/dist/tools/output-accumulator.d.ts +48 -0
- package/dist/tools/output-accumulator.js +106 -0
- package/dist/tools/paths.d.ts +20 -0
- package/dist/tools/paths.js +58 -0
- package/dist/tools/presets.d.ts +71 -0
- package/dist/tools/presets.js +128 -0
- package/dist/tools/process-tree.d.ts +35 -0
- package/dist/tools/process-tree.js +125 -0
- package/dist/tools/read.d.ts +24 -0
- package/dist/tools/read.js +132 -0
- package/dist/tools/registry.d.ts +43 -0
- package/dist/tools/registry.js +106 -0
- package/dist/tools/shell.d.ts +38 -0
- package/dist/tools/shell.js +91 -0
- package/dist/tools/task.d.ts +29 -0
- package/dist/tools/task.js +107 -0
- package/dist/tools/todo.d.ts +28 -0
- package/dist/tools/todo.js +107 -0
- package/dist/tools/truncate.d.ts +44 -0
- package/dist/tools/truncate.js +175 -0
- package/dist/tools/types.d.ts +135 -0
- package/dist/tools/types.js +19 -0
- package/dist/tools/write.d.ts +20 -0
- package/dist/tools/write.js +84 -0
- package/dist/tui/ansi.d.ts +29 -0
- package/dist/tui/ansi.js +420 -0
- package/dist/tui/component.d.ts +38 -0
- package/dist/tui/component.js +13 -0
- package/dist/tui/components/box.d.ts +26 -0
- package/dist/tui/components/box.js +65 -0
- package/dist/tui/components/container.d.ts +14 -0
- package/dist/tui/components/container.js +39 -0
- package/dist/tui/components/editor-buffer.d.ts +67 -0
- package/dist/tui/components/editor-buffer.js +300 -0
- package/dist/tui/components/editor-paste.d.ts +29 -0
- package/dist/tui/components/editor-paste.js +64 -0
- package/dist/tui/components/editor.d.ts +114 -0
- package/dist/tui/components/editor.js +530 -0
- package/dist/tui/components/key-value.d.ts +29 -0
- package/dist/tui/components/key-value.js +48 -0
- package/dist/tui/components/loader.d.ts +35 -0
- package/dist/tui/components/loader.js +71 -0
- package/dist/tui/components/markdown.d.ts +90 -0
- package/dist/tui/components/markdown.js +462 -0
- package/dist/tui/components/meter.d.ts +29 -0
- package/dist/tui/components/meter.js +47 -0
- package/dist/tui/components/overlay.d.ts +24 -0
- package/dist/tui/components/overlay.js +52 -0
- package/dist/tui/components/select-list.d.ts +57 -0
- package/dist/tui/components/select-list.js +179 -0
- package/dist/tui/components/spacer.d.ts +11 -0
- package/dist/tui/components/spacer.js +16 -0
- package/dist/tui/components/text.d.ts +30 -0
- package/dist/tui/components/text.js +65 -0
- package/dist/tui/keybindings.d.ts +66 -0
- package/dist/tui/keybindings.js +120 -0
- package/dist/tui/keys.d.ts +38 -0
- package/dist/tui/keys.js +275 -0
- package/dist/tui/stdin-buffer.d.ts +41 -0
- package/dist/tui/stdin-buffer.js +206 -0
- package/dist/tui/terminal.d.ts +79 -0
- package/dist/tui/terminal.js +153 -0
- package/dist/tui/theme.d.ts +31 -0
- package/dist/tui/theme.js +199 -0
- package/dist/tui/tui.d.ts +103 -0
- package/dist/tui/tui.js +363 -0
- package/dist/tui/vt-screen.d.ts +46 -0
- package/dist/tui/vt-screen.js +251 -0
- package/dist/tui.d.ts +30 -0
- package/dist/tui.js +31 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.js +24 -0
- package/docs/codemode.md +102 -0
- package/docs/hooks.md +196 -0
- package/docs/host-api.md +161 -0
- package/docs/providers.md +531 -0
- package/docs/rpc.md +253 -0
- package/docs/session-format.md +119 -0
- package/docs/tui.md +170 -0
- package/package.json +101 -4
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具执行(设计 §4.4、§6.3)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 准备阶段**串行**:找工具 → JSON Schema 校验 → `beforeToolCall`(PreToolUse Hook → 权限管线 → broker,
|
|
5
|
+
* 由 AgentSession 注入)→ 若 gate 改了输入则重新校验 → abort 检查。
|
|
6
|
+
* 执行阶段**并行**;任一已准备的工具是 sequential(显式声明,或 write / execute 缺省)则整批串行。
|
|
7
|
+
* `tool_execution_end` 按完成顺序发;toolResult 消息按原序发 `message_start/end`(入转录)。
|
|
8
|
+
* `terminate` 要整批都为真才提前结束。`stopReason: "length"` 且有工具调用 → 整批判失败不执行。
|
|
9
|
+
* abort:未开始的调用直接给 `aborted by user` 错误结果;执行中的工具收到 signal,超过宽限期仍未结束
|
|
10
|
+
* 则不再等待、记 `aborted by user`。结果超 `maxToolResultChars` 截断并把全文写到 `outputDir`。
|
|
11
|
+
*
|
|
12
|
+
* 嵌套调用(`ToolContext.tools.executeTool`,codemode 脚本里的 `tools.*`)走 `runSingleToolCall`:
|
|
13
|
+
* 同一套校验与门禁,按**全部未禁用工具**查找(codemode only 模式下活动集只有 codemode),
|
|
14
|
+
* 发带 `parentToolCallId` 的 `tool_execution_start / update / end`(不入转录),门禁与 PostToolUse
|
|
15
|
+
* 拿到 `parent`(Hook 输入的 `viaCodemode / parentToolCallId`);结果给脚本而不是模型,截断上限放宽到
|
|
16
|
+
* `NESTED_MAX_RESULT_CHARS`。
|
|
17
|
+
*/
|
|
18
|
+
import type { AssistantMessage, ToolCallBlock, ToolResultMessage } from "../ai/types.js";
|
|
19
|
+
import type { NestedCallInfo, SessionEvent, ToolCallGate, ToolCallGateContext } from "./types.js";
|
|
20
|
+
import type { ToolContext, ToolDefinition, ToolResult } from "../tools/types.js";
|
|
21
|
+
export declare const ABORTED_TOOL_TEXT = "aborted by user";
|
|
22
|
+
export declare const DEFAULT_MAX_TOOL_RESULT_CHARS = 30000;
|
|
23
|
+
export declare const DEFAULT_ABORT_GRACE_MS = 3000;
|
|
24
|
+
/** 嵌套调用结果的截断上限(给脚本,不进上下文)。 */
|
|
25
|
+
export declare const NESTED_MAX_RESULT_CHARS: number;
|
|
26
|
+
export type LoopEmit = (event: SessionEvent) => Promise<void>;
|
|
27
|
+
/** 嵌套调用:由 `runSingleToolCall` 的调用方提供。 */
|
|
28
|
+
export interface NestedCall {
|
|
29
|
+
parent: NestedCallInfo;
|
|
30
|
+
/** 嵌套调用的 tool_execution_* 事件出口(事件已带 parentToolCallId)。 */
|
|
31
|
+
emit: LoopEmit;
|
|
32
|
+
}
|
|
33
|
+
export interface ToolRunnerOptions {
|
|
34
|
+
getTool(name: string): ToolDefinition | undefined;
|
|
35
|
+
beforeToolCall(call: ToolCallBlock, ctx: ToolCallGateContext): Promise<ToolCallGate>;
|
|
36
|
+
afterToolCall?(call: ToolCallBlock, result: ToolResult, ctx?: {
|
|
37
|
+
parent?: NestedCallInfo;
|
|
38
|
+
}): Promise<ToolResult>;
|
|
39
|
+
/** 嵌套调用的工具查找(全部未禁用工具);缺省同 getTool。 */
|
|
40
|
+
getNestedTool?(name: string): ToolDefinition | undefined;
|
|
41
|
+
createToolContext(call: ToolCallBlock, signal: AbortSignal, onUpdate: (partial: string) => void): ToolContext;
|
|
42
|
+
maxToolResultChars?: number;
|
|
43
|
+
outputDir?: string | undefined;
|
|
44
|
+
/** abort 后等待工具自行结束的上限。 */
|
|
45
|
+
abortGraceMs?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ToolBatchResult {
|
|
48
|
+
messages: ToolResultMessage[];
|
|
49
|
+
terminate: boolean;
|
|
50
|
+
}
|
|
51
|
+
export declare function errorResult(text: string): ToolResult;
|
|
52
|
+
export declare function toolResultMessage(call: ToolCallBlock, result: ToolResult): ToolResultMessage;
|
|
53
|
+
export declare function toolCallsOf(message: AssistantMessage): ToolCallBlock[];
|
|
54
|
+
export declare function runToolBatch(assistant: AssistantMessage, options: ToolRunnerOptions, signal: AbortSignal, emit: LoopEmit): Promise<ToolBatchResult>;
|
|
55
|
+
/** `length` 截断的助手消息:整批判失败,不执行。 */
|
|
56
|
+
export declare function failTruncatedBatch(assistant: AssistantMessage, emit: LoopEmit): Promise<ToolBatchResult>;
|
|
57
|
+
/** 被中断 / 出错的助手消息里的工具调用:各补一条错误结果(落盘不变式:每个 tool_call 恰有一个 result)。 */
|
|
58
|
+
export declare function closeDanglingCalls(assistant: AssistantMessage, emit: LoopEmit): Promise<ToolResultMessage[]>;
|
|
59
|
+
/**
|
|
60
|
+
* 单次调用(ToolContext.tools.executeTool 用):同样经过校验与 beforeToolCall / afterToolCall,
|
|
61
|
+
* 不入转录。给了 `nested` 时按全部工具查找、发带 `parentToolCallId` 的 tool_execution_* 事件、
|
|
62
|
+
* 门禁拿到 `parent`;不给则不发事件(与旧行为相同)。
|
|
63
|
+
*/
|
|
64
|
+
export declare function runSingleToolCall(call: ToolCallBlock, assistant: AssistantMessage, options: ToolRunnerOptions, signal: AbortSignal, nested?: NestedCall): Promise<ToolResult>;
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具执行(设计 §4.4、§6.3)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* 准备阶段**串行**:找工具 → JSON Schema 校验 → `beforeToolCall`(PreToolUse Hook → 权限管线 → broker,
|
|
5
|
+
* 由 AgentSession 注入)→ 若 gate 改了输入则重新校验 → abort 检查。
|
|
6
|
+
* 执行阶段**并行**;任一已准备的工具是 sequential(显式声明,或 write / execute 缺省)则整批串行。
|
|
7
|
+
* `tool_execution_end` 按完成顺序发;toolResult 消息按原序发 `message_start/end`(入转录)。
|
|
8
|
+
* `terminate` 要整批都为真才提前结束。`stopReason: "length"` 且有工具调用 → 整批判失败不执行。
|
|
9
|
+
* abort:未开始的调用直接给 `aborted by user` 错误结果;执行中的工具收到 signal,超过宽限期仍未结束
|
|
10
|
+
* 则不再等待、记 `aborted by user`。结果超 `maxToolResultChars` 截断并把全文写到 `outputDir`。
|
|
11
|
+
*
|
|
12
|
+
* 嵌套调用(`ToolContext.tools.executeTool`,codemode 脚本里的 `tools.*`)走 `runSingleToolCall`:
|
|
13
|
+
* 同一套校验与门禁,按**全部未禁用工具**查找(codemode only 模式下活动集只有 codemode),
|
|
14
|
+
* 发带 `parentToolCallId` 的 `tool_execution_start / update / end`(不入转录),门禁与 PostToolUse
|
|
15
|
+
* 拿到 `parent`(Hook 输入的 `viaCodemode / parentToolCallId`);结果给脚本而不是模型,截断上限放宽到
|
|
16
|
+
* `NESTED_MAX_RESULT_CHARS`。
|
|
17
|
+
*/
|
|
18
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { formatSchemaErrors, validateSchema } from "./schema.js";
|
|
21
|
+
import { CODEMODE_TOOL } from "../tools/presets.js";
|
|
22
|
+
import { executionModeOf } from "../tools/registry.js";
|
|
23
|
+
export const ABORTED_TOOL_TEXT = "aborted by user";
|
|
24
|
+
export const DEFAULT_MAX_TOOL_RESULT_CHARS = 30_000;
|
|
25
|
+
export const DEFAULT_ABORT_GRACE_MS = 3000;
|
|
26
|
+
/** 嵌套调用结果的截断上限(给脚本,不进上下文)。 */
|
|
27
|
+
export const NESTED_MAX_RESULT_CHARS = 1024 * 1024;
|
|
28
|
+
export function errorResult(text) {
|
|
29
|
+
return { content: text, isError: true };
|
|
30
|
+
}
|
|
31
|
+
export function toolResultMessage(call, result) {
|
|
32
|
+
const message = {
|
|
33
|
+
role: "toolResult",
|
|
34
|
+
toolCallId: call.id,
|
|
35
|
+
toolName: call.name,
|
|
36
|
+
content: result.content,
|
|
37
|
+
isError: result.isError === true,
|
|
38
|
+
timestamp: Date.now(),
|
|
39
|
+
};
|
|
40
|
+
if (result.details !== undefined)
|
|
41
|
+
message.details = result.details;
|
|
42
|
+
return message;
|
|
43
|
+
}
|
|
44
|
+
export function toolCallsOf(message) {
|
|
45
|
+
return message.content.filter((block) => block.type === "toolCall");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 找不到工具的错误文本。codemode `only` 下模型常直接调用 `read` 等(它们只在脚本里可用):
|
|
49
|
+
* 这时告诉模型正确写法。
|
|
50
|
+
*/
|
|
51
|
+
function notFoundText(name, options) {
|
|
52
|
+
if (options.getNestedTool?.(name) !== undefined && options.getTool(CODEMODE_TOOL) !== undefined) {
|
|
53
|
+
return `Tool ${name} is only callable inside a codemode script: tools.${name}({...})`;
|
|
54
|
+
}
|
|
55
|
+
return `Tool ${name} not found`;
|
|
56
|
+
}
|
|
57
|
+
async function prepare(call, assistant, options, signal, parent) {
|
|
58
|
+
if (signal.aborted)
|
|
59
|
+
return { kind: "immediate", call, result: errorResult(ABORTED_TOOL_TEXT) };
|
|
60
|
+
const tool = parent !== undefined && options.getNestedTool !== undefined
|
|
61
|
+
? options.getNestedTool(call.name)
|
|
62
|
+
: options.getTool(call.name);
|
|
63
|
+
if (tool === undefined) {
|
|
64
|
+
return { kind: "immediate", call, result: errorResult(notFoundText(call.name, options)) };
|
|
65
|
+
}
|
|
66
|
+
const invalid = (input) => {
|
|
67
|
+
const errors = validateSchema(tool.parameters, input);
|
|
68
|
+
if (errors.length === 0)
|
|
69
|
+
return undefined;
|
|
70
|
+
return {
|
|
71
|
+
kind: "immediate",
|
|
72
|
+
call,
|
|
73
|
+
result: errorResult(`Invalid arguments for ${call.name}:\n${formatSchemaErrors(errors)}`),
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const firstCheck = invalid(call.arguments);
|
|
77
|
+
if (firstCheck !== undefined)
|
|
78
|
+
return firstCheck;
|
|
79
|
+
let input = call.arguments;
|
|
80
|
+
try {
|
|
81
|
+
const gateContext = parent === undefined ? { signal, assistant, tool } : { signal, assistant, tool, parent };
|
|
82
|
+
const gate = await options.beforeToolCall(call, gateContext);
|
|
83
|
+
if (signal.aborted)
|
|
84
|
+
return { kind: "immediate", call, result: errorResult(ABORTED_TOOL_TEXT) };
|
|
85
|
+
if (gate.block === true) {
|
|
86
|
+
return {
|
|
87
|
+
kind: "immediate",
|
|
88
|
+
call,
|
|
89
|
+
result: errorResult(gate.reason ?? `Tool ${call.name} was blocked`),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (gate.input !== undefined) {
|
|
93
|
+
const recheck = invalid(gate.input);
|
|
94
|
+
if (recheck !== undefined)
|
|
95
|
+
return recheck;
|
|
96
|
+
input = gate.input;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
kind: "immediate",
|
|
102
|
+
call,
|
|
103
|
+
result: errorResult(signal.aborted ? ABORTED_TOOL_TEXT : `Tool call rejected: ${String(error)}`),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return { kind: "prepared", call, tool, input };
|
|
107
|
+
}
|
|
108
|
+
function resultText(content) {
|
|
109
|
+
if (typeof content === "string")
|
|
110
|
+
return content;
|
|
111
|
+
return content.map((block) => (block.type === "text" ? block.text : "")).join("");
|
|
112
|
+
}
|
|
113
|
+
function truncateResult(call, result, options, nested = false) {
|
|
114
|
+
const limit = nested
|
|
115
|
+
? NESTED_MAX_RESULT_CHARS
|
|
116
|
+
: (options.maxToolResultChars ?? DEFAULT_MAX_TOOL_RESULT_CHARS);
|
|
117
|
+
const text = resultText(result.content);
|
|
118
|
+
if (text.length <= limit)
|
|
119
|
+
return result;
|
|
120
|
+
let where = "全文未保存";
|
|
121
|
+
if (options.outputDir !== undefined) {
|
|
122
|
+
try {
|
|
123
|
+
mkdirSync(options.outputDir, { recursive: true });
|
|
124
|
+
const file = join(options.outputDir, `${call.id.replace(/[^A-Za-z0-9_-]/g, "_")}.txt`);
|
|
125
|
+
writeFileSync(file, text, "utf8");
|
|
126
|
+
where = `全文 ${file}`;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// 落盘失败只影响提示文字
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const images = typeof result.content === "string"
|
|
133
|
+
? []
|
|
134
|
+
: result.content.filter((block) => block.type === "image");
|
|
135
|
+
const head = `${text.slice(0, limit)}\n\n[输出过长已截断:共 ${text.length} 字符,${where}]`;
|
|
136
|
+
return {
|
|
137
|
+
...result,
|
|
138
|
+
content: images.length > 0 ? [{ type: "text", text: head }, ...images] : head,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async function execute(prepared, options, signal, emit, parent) {
|
|
142
|
+
const { call, tool, input } = prepared;
|
|
143
|
+
if (signal.aborted)
|
|
144
|
+
return errorResult(ABORTED_TOOL_TEXT);
|
|
145
|
+
const updates = [];
|
|
146
|
+
let accepting = true;
|
|
147
|
+
const onUpdate = (partial) => {
|
|
148
|
+
if (!accepting)
|
|
149
|
+
return;
|
|
150
|
+
updates.push(emit({ type: "tool_execution_update", toolCallId: call.id, toolName: call.name, partial }));
|
|
151
|
+
};
|
|
152
|
+
const grace = options.abortGraceMs ?? DEFAULT_ABORT_GRACE_MS;
|
|
153
|
+
let onAbort;
|
|
154
|
+
let graceTimer;
|
|
155
|
+
const abortRace = new Promise((resolve) => {
|
|
156
|
+
onAbort = () => {
|
|
157
|
+
graceTimer = setTimeout(() => resolve(errorResult(ABORTED_TOOL_TEXT)), grace);
|
|
158
|
+
};
|
|
159
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
160
|
+
});
|
|
161
|
+
let result;
|
|
162
|
+
try {
|
|
163
|
+
const ctx = options.createToolContext(call, signal, onUpdate);
|
|
164
|
+
const run = tool.execute(input, ctx).then((value) => value, (error) => errorResult(signal.aborted
|
|
165
|
+
? ABORTED_TOOL_TEXT
|
|
166
|
+
: error instanceof Error
|
|
167
|
+
? error.message
|
|
168
|
+
: String(error)));
|
|
169
|
+
result = await Promise.race([run, abortRace]);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
result = errorResult(error instanceof Error ? error.message : String(error));
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
accepting = false;
|
|
176
|
+
if (onAbort !== undefined)
|
|
177
|
+
signal.removeEventListener("abort", onAbort);
|
|
178
|
+
if (graceTimer !== undefined)
|
|
179
|
+
clearTimeout(graceTimer);
|
|
180
|
+
}
|
|
181
|
+
await Promise.all(updates);
|
|
182
|
+
if (options.afterToolCall !== undefined && !signal.aborted) {
|
|
183
|
+
try {
|
|
184
|
+
result =
|
|
185
|
+
parent === undefined
|
|
186
|
+
? await options.afterToolCall(call, result)
|
|
187
|
+
: await options.afterToolCall(call, result, { parent });
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
result = errorResult(`PostToolUse failed: ${String(error)}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return truncateResult(call, result, options, parent !== undefined);
|
|
194
|
+
}
|
|
195
|
+
async function emitEnd(call, result, emit) {
|
|
196
|
+
await emit({
|
|
197
|
+
type: "tool_execution_end",
|
|
198
|
+
toolCallId: call.id,
|
|
199
|
+
toolName: call.name,
|
|
200
|
+
result,
|
|
201
|
+
isError: result.isError === true,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
async function emitResultMessages(finalized, emit) {
|
|
205
|
+
const messages = [];
|
|
206
|
+
for (const { call, result } of finalized) {
|
|
207
|
+
const message = toolResultMessage(call, result);
|
|
208
|
+
await emit({ type: "message_start", message });
|
|
209
|
+
await emit({ type: "message_end", message });
|
|
210
|
+
messages.push(message);
|
|
211
|
+
}
|
|
212
|
+
return messages;
|
|
213
|
+
}
|
|
214
|
+
export async function runToolBatch(assistant, options, signal, emit) {
|
|
215
|
+
const calls = toolCallsOf(assistant);
|
|
216
|
+
const preparedList = [];
|
|
217
|
+
for (const call of calls) {
|
|
218
|
+
await emit({
|
|
219
|
+
type: "tool_execution_start",
|
|
220
|
+
toolCallId: call.id,
|
|
221
|
+
toolName: call.name,
|
|
222
|
+
args: call.arguments,
|
|
223
|
+
});
|
|
224
|
+
const prepared = await prepare(call, assistant, options, signal);
|
|
225
|
+
if (prepared.kind === "immediate")
|
|
226
|
+
await emitEnd(call, prepared.result, emit);
|
|
227
|
+
preparedList.push(prepared);
|
|
228
|
+
}
|
|
229
|
+
const sequential = preparedList.some((item) => item.kind === "prepared" && executionModeOf(item.tool) === "sequential");
|
|
230
|
+
const finalized = [];
|
|
231
|
+
if (sequential) {
|
|
232
|
+
for (const item of preparedList) {
|
|
233
|
+
if (item.kind === "immediate") {
|
|
234
|
+
finalized.push({ call: item.call, result: item.result });
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const result = await execute(item, options, signal, emit);
|
|
238
|
+
await emitEnd(item.call, result, emit);
|
|
239
|
+
finalized.push({ call: item.call, result });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
const results = await Promise.all(preparedList.map(async (item) => {
|
|
244
|
+
if (item.kind === "immediate")
|
|
245
|
+
return { call: item.call, result: item.result };
|
|
246
|
+
const result = await execute(item, options, signal, emit);
|
|
247
|
+
await emitEnd(item.call, result, emit);
|
|
248
|
+
return { call: item.call, result };
|
|
249
|
+
}));
|
|
250
|
+
finalized.push(...results);
|
|
251
|
+
}
|
|
252
|
+
const messages = await emitResultMessages(finalized, emit);
|
|
253
|
+
const terminate = finalized.length > 0 && finalized.every(({ result }) => result.terminate === true);
|
|
254
|
+
return { messages, terminate };
|
|
255
|
+
}
|
|
256
|
+
/** `length` 截断的助手消息:整批判失败,不执行。 */
|
|
257
|
+
export async function failTruncatedBatch(assistant, emit) {
|
|
258
|
+
const finalized = toolCallsOf(assistant).map((call) => ({
|
|
259
|
+
call,
|
|
260
|
+
result: errorResult(`Tool call "${call.name}" was not executed: the response hit the output token limit, so its arguments may be truncated. Re-issue the tool call with complete arguments.`),
|
|
261
|
+
}));
|
|
262
|
+
for (const { call, result } of finalized) {
|
|
263
|
+
await emit({
|
|
264
|
+
type: "tool_execution_start",
|
|
265
|
+
toolCallId: call.id,
|
|
266
|
+
toolName: call.name,
|
|
267
|
+
args: call.arguments,
|
|
268
|
+
});
|
|
269
|
+
await emitEnd(call, result, emit);
|
|
270
|
+
}
|
|
271
|
+
return { messages: await emitResultMessages(finalized, emit), terminate: false };
|
|
272
|
+
}
|
|
273
|
+
/** 被中断 / 出错的助手消息里的工具调用:各补一条错误结果(落盘不变式:每个 tool_call 恰有一个 result)。 */
|
|
274
|
+
export async function closeDanglingCalls(assistant, emit) {
|
|
275
|
+
const text = assistant.stopReason === "aborted" ? ABORTED_TOOL_TEXT : "not executed: the response failed";
|
|
276
|
+
const finalized = toolCallsOf(assistant).map((call) => ({ call, result: errorResult(text) }));
|
|
277
|
+
return emitResultMessages(finalized, emit);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* 单次调用(ToolContext.tools.executeTool 用):同样经过校验与 beforeToolCall / afterToolCall,
|
|
281
|
+
* 不入转录。给了 `nested` 时按全部工具查找、发带 `parentToolCallId` 的 tool_execution_* 事件、
|
|
282
|
+
* 门禁拿到 `parent`;不给则不发事件(与旧行为相同)。
|
|
283
|
+
*/
|
|
284
|
+
export async function runSingleToolCall(call, assistant, options, signal, nested) {
|
|
285
|
+
if (nested === undefined) {
|
|
286
|
+
const prepared = await prepare(call, assistant, options, signal);
|
|
287
|
+
if (prepared.kind === "immediate")
|
|
288
|
+
return prepared.result;
|
|
289
|
+
return execute(prepared, options, signal, async () => { });
|
|
290
|
+
}
|
|
291
|
+
const parentToolCallId = nested.parent.toolCallId;
|
|
292
|
+
const emit = (event) => nested.emit(event.type === "tool_execution_start" ||
|
|
293
|
+
event.type === "tool_execution_update" ||
|
|
294
|
+
event.type === "tool_execution_end"
|
|
295
|
+
? { ...event, parentToolCallId }
|
|
296
|
+
: event);
|
|
297
|
+
await emit({
|
|
298
|
+
type: "tool_execution_start",
|
|
299
|
+
toolCallId: call.id,
|
|
300
|
+
toolName: call.name,
|
|
301
|
+
args: call.arguments,
|
|
302
|
+
});
|
|
303
|
+
const prepared = await prepare(call, assistant, options, signal, nested.parent);
|
|
304
|
+
const result = prepared.kind === "immediate"
|
|
305
|
+
? prepared.result
|
|
306
|
+
: await execute(prepared, options, signal, emit, nested.parent);
|
|
307
|
+
await emitEnd(call, result, emit);
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上下文转换与回放修复(设计 §4.1 convertToLlm、§4.2「回放时 transform.ts 再兜底修复旧文件」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* `convertToLlm(messages, target)`:AgentMessage → Message,三种扩展角色转成 user 消息,再
|
|
5
|
+
* `repairTranscript`。**不得抛错**。
|
|
6
|
+
*
|
|
7
|
+
* `repairTranscript(messages, target)`:
|
|
8
|
+
* 1. 跳过 `stopReason` 为 error / aborted 的助手消息(失败尝试与被中断的半条回复不回放);
|
|
9
|
+
* 2. tool id 归一化:只留 `[A-Za-z0-9_-]`、≤ 64 字符,同一原 id 映射一致且不撞车;
|
|
10
|
+
* 3. 跨模型思考块降级:来自其它 provider / model 的 thinking 转成普通文本(redacted 丢弃),
|
|
11
|
+
* 并去掉签名类字段;同模型原样保留;
|
|
12
|
+
* 4. 孤儿 tool_call(没有结果)补一条错误结果,紧跟在该助手消息已有结果之后;
|
|
13
|
+
* 5. 孤儿 toolResult(前面没有对应调用)丢弃。
|
|
14
|
+
*/
|
|
15
|
+
import type { Message } from "../ai/types.js";
|
|
16
|
+
import type { AgentMessage } from "../session/types.js";
|
|
17
|
+
export interface TransformTarget {
|
|
18
|
+
provider: string;
|
|
19
|
+
model: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const ORPHAN_TOOL_RESULT_TEXT = "No result was recorded for this tool call (interrupted).";
|
|
22
|
+
export declare function compactionSummaryText(summary: string): string;
|
|
23
|
+
export declare function branchSummaryText(summary: string): string;
|
|
24
|
+
export declare function repairTranscript(messages: readonly Message[], target?: TransformTarget): Message[];
|
|
25
|
+
/** AgentMessage[] → Message[](扩展角色转 user)+ 回放修复。不抛错。 */
|
|
26
|
+
export declare function convertToLlm(messages: readonly AgentMessage[], target?: TransformTarget): Message[];
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上下文转换与回放修复(设计 §4.1 convertToLlm、§4.2「回放时 transform.ts 再兜底修复旧文件」)。[B2]
|
|
3
|
+
*
|
|
4
|
+
* `convertToLlm(messages, target)`:AgentMessage → Message,三种扩展角色转成 user 消息,再
|
|
5
|
+
* `repairTranscript`。**不得抛错**。
|
|
6
|
+
*
|
|
7
|
+
* `repairTranscript(messages, target)`:
|
|
8
|
+
* 1. 跳过 `stopReason` 为 error / aborted 的助手消息(失败尝试与被中断的半条回复不回放);
|
|
9
|
+
* 2. tool id 归一化:只留 `[A-Za-z0-9_-]`、≤ 64 字符,同一原 id 映射一致且不撞车;
|
|
10
|
+
* 3. 跨模型思考块降级:来自其它 provider / model 的 thinking 转成普通文本(redacted 丢弃),
|
|
11
|
+
* 并去掉签名类字段;同模型原样保留;
|
|
12
|
+
* 4. 孤儿 tool_call(没有结果)补一条错误结果,紧跟在该助手消息已有结果之后;
|
|
13
|
+
* 5. 孤儿 toolResult(前面没有对应调用)丢弃。
|
|
14
|
+
*/
|
|
15
|
+
export const ORPHAN_TOOL_RESULT_TEXT = "No result was recorded for this tool call (interrupted).";
|
|
16
|
+
const TOOL_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
17
|
+
export function compactionSummaryText(summary) {
|
|
18
|
+
return `The conversation history before this point was compacted into the following summary:\n\n<summary>\n${summary}\n</summary>`;
|
|
19
|
+
}
|
|
20
|
+
export function branchSummaryText(summary) {
|
|
21
|
+
return `The following is a summary of a branch of this conversation that was left to continue from an earlier point:\n\n<summary>\n${summary}\n</summary>`;
|
|
22
|
+
}
|
|
23
|
+
function toUserMessage(message) {
|
|
24
|
+
switch (message.role) {
|
|
25
|
+
case "system":
|
|
26
|
+
case "user":
|
|
27
|
+
case "assistant":
|
|
28
|
+
case "toolResult":
|
|
29
|
+
return message;
|
|
30
|
+
case "custom": {
|
|
31
|
+
const user = {
|
|
32
|
+
role: "user",
|
|
33
|
+
content: message.content,
|
|
34
|
+
timestamp: message.timestamp,
|
|
35
|
+
};
|
|
36
|
+
return user;
|
|
37
|
+
}
|
|
38
|
+
case "compactionSummary":
|
|
39
|
+
return {
|
|
40
|
+
role: "user",
|
|
41
|
+
content: compactionSummaryText(message.summary),
|
|
42
|
+
timestamp: message.timestamp,
|
|
43
|
+
};
|
|
44
|
+
case "branchSummary":
|
|
45
|
+
return {
|
|
46
|
+
role: "user",
|
|
47
|
+
content: branchSummaryText(message.summary),
|
|
48
|
+
timestamp: message.timestamp,
|
|
49
|
+
};
|
|
50
|
+
default:
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** 全局唯一的 tool id 分配;每条助手消息一个局部映射(有的供应商每轮都从 call_0 编号)。 */
|
|
55
|
+
class ToolIdMapper {
|
|
56
|
+
used = new Set();
|
|
57
|
+
scope() {
|
|
58
|
+
return new Map();
|
|
59
|
+
}
|
|
60
|
+
get(local, original) {
|
|
61
|
+
const existing = local.get(original);
|
|
62
|
+
if (existing !== undefined)
|
|
63
|
+
return existing;
|
|
64
|
+
const base = TOOL_ID_PATTERN.test(original)
|
|
65
|
+
? original
|
|
66
|
+
: original.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 64) || "tool";
|
|
67
|
+
let candidate = base;
|
|
68
|
+
for (let n = 1; this.used.has(candidate); n++)
|
|
69
|
+
candidate = `${base.slice(0, 56)}_${n}`;
|
|
70
|
+
local.set(original, candidate);
|
|
71
|
+
this.used.add(candidate);
|
|
72
|
+
return candidate;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function downgradeBlocks(message, target, ids, local) {
|
|
76
|
+
const sameModel = target === undefined ||
|
|
77
|
+
(message.provider === target.provider && message.model === target.model);
|
|
78
|
+
const content = [];
|
|
79
|
+
for (const block of message.content) {
|
|
80
|
+
if (block.type === "toolCall") {
|
|
81
|
+
const id = ids.get(local, block.id);
|
|
82
|
+
if (sameModel)
|
|
83
|
+
content.push(id === block.id ? block : { ...block, id });
|
|
84
|
+
else {
|
|
85
|
+
const { thoughtSignature: _signature, ...rest } = block;
|
|
86
|
+
content.push({ ...rest, id });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (block.type === "thinking") {
|
|
90
|
+
if (sameModel)
|
|
91
|
+
content.push(block);
|
|
92
|
+
else if (block.redacted !== true && block.thinking.trim() !== "") {
|
|
93
|
+
content.push({ type: "text", text: block.thinking });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (sameModel)
|
|
97
|
+
content.push(block);
|
|
98
|
+
else
|
|
99
|
+
content.push({ type: "text", text: block.text });
|
|
100
|
+
}
|
|
101
|
+
return { ...message, content };
|
|
102
|
+
}
|
|
103
|
+
export function repairTranscript(messages, target) {
|
|
104
|
+
const ids = new ToolIdMapper();
|
|
105
|
+
const out = [];
|
|
106
|
+
let i = 0;
|
|
107
|
+
while (i < messages.length) {
|
|
108
|
+
const message = messages[i];
|
|
109
|
+
i++;
|
|
110
|
+
if (message.role === "toolResult")
|
|
111
|
+
continue; // 孤儿结果:其调用已在前面消费掉
|
|
112
|
+
if (message.role !== "assistant") {
|
|
113
|
+
out.push(message);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const skip = message.stopReason === "error" || message.stopReason === "aborted";
|
|
117
|
+
const calls = message.content.filter((block) => block.type === "toolCall");
|
|
118
|
+
// 收集紧随其后的结果段
|
|
119
|
+
const results = [];
|
|
120
|
+
while (i < messages.length && messages[i]?.role === "toolResult") {
|
|
121
|
+
results.push(messages[i]);
|
|
122
|
+
i++;
|
|
123
|
+
}
|
|
124
|
+
if (skip)
|
|
125
|
+
continue;
|
|
126
|
+
const local = ids.scope();
|
|
127
|
+
out.push(downgradeBlocks(message, target, ids, local));
|
|
128
|
+
if (calls.length === 0)
|
|
129
|
+
continue;
|
|
130
|
+
const byId = new Map();
|
|
131
|
+
for (const result of results)
|
|
132
|
+
if (!byId.has(result.toolCallId))
|
|
133
|
+
byId.set(result.toolCallId, result);
|
|
134
|
+
for (const call of calls) {
|
|
135
|
+
const result = byId.get(call.id);
|
|
136
|
+
const id = ids.get(local, call.id);
|
|
137
|
+
if (result !== undefined)
|
|
138
|
+
out.push(result.toolCallId === id ? result : { ...result, toolCallId: id });
|
|
139
|
+
else {
|
|
140
|
+
out.push({
|
|
141
|
+
role: "toolResult",
|
|
142
|
+
toolCallId: id,
|
|
143
|
+
toolName: call.name,
|
|
144
|
+
content: ORPHAN_TOOL_RESULT_TEXT,
|
|
145
|
+
isError: true,
|
|
146
|
+
timestamp: message.timestamp,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
/** AgentMessage[] → Message[](扩展角色转 user)+ 回放修复。不抛错。 */
|
|
154
|
+
export function convertToLlm(messages, target) {
|
|
155
|
+
try {
|
|
156
|
+
const converted = [];
|
|
157
|
+
for (const message of messages) {
|
|
158
|
+
const llm = toUserMessage(message);
|
|
159
|
+
if (llm !== undefined)
|
|
160
|
+
converted.push(llm);
|
|
161
|
+
}
|
|
162
|
+
return repairTranscript(converted, target);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// 契约:不得抛错。退回只保留基本角色的未修复转录。
|
|
166
|
+
return messages.filter((message) => message.role === "system" ||
|
|
167
|
+
message.role === "user" ||
|
|
168
|
+
message.role === "assistant" ||
|
|
169
|
+
message.role === "toolResult");
|
|
170
|
+
}
|
|
171
|
+
}
|