@armadra/agent 0.4.0 → 0.5.1
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 +184 -1
- package/README.md +202 -83
- package/THIRD_PARTY_NOTICES.md +34 -0
- package/dist/acp.d.ts +18 -0
- package/dist/acp.js +19 -0
- package/dist/agent/limits.d.ts +38 -0
- package/dist/agent/limits.js +127 -0
- package/dist/agent/loop-guard.d.ts +38 -0
- package/dist/agent/loop-guard.js +95 -0
- package/dist/agent/loop.d.ts +4 -0
- package/dist/agent/loop.js +7 -0
- package/dist/agent/prompt-rules.d.ts +12 -0
- package/dist/agent/prompt-rules.js +18 -0
- package/dist/agent/reminders.d.ts +59 -0
- package/dist/agent/reminders.js +327 -0
- package/dist/agent/session-cache.d.ts +5 -0
- package/dist/agent/session-cache.js +17 -1
- package/dist/agent/session-classifier.d.ts +1 -1
- package/dist/agent/session-classifier.js +3 -1
- package/dist/agent/session-compaction.d.ts +35 -6
- package/dist/agent/session-compaction.js +157 -31
- package/dist/agent/session-core.d.ts +27 -1
- package/dist/agent/session-extensions.d.ts +61 -0
- package/dist/agent/session-extensions.js +129 -0
- package/dist/agent/session-images.d.ts +23 -0
- package/dist/agent/session-images.js +97 -0
- package/dist/agent/session-plan.d.ts +21 -0
- package/dist/agent/session-plan.js +524 -0
- package/dist/agent/session-rewind.d.ts +81 -0
- package/dist/agent/session-rewind.js +354 -0
- package/dist/agent/session-run.d.ts +28 -1
- package/dist/agent/session-run.js +157 -6
- package/dist/agent/session-settings.d.ts +49 -0
- package/dist/agent/session-settings.js +95 -0
- package/dist/agent/session-subagent.d.ts +36 -24
- package/dist/agent/session-subagent.js +259 -129
- package/dist/agent/session-sync.d.ts +10 -3
- package/dist/agent/session-sync.js +37 -8
- package/dist/agent/session-telemetry.d.ts +39 -0
- package/dist/agent/session-telemetry.js +231 -0
- package/dist/agent/session-tools.js +40 -1
- package/dist/agent/session.d.ts +30 -29
- package/dist/agent/session.js +79 -123
- package/dist/agent/subagent-registry.d.ts +91 -0
- package/dist/agent/subagent-registry.js +494 -0
- package/dist/agent/system-prompt.d.ts +6 -3
- package/dist/agent/system-prompt.js +7 -2
- package/dist/agent/tool-runner.d.ts +8 -1
- package/dist/agent/tool-runner.js +57 -4
- package/dist/agent/types-w5.d.ts +192 -0
- package/dist/agent/types-w5.js +10 -0
- package/dist/agent/types.d.ts +47 -4
- package/dist/agent/types.js +2 -0
- package/dist/agent/worktree.d.ts +36 -0
- package/dist/agent/worktree.js +86 -0
- package/dist/agents/builtin.d.ts +14 -0
- package/dist/agents/builtin.js +39 -0
- package/dist/agents/catalog.d.ts +43 -0
- package/dist/agents/catalog.js +82 -0
- package/dist/agents/discover.d.ts +35 -0
- package/dist/agents/discover.js +83 -0
- package/dist/agents/external.d.ts +82 -0
- package/dist/agents/external.js +326 -0
- package/dist/agents/parse.d.ts +18 -0
- package/dist/agents/parse.js +116 -0
- package/dist/agents/result.d.ts +32 -0
- package/dist/agents/result.js +67 -0
- package/dist/agents/task-control.d.ts +17 -0
- package/dist/agents/task-control.js +17 -0
- package/dist/agents/task-record.d.ts +78 -0
- package/dist/agents/task-record.js +141 -0
- package/dist/agents/types.d.ts +46 -0
- package/dist/agents/types.js +8 -0
- package/dist/ai/apis/anthropic-compat.d.ts +36 -0
- package/dist/ai/apis/anthropic-compat.js +92 -0
- package/dist/ai/apis/anthropic-request.d.ts +7 -6
- package/dist/ai/apis/anthropic-request.js +10 -28
- package/dist/ai/apis/cache-params.d.ts +11 -2
- package/dist/ai/apis/cache-params.js +25 -6
- package/dist/ai/apis/openai-compat.d.ts +4 -1
- package/dist/ai/apis/openai-compat.js +8 -1
- package/dist/ai/image-limits.d.ts +45 -0
- package/dist/ai/image-limits.js +68 -0
- package/dist/ai/providers/builtin.d.ts +18 -6
- package/dist/ai/providers/builtin.js +135 -6
- package/dist/ai/providers/catalog-data.js +17 -13
- package/dist/ai/providers/catalog.d.ts +60 -7
- package/dist/ai/providers/catalog.js +197 -38
- package/dist/ai/providers/channels.d.ts +33 -3
- package/dist/ai/providers/channels.js +108 -7
- package/dist/ai/providers/enrich.d.ts +6 -3
- package/dist/ai/providers/enrich.js +19 -4
- package/dist/ai/providers/models-dev-cache.d.ts +28 -24
- package/dist/ai/providers/models-dev-cache.js +92 -78
- package/dist/ai/providers/models-dev-data.d.ts +7 -0
- package/dist/ai/providers/models-dev-data.js +32 -0
- package/dist/ai/providers/models-dev-snapshot.d.ts +50 -0
- package/dist/ai/providers/models-dev-snapshot.js +137 -0
- package/dist/ai/providers/models-dev.d.ts +51 -10
- package/dist/ai/providers/models-dev.js +132 -39
- package/dist/ai/providers/registry.d.ts +20 -10
- package/dist/ai/providers/registry.js +82 -43
- package/dist/ai/types.d.ts +28 -2
- package/dist/ai/types.js +5 -0
- package/dist/bundle/ama.cjs +37605 -21619
- package/dist/checkpoints/backend.d.ts +61 -0
- package/dist/checkpoints/backend.js +135 -0
- package/dist/checkpoints/blobs.d.ts +60 -0
- package/dist/checkpoints/blobs.js +203 -0
- package/dist/checkpoints/gc.d.ts +47 -0
- package/dist/checkpoints/gc.js +144 -0
- package/dist/checkpoints/git-head.d.ts +15 -0
- package/dist/checkpoints/git-head.js +89 -0
- package/dist/checkpoints/index.d.ts +17 -0
- package/dist/checkpoints/index.js +17 -0
- package/dist/checkpoints/replay.d.ts +48 -0
- package/dist/checkpoints/replay.js +154 -0
- package/dist/checkpoints/restore.d.ts +86 -0
- package/dist/checkpoints/restore.js +263 -0
- package/dist/checkpoints/settings.d.ts +14 -0
- package/dist/checkpoints/settings.js +23 -0
- package/dist/checkpoints/shadow-git.d.ts +108 -0
- package/dist/checkpoints/shadow-git.js +491 -0
- package/dist/checkpoints/shadow-restore.d.ts +25 -0
- package/dist/checkpoints/shadow-restore.js +128 -0
- package/dist/checkpoints/tracker.d.ts +64 -0
- package/dist/checkpoints/tracker.js +192 -0
- package/dist/checkpoints/types.d.ts +99 -0
- package/dist/checkpoints/types.js +12 -0
- package/dist/cli/args.d.ts +8 -4
- package/dist/cli/args.js +17 -97
- package/dist/cli/bootstrap.js +13 -1
- package/dist/cli/choice-prompt.d.ts +58 -0
- package/dist/cli/choice-prompt.js +140 -0
- package/dist/cli/codemode-notice.d.ts +3 -2
- package/dist/cli/codemode-notice.js +3 -2
- package/dist/cli/compose-agents.d.ts +27 -0
- package/dist/cli/compose-agents.js +111 -0
- package/dist/cli/compose-extensions.d.ts +29 -0
- package/dist/cli/compose-extensions.js +44 -0
- package/dist/cli/compose-session.d.ts +3 -0
- package/dist/cli/compose-session.js +47 -4
- package/dist/cli/compose.d.ts +3 -0
- package/dist/cli/compose.js +33 -6
- package/dist/cli/deps.d.ts +6 -0
- package/dist/cli/exit-codes.d.ts +10 -0
- package/dist/cli/exit-codes.js +12 -0
- package/dist/cli/help-text.d.ts +4 -0
- package/dist/cli/help-text.js +102 -0
- package/dist/cli/startup-screen.js +5 -3
- package/dist/cli/startup-steps.js +7 -1
- package/dist/cli/subcommands/config.d.ts +4 -1
- package/dist/cli/subcommands/config.js +17 -6
- package/dist/cli/subcommands/doctor.js +21 -0
- package/dist/cli/subcommands/models-cache-probe.d.ts +1 -1
- package/dist/cli/subcommands/models-cache-probe.js +3 -12
- package/dist/cli/subcommands/models-discover.js +5 -6
- package/dist/cli/subcommands/models.d.ts +2 -0
- package/dist/cli/subcommands/models.js +33 -14
- package/dist/cli/subcommands/providers.d.ts +1 -1
- package/dist/cli/subcommands/providers.js +8 -15
- package/dist/cli/subcommands/sessions.d.ts +3 -1
- package/dist/cli/subcommands/sessions.js +36 -2
- package/dist/codemode/capability.d.ts +24 -7
- package/dist/codemode/capability.js +38 -10
- package/dist/codemode/host-side.d.ts +14 -0
- package/dist/codemode/host-side.js +24 -1
- package/dist/codemode/tool.d.ts +1 -1
- package/dist/codemode/tool.js +6 -4
- package/dist/compaction/breaker.d.ts +25 -9
- package/dist/compaction/breaker.js +47 -17
- package/dist/compaction/estimate.d.ts +11 -3
- package/dist/compaction/estimate.js +64 -22
- package/dist/compaction/image-budget.d.ts +45 -0
- package/dist/compaction/image-budget.js +115 -0
- package/dist/compaction/post-compact.d.ts +38 -0
- package/dist/compaction/post-compact.js +152 -0
- package/dist/compaction/protect.d.ts +27 -0
- package/dist/compaction/protect.js +56 -0
- package/dist/compaction/prune-tier.d.ts +58 -12
- package/dist/compaction/prune-tier.js +120 -54
- package/dist/compaction/summarize-tier.d.ts +23 -4
- package/dist/compaction/summarize-tier.js +100 -29
- package/dist/config/checker.d.ts +28 -0
- package/dist/config/checker.js +97 -0
- package/dist/config/json-schema.js +63 -2
- package/dist/config/key-docs.js +72 -3
- package/dist/config/merge.d.ts +5 -3
- package/dist/config/merge.js +81 -7
- package/dist/config/profile.d.ts +2 -0
- package/dist/config/profile.js +2 -0
- package/dist/config/schema-w5.d.ts +18 -0
- package/dist/config/schema-w5.js +109 -0
- package/dist/config/schema.d.ts +5 -9
- package/dist/config/schema.js +68 -114
- package/dist/config/types-w5.d.ts +118 -0
- package/dist/config/types-w5.js +20 -0
- package/dist/config/types.d.ts +50 -1
- package/dist/config/types.js +12 -0
- package/dist/drivers/acp/client.d.ts +57 -0
- package/dist/drivers/acp/client.js +160 -0
- package/dist/drivers/acp/driver.d.ts +31 -0
- package/dist/drivers/acp/driver.js +274 -0
- package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
- package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
- package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
- package/dist/drivers/acp/testing/fake-agent.js +211 -0
- package/dist/drivers/acp/types.d.ts +285 -0
- package/dist/drivers/acp/types.js +36 -0
- package/dist/drivers/agents.d.ts +58 -0
- package/dist/drivers/agents.js +142 -0
- package/dist/drivers/base.d.ts +35 -0
- package/dist/drivers/base.js +59 -0
- package/dist/drivers/catalog.d.ts +46 -0
- package/dist/drivers/catalog.js +167 -0
- package/dist/drivers/env.d.ts +20 -0
- package/dist/drivers/env.js +46 -0
- package/dist/drivers/host-runners.d.ts +18 -0
- package/dist/drivers/host-runners.js +45 -0
- package/dist/drivers/jsonrpc.d.ts +58 -0
- package/dist/drivers/jsonrpc.js +180 -0
- package/dist/drivers/native/claude-normalize.d.ts +46 -0
- package/dist/drivers/native/claude-normalize.js +106 -0
- package/dist/drivers/native/claude-stream.d.ts +29 -0
- package/dist/drivers/native/claude-stream.js +456 -0
- package/dist/drivers/native/codex-app-server.d.ts +26 -0
- package/dist/drivers/native/codex-app-server.js +407 -0
- package/dist/drivers/native/codex-normalize.d.ts +38 -0
- package/dist/drivers/native/codex-normalize.js +124 -0
- package/dist/drivers/native/oneshot.d.ts +28 -0
- package/dist/drivers/native/oneshot.js +312 -0
- package/dist/drivers/permissions.d.ts +40 -0
- package/dist/drivers/permissions.js +92 -0
- package/dist/drivers/pids.d.ts +32 -0
- package/dist/drivers/pids.js +93 -0
- package/dist/drivers/pool.d.ts +28 -0
- package/dist/drivers/pool.js +96 -0
- package/dist/drivers/probe.d.ts +47 -0
- package/dist/drivers/probe.js +172 -0
- package/dist/drivers/process.d.ts +48 -0
- package/dist/drivers/process.js +100 -0
- package/dist/drivers/runner.d.ts +61 -0
- package/dist/drivers/runner.js +388 -0
- package/dist/drivers/store.d.ts +49 -0
- package/dist/drivers/store.js +91 -0
- package/dist/drivers/turn.d.ts +41 -0
- package/dist/drivers/turn.js +99 -0
- package/dist/drivers/types.d.ts +122 -0
- package/dist/drivers/types.js +11 -0
- package/dist/git/info.d.ts +75 -0
- package/dist/git/info.js +191 -0
- package/dist/hooks/protocol.js +6 -1
- package/dist/hooks/types.d.ts +10 -1
- package/dist/hooks/types.js +2 -0
- package/dist/host/api-impl.d.ts +7 -0
- package/dist/host/api-impl.js +15 -0
- package/dist/host/types.d.ts +24 -2
- package/dist/host/types.js +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/modes/acp/acp-events.d.ts +46 -0
- package/dist/modes/acp/acp-events.js +233 -0
- package/dist/modes/acp/acp-mode.d.ts +17 -0
- package/dist/modes/acp/acp-mode.js +47 -0
- package/dist/modes/acp/acp-server.d.ts +79 -0
- package/dist/modes/acp/acp-server.js +315 -0
- package/dist/modes/commands-core.d.ts +13 -1
- package/dist/modes/commands-core.js +64 -0
- package/dist/modes/image-input.d.ts +12 -4
- package/dist/modes/image-input.js +22 -5
- package/dist/modes/interactive/agent-panels.d.ts +14 -0
- package/dist/modes/interactive/agent-panels.js +103 -0
- package/dist/modes/interactive/agent-ui.d.ts +73 -0
- package/dist/modes/interactive/agent-ui.js +234 -0
- package/dist/modes/interactive/approval-dialog.d.ts +21 -4
- package/dist/modes/interactive/approval-dialog.js +98 -20
- package/dist/modes/interactive/approval-merge.d.ts +41 -0
- package/dist/modes/interactive/approval-merge.js +89 -0
- package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
- package/dist/modes/interactive/clipboard-paste.js +28 -0
- package/dist/modes/interactive/commands.d.ts +12 -1
- package/dist/modes/interactive/commands.js +31 -1
- package/dist/modes/interactive/confirm-dialog.d.ts +49 -0
- package/dist/modes/interactive/confirm-dialog.js +121 -0
- package/dist/modes/interactive/double-esc.d.ts +20 -0
- package/dist/modes/interactive/double-esc.js +34 -0
- package/dist/modes/interactive/event-notices.d.ts +32 -0
- package/dist/modes/interactive/event-notices.js +79 -0
- package/dist/modes/interactive/external-editor.d.ts +16 -0
- package/dist/modes/interactive/external-editor.js +39 -0
- package/dist/modes/interactive/interactive-mode.d.ts +14 -1
- package/dist/modes/interactive/interactive-mode.js +140 -136
- package/dist/modes/interactive/key-dispatch.d.ts +22 -2
- package/dist/modes/interactive/key-dispatch.js +66 -7
- package/dist/modes/interactive/line/line-mode.d.ts +4 -1
- package/dist/modes/interactive/line/line-mode.js +31 -5
- package/dist/modes/interactive/line/line-render.d.ts +2 -1
- package/dist/modes/interactive/line/line-render.js +40 -4
- package/dist/modes/interactive/message-view.d.ts +1 -1
- package/dist/modes/interactive/message-view.js +24 -2
- package/dist/modes/interactive/panels.d.ts +18 -1
- package/dist/modes/interactive/panels.js +19 -5
- package/dist/modes/interactive/plan-command.d.ts +40 -0
- package/dist/modes/interactive/plan-command.js +129 -0
- package/dist/modes/interactive/plan-dialog.d.ts +98 -0
- package/dist/modes/interactive/plan-dialog.js +290 -0
- package/dist/modes/interactive/plan-flow.d.ts +34 -0
- package/dist/modes/interactive/plan-flow.js +98 -0
- package/dist/modes/interactive/rewind-command.d.ts +31 -0
- package/dist/modes/interactive/rewind-command.js +106 -0
- package/dist/modes/interactive/rewind-flow.d.ts +37 -0
- package/dist/modes/interactive/rewind-flow.js +151 -0
- package/dist/modes/interactive/rewind-list.d.ts +36 -0
- package/dist/modes/interactive/rewind-list.js +123 -0
- package/dist/modes/interactive/rewind-panel.d.ts +77 -0
- package/dist/modes/interactive/rewind-panel.js +309 -0
- package/dist/modes/interactive/rewind-text.d.ts +40 -0
- package/dist/modes/interactive/rewind-text.js +139 -0
- package/dist/modes/interactive/session-events.d.ts +24 -0
- package/dist/modes/interactive/session-events.js +96 -0
- package/dist/modes/interactive/startup-ui.js +5 -1
- package/dist/modes/interactive/status-area.d.ts +73 -0
- package/dist/modes/interactive/status-area.js +190 -0
- package/dist/modes/interactive/status-bar.d.ts +99 -11
- package/dist/modes/interactive/status-bar.js +273 -90
- package/dist/modes/interactive/status-line.d.ts +33 -0
- package/dist/modes/interactive/status-line.js +113 -0
- package/dist/modes/interactive/subagent-view.d.ts +56 -0
- package/dist/modes/interactive/subagent-view.js +154 -0
- package/dist/modes/interactive/tasks-report.d.ts +27 -0
- package/dist/modes/interactive/tasks-report.js +114 -0
- package/dist/modes/interactive/tool-view.d.ts +12 -2
- package/dist/modes/interactive/tool-view.js +34 -4
- package/dist/modes/print/print-mode.d.ts +13 -4
- package/dist/modes/print/print-mode.js +56 -12
- package/dist/modes/rpc/commands.d.ts +9 -0
- package/dist/modes/rpc/commands.js +44 -0
- package/dist/modes/rpc/rpc-mode.js +7 -1
- package/dist/modes/session-report.d.ts +11 -0
- package/dist/modes/session-report.js +48 -1
- package/dist/permissions/auto-safe.d.ts +5 -0
- package/dist/permissions/auto-safe.js +18 -0
- package/dist/permissions/bypass.d.ts +32 -0
- package/dist/permissions/bypass.js +36 -0
- package/dist/permissions/classifier.d.ts +4 -0
- package/dist/permissions/classifier.js +2 -0
- package/dist/permissions/modes.js +1 -1
- package/dist/permissions/pipeline.d.ts +43 -4
- package/dist/permissions/pipeline.js +114 -9
- package/dist/permissions/readonly-bash.d.ts +30 -0
- package/dist/permissions/readonly-bash.js +118 -0
- package/dist/permissions/types.d.ts +42 -0
- package/dist/plan/compose.d.ts +9 -0
- package/dist/plan/compose.js +19 -0
- package/dist/plan/controller.d.ts +71 -0
- package/dist/plan/controller.js +27 -0
- package/dist/plan/done-markers.d.ts +17 -0
- package/dist/plan/done-markers.js +51 -0
- package/dist/plan/extract.d.ts +29 -0
- package/dist/plan/extract.js +160 -0
- package/dist/plan/prompts.d.ts +36 -0
- package/dist/plan/prompts.js +89 -0
- package/dist/plan/store.d.ts +37 -0
- package/dist/plan/store.js +90 -0
- package/dist/rpc.d.ts +58 -2
- package/dist/rpc.js +7 -0
- package/dist/sandbox/bash.d.ts +74 -0
- package/dist/sandbox/bash.js +126 -0
- package/dist/sandbox/detect.d.ts +48 -0
- package/dist/sandbox/detect.js +149 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.js +5 -0
- package/dist/sandbox/profile.d.ts +33 -0
- package/dist/sandbox/profile.js +76 -0
- package/dist/sandbox/wrap.d.ts +29 -0
- package/dist/sandbox/wrap.js +117 -0
- package/dist/sdk.d.ts +33 -2
- package/dist/sdk.js +24 -2
- package/dist/session/types.d.ts +3 -1
- package/dist/session/types.js +1 -0
- package/dist/skills/builtin.d.ts +31 -0
- package/dist/skills/builtin.js +109 -0
- package/dist/skills/discover.d.ts +2 -1
- package/dist/skills/index-prompt.js +5 -8
- package/dist/tools/background-jobs.d.ts +71 -0
- package/dist/tools/background-jobs.js +198 -0
- package/dist/tools/bash.d.ts +53 -1
- package/dist/tools/bash.js +158 -7
- package/dist/tools/clipboard-image.d.ts +44 -0
- package/dist/tools/clipboard-image.js +148 -0
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.js +18 -3
- package/dist/tools/image-file.d.ts +38 -3
- package/dist/tools/image-file.js +81 -10
- package/dist/tools/image-resize.d.ts +58 -0
- package/dist/tools/image-resize.js +97 -0
- package/dist/tools/presets.d.ts +7 -2
- package/dist/tools/presets.js +11 -4
- package/dist/tools/read.d.ts +5 -2
- package/dist/tools/read.js +13 -10
- package/dist/tools/registry.d.ts +7 -1
- package/dist/tools/registry.js +27 -4
- package/dist/tools/task-ctl.d.ts +22 -0
- package/dist/tools/task-ctl.js +116 -0
- package/dist/tools/task.d.ts +22 -10
- package/dist/tools/task.js +66 -26
- package/dist/tools/todo.d.ts +22 -2
- package/dist/tools/todo.js +58 -14
- package/dist/tools/truncate.d.ts +15 -0
- package/dist/tools/truncate.js +25 -0
- package/dist/tools/types.d.ts +113 -0
- package/dist/tools/types.js +5 -0
- package/dist/tools/write.js +3 -0
- package/dist/tui/glyphs.d.ts +4 -0
- package/dist/tui/glyphs.js +4 -0
- package/dist/tui/keybindings.d.ts +6 -1
- package/dist/tui/keybindings.js +6 -1
- package/docs/acp.md +78 -0
- package/docs/agents.md +250 -0
- package/docs/codemode.md +12 -8
- package/docs/hooks.md +26 -22
- package/docs/permissions.md +78 -13
- package/docs/plan.md +107 -0
- package/docs/providers.md +228 -52
- package/docs/rewind-plan.md +181 -0
- package/docs/rpc.md +92 -5
- package/docs/sandbox.md +215 -0
- package/docs/session-format.md +41 -21
- package/docs/sessions.md +41 -1
- package/docs/tui-design.md +751 -0
- package/docs/tui.md +179 -21
- package/package.json +13 -2
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 子 Agent 任务注册表(docs/wave5-plan.md §7.3–§7.6,D13、D23–D24)。[W5-G]
|
|
3
|
+
*
|
|
4
|
+
* 每个根会话一份(按会话 id 登记;compose-agents.ts 的扩展在会话构造时建,SDK / 测试直接构造的
|
|
5
|
+
* 会话在首次 task 时以缺省环境懒建)。职责:
|
|
6
|
+
* - 解析类型(catalog)→ 选 runner:ama 子会话(session-subagent.ts 的工厂)或外部 runner(W5-E /
|
|
7
|
+
* 宿主,经 `env.runners`);同一 `task(agent=…)` 入口;
|
|
8
|
+
* - 并发池(`subagents.maxConcurrent`,缺省 4)+ 排队上限(`maxPending`,缺省 16,超出报错不要重试);
|
|
9
|
+
* - 前台(等结果)/ 后台(立即返回 taskId,完成后 `<task-notification>` 走父会话 followUp 队列);
|
|
10
|
+
* - `taskId` 续聊:句柄保留 16 个(LRU,释放的只是内存,JSONL 永在),被释放或 resume 后按会话
|
|
11
|
+
* 文件 / 外部会话 id 重新接上;
|
|
12
|
+
* - 结果 > 50 KB 保留头尾并全文落 `outputs/`;轮数耗尽加说明;worktree 隔离;
|
|
13
|
+
* - 事件 `subagent_start / update / end`;父会话 `custom{ama.task}` 记任务快照(带 `status`),
|
|
14
|
+
* resume 时据此重建(未完成标 `interrupted`)。
|
|
15
|
+
*/
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { AmaError } from "../errors.js";
|
|
19
|
+
import { DEFAULT_AGENT } from "../agents/builtin.js";
|
|
20
|
+
import { AgentCatalog, resolveAgentModel } from "../agents/catalog.js";
|
|
21
|
+
import { MAX_TURNS_NOTE, capTaskText, taskNotification, writeTaskOutput, } from "../agents/result.js";
|
|
22
|
+
import { SubagentPool, TASK_CUSTOM_TYPE, applyRunnerEvent, flushText, newRecord, rebuildRecords, recordData, } from "../agents/task-record.js";
|
|
23
|
+
import { registerTaskControl, unregisterTaskControl, } from "../agents/task-control.js";
|
|
24
|
+
import { ZERO_USAGE } from "./loop.js";
|
|
25
|
+
import { createWorktree, finishWorktree } from "./worktree.js";
|
|
26
|
+
export const DEFAULT_SUBAGENT_CONCURRENCY = 4;
|
|
27
|
+
export const DEFAULT_MAX_PENDING = 16;
|
|
28
|
+
export const DEFAULT_RETAINED = 16;
|
|
29
|
+
export { SubagentPool, TASK_CUSTOM_TYPE };
|
|
30
|
+
const registries = new Map();
|
|
31
|
+
/** RPC `get_tasks` / `/tasks`:会话 id → 注册表视图(没有任务时 undefined)。 */
|
|
32
|
+
export function taskRegistryView(sessionId) {
|
|
33
|
+
return registries.get(sessionId);
|
|
34
|
+
}
|
|
35
|
+
/** RPC `get_agents` / `/agents`:会话可用的类型(没有 task 工具、注册表未装配时为空)。 */
|
|
36
|
+
export function sessionAgents(sessionId) {
|
|
37
|
+
return registries.get(sessionId)?.catalog.infos() ?? [];
|
|
38
|
+
}
|
|
39
|
+
export function registryOf(sessionId) {
|
|
40
|
+
return registries.get(sessionId);
|
|
41
|
+
}
|
|
42
|
+
/** 取会话的注册表;没有就以 `env`(缺省只有内置类型)新建并登记。 */
|
|
43
|
+
export function subagentRegistryFor(host, env = { catalog: new AgentCatalog() }) {
|
|
44
|
+
const existing = registries.get(host.manager.id);
|
|
45
|
+
if (existing !== undefined)
|
|
46
|
+
return existing;
|
|
47
|
+
const registry = new SubagentRegistry(host, env);
|
|
48
|
+
registries.set(host.manager.id, registry);
|
|
49
|
+
registerTaskControl(host.manager.id, registry);
|
|
50
|
+
return registry;
|
|
51
|
+
}
|
|
52
|
+
function failed(text) {
|
|
53
|
+
return { text, usage: { ...ZERO_USAGE }, stopReason: "error", isError: true, status: "failed" };
|
|
54
|
+
}
|
|
55
|
+
const TERMINAL = [
|
|
56
|
+
"completed",
|
|
57
|
+
"failed",
|
|
58
|
+
"aborted",
|
|
59
|
+
"max_turns",
|
|
60
|
+
"interrupted",
|
|
61
|
+
];
|
|
62
|
+
export class SubagentRegistry {
|
|
63
|
+
host;
|
|
64
|
+
env;
|
|
65
|
+
catalog;
|
|
66
|
+
pool;
|
|
67
|
+
tasks;
|
|
68
|
+
retained = [];
|
|
69
|
+
seq = 0;
|
|
70
|
+
disposed = false;
|
|
71
|
+
delivery = Promise.resolve();
|
|
72
|
+
constructor(host, env) {
|
|
73
|
+
this.host = host;
|
|
74
|
+
this.env = env;
|
|
75
|
+
this.catalog = env.catalog;
|
|
76
|
+
this.pool = new SubagentPool(env.maxConcurrent ?? DEFAULT_SUBAGENT_CONCURRENCY);
|
|
77
|
+
const rebuilt = rebuildRecords(host.manager.branch(), this.catalog, host.cwd);
|
|
78
|
+
this.tasks = rebuilt.records;
|
|
79
|
+
this.seq = rebuilt.seq;
|
|
80
|
+
}
|
|
81
|
+
now() {
|
|
82
|
+
return this.env.now?.() ?? Date.now();
|
|
83
|
+
}
|
|
84
|
+
// -------------------------------------------------------------------------
|
|
85
|
+
// 视图
|
|
86
|
+
// -------------------------------------------------------------------------
|
|
87
|
+
list() {
|
|
88
|
+
return [...this.tasks.values()].map((record) => ({ ...record.info }));
|
|
89
|
+
}
|
|
90
|
+
get(taskId) {
|
|
91
|
+
const record = this.tasks.get(taskId);
|
|
92
|
+
return record === undefined ? undefined : { ...record.info };
|
|
93
|
+
}
|
|
94
|
+
stats() {
|
|
95
|
+
if (this.tasks.size === 0)
|
|
96
|
+
return undefined;
|
|
97
|
+
const byStatus = {};
|
|
98
|
+
let running = 0;
|
|
99
|
+
for (const { info } of this.tasks.values()) {
|
|
100
|
+
if (info.status === "running")
|
|
101
|
+
running++;
|
|
102
|
+
else
|
|
103
|
+
byStatus[info.status] = (byStatus[info.status] ?? 0) + 1;
|
|
104
|
+
}
|
|
105
|
+
return { total: this.tasks.size, running, byStatus };
|
|
106
|
+
}
|
|
107
|
+
/** 运行中任务的已有输出(`task_ctl output`);结束后为最终文本。 */
|
|
108
|
+
output(taskId) {
|
|
109
|
+
return this.tasks.get(taskId)?.text;
|
|
110
|
+
}
|
|
111
|
+
/** 等任务结束或超时;超时返回 undefined。 */
|
|
112
|
+
async wait(taskId, timeoutMs) {
|
|
113
|
+
const record = this.tasks.get(taskId);
|
|
114
|
+
if (record === undefined)
|
|
115
|
+
throw new AmaError("task_not_found", `unknown task ${taskId}`);
|
|
116
|
+
if (record.running === undefined)
|
|
117
|
+
return record.last;
|
|
118
|
+
let timer;
|
|
119
|
+
const timeout = new Promise((resolve) => {
|
|
120
|
+
timer = setTimeout(() => resolve(undefined), timeoutMs);
|
|
121
|
+
});
|
|
122
|
+
try {
|
|
123
|
+
return await Promise.race([record.running, timeout]);
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async stop(taskId) {
|
|
130
|
+
const record = this.tasks.get(taskId);
|
|
131
|
+
if (record === undefined)
|
|
132
|
+
throw new AmaError("task_not_found", `unknown task ${taskId}`);
|
|
133
|
+
if (record.running === undefined)
|
|
134
|
+
return record.last;
|
|
135
|
+
record.controller?.abort();
|
|
136
|
+
return record.running;
|
|
137
|
+
}
|
|
138
|
+
// -------------------------------------------------------------------------
|
|
139
|
+
// 运行
|
|
140
|
+
// -------------------------------------------------------------------------
|
|
141
|
+
async run(request, ama) {
|
|
142
|
+
if (this.disposed)
|
|
143
|
+
return failed("the session is closed");
|
|
144
|
+
if (request.taskId !== undefined)
|
|
145
|
+
return this.continueTask(request, ama);
|
|
146
|
+
const name = request.agent ?? DEFAULT_AGENT;
|
|
147
|
+
const agent = this.catalog.get(name);
|
|
148
|
+
if (agent === undefined)
|
|
149
|
+
return failed(`Unknown agent "${name}". Available: ${this.catalog.names().join(", ")}.`);
|
|
150
|
+
if (agent.runner !== "ama" && this.env.runners?.(agent) === undefined)
|
|
151
|
+
return failed(`External agent "${name}" (${agent.runner}) is not available here.`);
|
|
152
|
+
const queued = this.pool.waiting;
|
|
153
|
+
if (queued >= (this.env.maxPending ?? DEFAULT_MAX_PENDING))
|
|
154
|
+
return failed(`Too many sub-agent tasks are queued (${queued}). Do not retry; wait for running tasks first (task_ctl wait).`);
|
|
155
|
+
const model = resolveAgentModel(agent, request.model, this.env.modelConfig ?? {});
|
|
156
|
+
if (model.warning !== undefined)
|
|
157
|
+
this.host.log("warn", `agent ${name}: ${model.warning}`);
|
|
158
|
+
const background = request.background ?? agent.background;
|
|
159
|
+
const taskId = `t${++this.seq}`;
|
|
160
|
+
const record = newRecord({
|
|
161
|
+
taskId,
|
|
162
|
+
agent: agent.name,
|
|
163
|
+
runner: agent.runner,
|
|
164
|
+
description: request.description ?? "",
|
|
165
|
+
background,
|
|
166
|
+
status: "running",
|
|
167
|
+
startedAt: this.now(),
|
|
168
|
+
}, agent, request.parentToolCallId, this.host.cwd, request.isolation ?? agent.isolation);
|
|
169
|
+
if (model.ref !== undefined)
|
|
170
|
+
record.modelRef = model.ref;
|
|
171
|
+
this.tasks.set(taskId, record);
|
|
172
|
+
return this.launch(record, request, ama, background);
|
|
173
|
+
}
|
|
174
|
+
continueTask(request, ama) {
|
|
175
|
+
const taskId = request.taskId;
|
|
176
|
+
const record = this.tasks.get(taskId);
|
|
177
|
+
if (record === undefined)
|
|
178
|
+
return Promise.resolve(failed(`Unknown task ${taskId}.`));
|
|
179
|
+
if (record.running !== undefined)
|
|
180
|
+
return Promise.resolve(failed(`Task ${taskId} is still running; use task_ctl wait or stop first.`));
|
|
181
|
+
const ref = record.info.sessionRef;
|
|
182
|
+
const resumable = record.agent.runner === "ama" ? ref?.sessionFile !== undefined : ref !== undefined;
|
|
183
|
+
if (record.handle === undefined && !resumable)
|
|
184
|
+
return Promise.resolve(failed(`Task ${taskId} cannot be continued (its session was not saved).`));
|
|
185
|
+
return this.launch(record, request, ama, request.background ?? false);
|
|
186
|
+
}
|
|
187
|
+
launch(record, request, ama, background) {
|
|
188
|
+
const controller = new AbortController();
|
|
189
|
+
record.controller = controller;
|
|
190
|
+
record.info.status = "running";
|
|
191
|
+
record.info.background = background;
|
|
192
|
+
delete record.info.endedAt;
|
|
193
|
+
record.text = "";
|
|
194
|
+
if (background)
|
|
195
|
+
delete record.onUpdate;
|
|
196
|
+
else if (request.onUpdate !== undefined)
|
|
197
|
+
record.onUpdate = request.onUpdate;
|
|
198
|
+
const parentSignal = background ? undefined : request.signal;
|
|
199
|
+
const onParentAbort = () => controller.abort();
|
|
200
|
+
if (parentSignal?.aborted)
|
|
201
|
+
controller.abort();
|
|
202
|
+
else
|
|
203
|
+
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
204
|
+
this.persist(record);
|
|
205
|
+
const running = this.execute(record, request, ama, controller.signal).finally(() => {
|
|
206
|
+
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
207
|
+
delete record.running;
|
|
208
|
+
delete record.controller;
|
|
209
|
+
});
|
|
210
|
+
record.running = running;
|
|
211
|
+
if (!background)
|
|
212
|
+
return running;
|
|
213
|
+
void running.then((result) => this.notify(record, result));
|
|
214
|
+
return Promise.resolve(this.startedResult(record));
|
|
215
|
+
}
|
|
216
|
+
startedResult(record) {
|
|
217
|
+
const result = {
|
|
218
|
+
text: `Started background task ${record.info.taskId} (agent ${record.agent.name}). ` +
|
|
219
|
+
"A <task-notification> arrives when it finishes; use task_ctl to wait, stop or read output.",
|
|
220
|
+
usage: { ...ZERO_USAGE },
|
|
221
|
+
stopReason: "stop",
|
|
222
|
+
isError: false,
|
|
223
|
+
taskId: record.info.taskId,
|
|
224
|
+
status: "running",
|
|
225
|
+
};
|
|
226
|
+
const file = this.outputFileFor(record.info.taskId);
|
|
227
|
+
if (file !== undefined)
|
|
228
|
+
result.outputFile = file;
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
async execute(record, request, ama, signal) {
|
|
232
|
+
try {
|
|
233
|
+
await this.pool.acquire(signal);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return this.finish(record, { ...failed("aborted by user"), status: "aborted" });
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
if (record.isolation === "worktree" && record.worktree === undefined) {
|
|
240
|
+
// 名字带会话 id 前缀:不同会话的 t1 不会撞到同一个 worktree / 分支
|
|
241
|
+
const name = `${this.host.manager.id.slice(0, 8)}-${record.info.taskId}`;
|
|
242
|
+
record.worktree = await createWorktree(this.host.cwd, name);
|
|
243
|
+
record.cwd = record.worktree.cwd;
|
|
244
|
+
}
|
|
245
|
+
const handle = await this.handleFor(record, request, ama, signal);
|
|
246
|
+
const onAbort = () => void handle.stop().catch(() => undefined);
|
|
247
|
+
if (signal.aborted)
|
|
248
|
+
onAbort();
|
|
249
|
+
else
|
|
250
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
251
|
+
try {
|
|
252
|
+
return this.finish(record, await handle.wait(), signal.aborted);
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
signal.removeEventListener("abort", onAbort);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
260
|
+
return this.finish(record, failed(`Sub-agent failed: ${message}`), signal.aborted);
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
this.pool.release();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async handleFor(record, request, ama, signal) {
|
|
267
|
+
const live = record.handle;
|
|
268
|
+
if (live !== undefined) {
|
|
269
|
+
this.touch(record.info.taskId);
|
|
270
|
+
this.emitStart(record, live);
|
|
271
|
+
await live.send(request.prompt);
|
|
272
|
+
return live;
|
|
273
|
+
}
|
|
274
|
+
const ref = record.info.sessionRef;
|
|
275
|
+
const runner = record.agent.runner === "ama"
|
|
276
|
+
? ama({
|
|
277
|
+
taskId: record.info.taskId,
|
|
278
|
+
agent: record.agent,
|
|
279
|
+
request,
|
|
280
|
+
...(record.modelRef === undefined ? {} : { modelRef: record.modelRef }),
|
|
281
|
+
cwd: record.cwd,
|
|
282
|
+
...(ref?.sessionFile === undefined ? {} : { resumeFile: ref.sessionFile }),
|
|
283
|
+
isolated: record.worktree !== undefined,
|
|
284
|
+
})
|
|
285
|
+
: this.env.runners?.(record.agent);
|
|
286
|
+
if (runner === undefined)
|
|
287
|
+
throw new Error(`runner ${record.agent.runner} is not available`);
|
|
288
|
+
const handle = await runner.start({
|
|
289
|
+
prompt: request.prompt,
|
|
290
|
+
cwd: record.cwd,
|
|
291
|
+
mode: this.host.options.permission?.mode ?? "default",
|
|
292
|
+
...(record.modelRef === undefined ? {} : { model: record.modelRef }),
|
|
293
|
+
...(ref !== undefined && record.agent.runner !== "ama" ? { resume: ref.sessionId } : {}),
|
|
294
|
+
...(request.budgetUsd === undefined ? {} : { budgetUsd: request.budgetUsd }),
|
|
295
|
+
taskId: record.info.taskId,
|
|
296
|
+
signal,
|
|
297
|
+
onEvent: (event) => applyRunnerEvent(record, event, this.sink()),
|
|
298
|
+
});
|
|
299
|
+
record.handle = handle;
|
|
300
|
+
record.info.sessionRef = {
|
|
301
|
+
runner: record.agent.runner,
|
|
302
|
+
sessionId: handle.id,
|
|
303
|
+
...(handle.sessionFile === undefined ? {} : { sessionFile: handle.sessionFile }),
|
|
304
|
+
};
|
|
305
|
+
this.emitStart(record, handle);
|
|
306
|
+
return handle;
|
|
307
|
+
}
|
|
308
|
+
emitStart(record, handle) {
|
|
309
|
+
const event = {
|
|
310
|
+
type: "subagent_start",
|
|
311
|
+
taskId: record.info.taskId,
|
|
312
|
+
parentToolCallId: record.parentToolCallId,
|
|
313
|
+
agent: record.agent.name,
|
|
314
|
+
runner: record.agent.runner,
|
|
315
|
+
description: record.info.description,
|
|
316
|
+
background: record.info.background,
|
|
317
|
+
cwd: record.cwd,
|
|
318
|
+
};
|
|
319
|
+
if (handle.model !== undefined)
|
|
320
|
+
event.model = handle.model;
|
|
321
|
+
if (handle.sessionFile !== undefined)
|
|
322
|
+
event.sessionFile = handle.sessionFile;
|
|
323
|
+
this.host.emit(event);
|
|
324
|
+
}
|
|
325
|
+
sink() {
|
|
326
|
+
return {
|
|
327
|
+
emit: (event) => this.host.emit(event),
|
|
328
|
+
log: (level, message) => this.host.log(level, message),
|
|
329
|
+
now: () => this.now(),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
async finish(record, result, aborted = false) {
|
|
333
|
+
flushText(record, this.sink());
|
|
334
|
+
const status = aborted
|
|
335
|
+
? "aborted"
|
|
336
|
+
: result.status !== undefined && TERMINAL.includes(result.status)
|
|
337
|
+
? result.status
|
|
338
|
+
: result.isError
|
|
339
|
+
? "failed"
|
|
340
|
+
: "completed";
|
|
341
|
+
if (record.worktree !== undefined) {
|
|
342
|
+
try {
|
|
343
|
+
record.worktreeOutcome = await finishWorktree(record.worktree);
|
|
344
|
+
if (!record.worktreeOutcome.changed)
|
|
345
|
+
delete record.worktree;
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
this.host.log("warn", `worktree cleanup failed: ${String(error)}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const info = record.info;
|
|
352
|
+
info.status = status;
|
|
353
|
+
info.endedAt = this.now();
|
|
354
|
+
if (result.usage.totalTokens > 0 || info.usage === undefined)
|
|
355
|
+
info.usage = result.usage;
|
|
356
|
+
record.text = result.text;
|
|
357
|
+
let outputFile;
|
|
358
|
+
const full = result.text;
|
|
359
|
+
const capped = capTaskText(full, this.outputFileFor(info.taskId));
|
|
360
|
+
if (capped.truncated || info.background) {
|
|
361
|
+
outputFile = writeTaskOutput(this.outputDirectory(), this.outputName(info.taskId), full);
|
|
362
|
+
if (outputFile !== undefined)
|
|
363
|
+
info.outputFile = outputFile;
|
|
364
|
+
}
|
|
365
|
+
let text = capped.text.trim() === "" ? "(the sub-agent returned no text)" : capped.text;
|
|
366
|
+
if (status === "max_turns")
|
|
367
|
+
text = `${MAX_TURNS_NOTE}\n\n${text}`;
|
|
368
|
+
const outcome = record.worktreeOutcome;
|
|
369
|
+
if (outcome?.changed === true)
|
|
370
|
+
text += `\n\n[worktree kept: branch ${outcome.branch} at ${outcome.path ?? "?"}]\n${outcome.diffStat ?? ""}`;
|
|
371
|
+
const final = {
|
|
372
|
+
...result,
|
|
373
|
+
text,
|
|
374
|
+
taskId: info.taskId,
|
|
375
|
+
status,
|
|
376
|
+
isError: status === "failed" || status === "aborted",
|
|
377
|
+
};
|
|
378
|
+
if (outputFile !== undefined)
|
|
379
|
+
final.outputFile = outputFile;
|
|
380
|
+
if (info.sessionRef !== undefined)
|
|
381
|
+
final.sessionRef = info.sessionRef;
|
|
382
|
+
record.last = final;
|
|
383
|
+
this.persist(record);
|
|
384
|
+
const end = {
|
|
385
|
+
type: "subagent_end",
|
|
386
|
+
taskId: info.taskId,
|
|
387
|
+
status,
|
|
388
|
+
};
|
|
389
|
+
if (info.usage !== undefined)
|
|
390
|
+
end.usage = info.usage;
|
|
391
|
+
if (result.cache !== undefined)
|
|
392
|
+
end.cache = result.cache;
|
|
393
|
+
if (outputFile !== undefined)
|
|
394
|
+
end.outputFile = outputFile;
|
|
395
|
+
if (outcome !== undefined)
|
|
396
|
+
end.worktree = { branch: outcome.branch, changed: outcome.changed };
|
|
397
|
+
this.host.emit(end);
|
|
398
|
+
this.retain(info.taskId);
|
|
399
|
+
return final;
|
|
400
|
+
}
|
|
401
|
+
notify(record, result) {
|
|
402
|
+
// 被停止的任务(task_ctl stop、会话关闭)不再通知:发起停止的一方已经知道
|
|
403
|
+
if (this.disposed || result.status === "aborted")
|
|
404
|
+
return;
|
|
405
|
+
const info = record.info;
|
|
406
|
+
const text = taskNotification({
|
|
407
|
+
taskId: info.taskId,
|
|
408
|
+
agent: info.agent,
|
|
409
|
+
status: (result.status ?? "completed"),
|
|
410
|
+
...(info.turns === undefined ? {} : { turns: info.turns }),
|
|
411
|
+
...(info.usage === undefined ? {} : { usage: info.usage }),
|
|
412
|
+
...(info.outputFile === undefined ? {} : { outputFile: info.outputFile }),
|
|
413
|
+
report: result.text,
|
|
414
|
+
});
|
|
415
|
+
const host = this.host;
|
|
416
|
+
const followUp = host.followUp;
|
|
417
|
+
if (followUp === undefined) {
|
|
418
|
+
host.log("warn", `task ${info.taskId} finished but the session cannot be notified`);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
// 父空闲时投递(开新回合);父正忙则等这一周期结束再投——周期收尾阶段入队的 followUp 会滞留到
|
|
422
|
+
// 下一次提示。串行投递保证按完成顺序到达。
|
|
423
|
+
this.delivery = this.delivery
|
|
424
|
+
.then(async () => {
|
|
425
|
+
await host.waitForIdle?.();
|
|
426
|
+
if (!this.disposed)
|
|
427
|
+
await followUp.call(host, text, { origin: "task" });
|
|
428
|
+
})
|
|
429
|
+
.catch((error) => host.log("warn", `task notification failed: ${String(error)}`));
|
|
430
|
+
}
|
|
431
|
+
// -------------------------------------------------------------------------
|
|
432
|
+
// 落盘、保留与重建
|
|
433
|
+
// -------------------------------------------------------------------------
|
|
434
|
+
outputDirectory() {
|
|
435
|
+
return this.host.outputDir() ?? join(tmpdir(), "ama-outputs");
|
|
436
|
+
}
|
|
437
|
+
outputName(taskId) {
|
|
438
|
+
return `${this.host.manager.id}-${taskId}.md`;
|
|
439
|
+
}
|
|
440
|
+
outputFileFor(taskId) {
|
|
441
|
+
return join(this.outputDirectory(), this.outputName(taskId));
|
|
442
|
+
}
|
|
443
|
+
persist(record) {
|
|
444
|
+
if (this.disposed)
|
|
445
|
+
return;
|
|
446
|
+
try {
|
|
447
|
+
this.host.appendEntry({
|
|
448
|
+
type: "custom",
|
|
449
|
+
customType: TASK_CUSTOM_TYPE,
|
|
450
|
+
data: recordData(record),
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
this.host.log("warn", `could not record task ${record.info.taskId}: ${String(error)}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
touch(taskId) {
|
|
458
|
+
const at = this.retained.indexOf(taskId);
|
|
459
|
+
if (at >= 0)
|
|
460
|
+
this.retained.splice(at, 1);
|
|
461
|
+
}
|
|
462
|
+
/** 句柄 LRU:超出上限时释放最久未用、空闲的子会话(JSONL 仍在,续聊时重开)。 */
|
|
463
|
+
retain(taskId) {
|
|
464
|
+
this.touch(taskId);
|
|
465
|
+
if (this.tasks.get(taskId)?.handle === undefined)
|
|
466
|
+
return;
|
|
467
|
+
this.retained.push(taskId);
|
|
468
|
+
const limit = this.env.retain ?? DEFAULT_RETAINED;
|
|
469
|
+
while (this.retained.length > limit) {
|
|
470
|
+
const evicted = this.tasks.get(this.retained.shift());
|
|
471
|
+
const handle = evicted?.handle;
|
|
472
|
+
if (evicted === undefined || handle === undefined)
|
|
473
|
+
continue;
|
|
474
|
+
delete evicted.handle;
|
|
475
|
+
void (handle.dispose?.() ?? handle.stop()).catch(() => undefined);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/** 会话关闭:停止运行中的任务、释放保留的子会话。 */
|
|
479
|
+
dispose() {
|
|
480
|
+
if (this.disposed)
|
|
481
|
+
return;
|
|
482
|
+
for (const record of this.tasks.values()) {
|
|
483
|
+
record.controller?.abort();
|
|
484
|
+
const handle = record.handle;
|
|
485
|
+
delete record.handle;
|
|
486
|
+
if (handle !== undefined)
|
|
487
|
+
void (handle.dispose?.() ?? handle.stop()).catch(() => undefined);
|
|
488
|
+
}
|
|
489
|
+
this.disposed = true;
|
|
490
|
+
if (registries.get(this.host.manager.id) === this)
|
|
491
|
+
registries.delete(this.host.manager.id);
|
|
492
|
+
unregisterTaskControl(this.host.manager.id, this);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 系统提示的命名节装配与补丁 diff(设计 §1.2 system-prompt.ts、§9「缓存」)。[B2]
|
|
3
3
|
*
|
|
4
|
-
* 节顺序固定:preamble → tools → rules → project_context → skills → hooks → cwd → host
|
|
5
|
-
* (`hooks` = SessionStart Hook 的 additionalContext;`host` = 宿主 instructions.add
|
|
4
|
+
* 节顺序固定:preamble → tools → rules → project_context → skills → hooks → cwd → host → role
|
|
5
|
+
* (`hooks` = SessionStart Hook 的 additionalContext;`host` = 宿主 instructions.add;`role` = [W5-G]
|
|
6
|
+
* task 子会话的角色说明,只有子会话有、放在最末,父会话的全部节是子会话的逐字节前缀)。
|
|
6
7
|
* 不含时间戳等易变内容,保证前缀缓存稳定。
|
|
7
8
|
*
|
|
8
9
|
* 首次请求前,整份节 + 工具表作为首条 `system` 消息落盘;之后节或工具表变化时只落补丁
|
|
@@ -12,7 +13,7 @@ import type { SystemMessage, ToolDecl } from "../ai/types.js";
|
|
|
12
13
|
import { type SystemState } from "../session/projection.js";
|
|
13
14
|
import type { AgentMessage } from "../session/types.js";
|
|
14
15
|
import type { ToolDefinition } from "../tools/types.js";
|
|
15
|
-
export declare const SECTION_ORDER: readonly ["preamble", "tools", "rules", "project_context", "skills", "hooks", "cwd", "host"];
|
|
16
|
+
export declare const SECTION_ORDER: readonly ["preamble", "tools", "rules", "project_context", "skills", "hooks", "cwd", "host", "role"];
|
|
16
17
|
export type SectionName = (typeof SECTION_ORDER)[number];
|
|
17
18
|
export declare const DEFAULT_PREAMBLE: string;
|
|
18
19
|
export interface SystemPromptInput {
|
|
@@ -35,6 +36,8 @@ export interface SystemPromptInput {
|
|
|
35
36
|
cwd: string;
|
|
36
37
|
/** 宿主 instructions(已读入的文本),按添加顺序。 */
|
|
37
38
|
hostInstructions?: readonly string[];
|
|
39
|
+
/** [W5-G] task 子会话的角色说明(子 Agent 通用说明 + 类型正文);主会话没有。 */
|
|
40
|
+
role?: string;
|
|
38
41
|
}
|
|
39
42
|
export declare function toolDecl(tool: ToolDefinition): ToolDecl;
|
|
40
43
|
/** 按名排序的工具声明(与 ToolRegistry.active() 的顺序一致)。 */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 系统提示的命名节装配与补丁 diff(设计 §1.2 system-prompt.ts、§9「缓存」)。[B2]
|
|
3
3
|
*
|
|
4
|
-
* 节顺序固定:preamble → tools → rules → project_context → skills → hooks → cwd → host
|
|
5
|
-
* (`hooks` = SessionStart Hook 的 additionalContext;`host` = 宿主 instructions.add
|
|
4
|
+
* 节顺序固定:preamble → tools → rules → project_context → skills → hooks → cwd → host → role
|
|
5
|
+
* (`hooks` = SessionStart Hook 的 additionalContext;`host` = 宿主 instructions.add;`role` = [W5-G]
|
|
6
|
+
* task 子会话的角色说明,只有子会话有、放在最末,父会话的全部节是子会话的逐字节前缀)。
|
|
6
7
|
* 不含时间戳等易变内容,保证前缀缓存稳定。
|
|
7
8
|
*
|
|
8
9
|
* 首次请求前,整份节 + 工具表作为首条 `system` 消息落盘;之后节或工具表变化时只落补丁
|
|
@@ -10,6 +11,7 @@
|
|
|
10
11
|
*/
|
|
11
12
|
import { replaySystem } from "../session/projection.js";
|
|
12
13
|
import { escapeXml } from "../skills/index-prompt.js";
|
|
14
|
+
import { baseRules } from "./prompt-rules.js";
|
|
13
15
|
export const SECTION_ORDER = [
|
|
14
16
|
"preamble",
|
|
15
17
|
"tools",
|
|
@@ -19,6 +21,7 @@ export const SECTION_ORDER = [
|
|
|
19
21
|
"hooks",
|
|
20
22
|
"cwd",
|
|
21
23
|
"host",
|
|
24
|
+
"role",
|
|
22
25
|
];
|
|
23
26
|
export const DEFAULT_PREAMBLE = "You are ama, a coding agent working in the user's project through tools. Read files before " +
|
|
24
27
|
"changing them, make focused changes, and verify your work. Be concise.";
|
|
@@ -40,6 +43,7 @@ export function assembleSections(input) {
|
|
|
40
43
|
return snippet.startsWith(`${tool.name}:`) ? `- ${snippet}` : `- ${tool.name}: ${snippet}`;
|
|
41
44
|
});
|
|
42
45
|
const rules = [
|
|
46
|
+
...baseRules(tools),
|
|
43
47
|
...tools.flatMap((tool) => tool.promptGuidelines ?? []),
|
|
44
48
|
...(input.extraRules ?? []),
|
|
45
49
|
];
|
|
@@ -65,6 +69,7 @@ export function assembleSections(input) {
|
|
|
65
69
|
hooks: nonEmpty(input.hookContext),
|
|
66
70
|
cwd: `Current working directory: ${input.cwd}`,
|
|
67
71
|
host: host.length > 0 ? host.join("\n\n") : undefined,
|
|
72
|
+
role: nonEmpty(input.role),
|
|
68
73
|
};
|
|
69
74
|
}
|
|
70
75
|
/** 去掉 undefined 的有序节表。 */
|
|
@@ -7,13 +7,17 @@
|
|
|
7
7
|
* `tool_execution_end` 按完成顺序发;toolResult 消息按原序发 `message_start/end`(入转录)。
|
|
8
8
|
* `terminate` 要整批都为真才提前结束。`stopReason: "length"` 且有工具调用 → 整批判失败不执行。
|
|
9
9
|
* abort:未开始的调用直接给 `aborted by user` 错误结果;执行中的工具收到 signal,超过宽限期仍未结束
|
|
10
|
-
* 则不再等待、记 `aborted by user`。结果超 `maxToolResultChars` 截断并把全文写到 `outputDir
|
|
10
|
+
* 则不再等待、记 `aborted by user`。结果超 `maxToolResultChars` 截断并把全文写到 `outputDir`:
|
|
11
|
+
* [W5-H2] 保留头 70% + 尾 30%,中间 `[… N 字符已省略,全文 <path>]`(tools/truncate.ts)。
|
|
11
12
|
*
|
|
12
13
|
* 嵌套调用(`ToolContext.tools.executeTool`,codemode 脚本里的 `tools.*`)走 `runSingleToolCall`:
|
|
13
14
|
* 同一套校验与门禁,按**全部未禁用工具**查找(codemode only 模式下活动集只有 codemode),
|
|
14
15
|
* 发带 `parentToolCallId` 的 `tool_execution_start / update / end`(不入转录),门禁与 PostToolUse
|
|
15
16
|
* 拿到 `parent`(Hook 输入的 `viaCodemode / parentToolCallId`);结果给脚本而不是模型,截断上限放宽到
|
|
16
17
|
* `NESTED_MAX_RESULT_CHARS`。
|
|
18
|
+
*
|
|
19
|
+
* [W5-H2] 重复调用检测(loop-guard.ts)只看顶层调用:同 run 同名同参第 3、4 次在结果末尾追加提醒,
|
|
20
|
+
* 第 5 次不执行、给错误结果,整批返回 `stop: "repeated_tool_call"`(循环据此结束 run)。
|
|
17
21
|
*/
|
|
18
22
|
import type { AssistantMessage, ToolCallBlock, ToolResultMessage } from "../ai/types.js";
|
|
19
23
|
import type { NestedCallInfo, SessionEvent, ToolCallGate, ToolCallGateContext } from "./types.js";
|
|
@@ -47,11 +51,14 @@ export interface ToolRunnerOptions {
|
|
|
47
51
|
export interface ToolBatchResult {
|
|
48
52
|
messages: ToolResultMessage[];
|
|
49
53
|
terminate: boolean;
|
|
54
|
+
/** [W5-H2] 结束本 run 的原因(重复调用检测到 5 次:`repeated_tool_call`)。 */
|
|
55
|
+
stop?: string;
|
|
50
56
|
}
|
|
51
57
|
export declare function errorResult(text: string): ToolResult;
|
|
52
58
|
export declare function toolResultMessage(call: ToolCallBlock, result: ToolResult): ToolResultMessage;
|
|
53
59
|
export declare function toolCallsOf(message: AssistantMessage): ToolCallBlock[];
|
|
54
60
|
export declare function runToolBatch(assistant: AssistantMessage, options: ToolRunnerOptions, signal: AbortSignal, emit: LoopEmit): Promise<ToolBatchResult>;
|
|
61
|
+
export declare function setBatchSuffixSource(signal: AbortSignal, source: () => string | undefined): void;
|
|
55
62
|
/** `length` 截断的助手消息:整批判失败,不执行。 */
|
|
56
63
|
export declare function failTruncatedBatch(assistant: AssistantMessage, emit: LoopEmit): Promise<ToolBatchResult>;
|
|
57
64
|
/** 被中断 / 出错的助手消息里的工具调用:各补一条错误结果(落盘不变式:每个 tool_call 恰有一个 result)。 */
|