@armadra/agent 0.4.0 → 0.5.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 +174 -1
- package/README.md +201 -82
- 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 +36203 -20499
- 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/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-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.js +5 -6
- 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 +7 -1
- package/dist/modes/commands-core.js +55 -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 +9 -1
- package/dist/modes/interactive/commands.js +19 -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 +134 -136
- package/dist/modes/interactive/key-dispatch.d.ts +16 -2
- package/dist/modes/interactive/key-dispatch.js +49 -1
- package/dist/modes/interactive/line/line-mode.d.ts +2 -1
- package/dist/modes/interactive/line/line-mode.js +22 -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/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/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 +5 -0
- package/dist/tui/keybindings.js +5 -0
- 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 +77 -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 +125 -6
- package/package.json +13 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 交互界面的第五波接线(计划审批、子 Agent、外部 Agent、剪贴板图片、harness 提示),从 interactive-mode.ts
|
|
3
|
+
* 拆出来,保持装配文件在 600 行以内。[W5-U]
|
|
4
|
+
*
|
|
5
|
+
* - 事件:`subagent_start / update / end` → 折叠视图(subagent-view.ts)并刷新对应的 task 工具行,后台任务
|
|
6
|
+
* 结束给一行提示;`limit_reached`、`model_fallback`、`background_job` → 一行友好提示。
|
|
7
|
+
* - 会话:`attach(session)` 接管计划审批(plan-flow.ts)、把外部 Agent 的 notice 转到消息区。
|
|
8
|
+
* - 命令:`/plan`(无参数:面板,有待审批的计划时打开审批框)、`/tasks`(无参数:选择任务,查看输出或停止)、
|
|
9
|
+
* `/agents`、`/paste`;其余参数形式交给 commands-core(与 line 模式同一语义)。
|
|
10
|
+
* - `Ctrl+V` / `/paste`:剪贴板图片写进数据目录,输入框光标处插入 `@<路径>`。
|
|
11
|
+
*
|
|
12
|
+
* 外部 Agent 的 notice(W5-E runner 的 `{type:"notice"}`)由任务注册表写进会话日志(`[task tN] …`),没有
|
|
13
|
+
* 事件;这里在会话的 `options.log` 外面套一层,只把这类行转到消息区,其余照原样写日志。
|
|
14
|
+
*/
|
|
15
|
+
import type { AgentSession, SessionEvent } from "../../agent/types.js";
|
|
16
|
+
import type { SwitchRequest } from "../../cli/compose-session.js";
|
|
17
|
+
import type { ClipboardDeps } from "../../tools/clipboard-image.js";
|
|
18
|
+
import type { Component, Editor, TUI, Theme } from "../../tui.js";
|
|
19
|
+
import { FirstRunMerge } from "./approval-merge.js";
|
|
20
|
+
import type { NoticeLevel } from "./message-view.js";
|
|
21
|
+
import type { PickerSpec } from "./pickers.js";
|
|
22
|
+
import { PlanFlow } from "./plan-flow.js";
|
|
23
|
+
import type { PlanDialogHost } from "./plan-dialog.js";
|
|
24
|
+
import { SubagentTracker } from "./subagent-view.js";
|
|
25
|
+
import type { ToolTracker } from "./tool-view.js";
|
|
26
|
+
export interface AgentUiDeps {
|
|
27
|
+
theme: Theme;
|
|
28
|
+
tui: TUI;
|
|
29
|
+
editor: Editor;
|
|
30
|
+
tools(): ToolTracker;
|
|
31
|
+
subagents: SubagentTracker;
|
|
32
|
+
session(): AgentSession;
|
|
33
|
+
dataDir: string;
|
|
34
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
35
|
+
now(): number;
|
|
36
|
+
notice(level: NoticeLevel, text: string): void;
|
|
37
|
+
panel(component: Component): void;
|
|
38
|
+
pick(spec: PickerSpec): Promise<{
|
|
39
|
+
value: string;
|
|
40
|
+
} | undefined>;
|
|
41
|
+
hint(text: string): void;
|
|
42
|
+
render(): void;
|
|
43
|
+
switchSession(request: SwitchRequest): Promise<AgentSession>;
|
|
44
|
+
prompt(text: string): void;
|
|
45
|
+
dialog: Omit<PlanDialogHost, "editExternal" | "render">;
|
|
46
|
+
/** 路径显示(缩写家目录)。 */
|
|
47
|
+
displayPath(path: string): string;
|
|
48
|
+
/** 剪贴板读取的注入(测试)。 */
|
|
49
|
+
clipboard?: ClipboardDeps;
|
|
50
|
+
}
|
|
51
|
+
export declare class AgentUi {
|
|
52
|
+
private readonly deps;
|
|
53
|
+
readonly plan: PlanFlow;
|
|
54
|
+
readonly merge: FirstRunMerge;
|
|
55
|
+
private restoreLog;
|
|
56
|
+
/** 后台子 Agent 运行中:每秒重画跟随行的耗时(主会话空闲时 Loader 不转)。 */
|
|
57
|
+
private ticker;
|
|
58
|
+
constructor(deps: AgentUiDeps);
|
|
59
|
+
/** 新会话(启动、/new /resume /fork):接管计划审批,转发外部 Agent notice。 */
|
|
60
|
+
attach(session: AgentSession): void;
|
|
61
|
+
detach(): void;
|
|
62
|
+
private followBackground;
|
|
63
|
+
private stopTicker;
|
|
64
|
+
private routeNotices;
|
|
65
|
+
/** 返回 true:事件已处理(调用方只需重画)。 */
|
|
66
|
+
onEvent(event: SessionEvent): boolean;
|
|
67
|
+
/** 交互界面自己处理的第五波命令;不是返回 false。 */
|
|
68
|
+
command(name: string, args: string): Promise<boolean>;
|
|
69
|
+
private showPlan;
|
|
70
|
+
private tasks;
|
|
71
|
+
/** `Ctrl+V` / `/paste`:光标处插入 `@<路径>`。 */
|
|
72
|
+
paste(): Promise<void>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 交互界面的第五波接线(计划审批、子 Agent、外部 Agent、剪贴板图片、harness 提示),从 interactive-mode.ts
|
|
3
|
+
* 拆出来,保持装配文件在 600 行以内。[W5-U]
|
|
4
|
+
*
|
|
5
|
+
* - 事件:`subagent_start / update / end` → 折叠视图(subagent-view.ts)并刷新对应的 task 工具行,后台任务
|
|
6
|
+
* 结束给一行提示;`limit_reached`、`model_fallback`、`background_job` → 一行友好提示。
|
|
7
|
+
* - 会话:`attach(session)` 接管计划审批(plan-flow.ts)、把外部 Agent 的 notice 转到消息区。
|
|
8
|
+
* - 命令:`/plan`(无参数:面板,有待审批的计划时打开审批框)、`/tasks`(无参数:选择任务,查看输出或停止)、
|
|
9
|
+
* `/agents`、`/paste`;其余参数形式交给 commands-core(与 line 模式同一语义)。
|
|
10
|
+
* - `Ctrl+V` / `/paste`:剪贴板图片写进数据目录,输入框光标处插入 `@<路径>`。
|
|
11
|
+
*
|
|
12
|
+
* 外部 Agent 的 notice(W5-E runner 的 `{type:"notice"}`)由任务注册表写进会话日志(`[task tN] …`),没有
|
|
13
|
+
* 事件;这里在会话的 `options.log` 外面套一层,只把这类行转到消息区,其余照原样写日志。
|
|
14
|
+
*/
|
|
15
|
+
import { taskRegistryView } from "../../agent/subagent-registry.js";
|
|
16
|
+
import { planController } from "../../plan/controller.js";
|
|
17
|
+
import { agentsPanel, planPanel, taskOutputPanel } from "./agent-panels.js";
|
|
18
|
+
import { FirstRunMerge } from "./approval-merge.js";
|
|
19
|
+
import { pasteImage } from "./clipboard-paste.js";
|
|
20
|
+
import { backgroundJobText, limitReachedText, modelFallbackText } from "./event-notices.js";
|
|
21
|
+
import { editExternally } from "./external-editor.js";
|
|
22
|
+
import { PlanFlow } from "./plan-flow.js";
|
|
23
|
+
import { backgroundEndText, SubagentTracker } from "./subagent-view.js";
|
|
24
|
+
import { listTasks, stopTask, taskLine, taskOutput } from "./tasks-report.js";
|
|
25
|
+
const TASK_NOTICE = /^\[task (t\d+)\] ([\s\S]*)$/;
|
|
26
|
+
export class AgentUi {
|
|
27
|
+
deps;
|
|
28
|
+
plan;
|
|
29
|
+
merge;
|
|
30
|
+
restoreLog;
|
|
31
|
+
/** 后台子 Agent 运行中:每秒重画跟随行的耗时(主会话空闲时 Loader 不转)。 */
|
|
32
|
+
ticker;
|
|
33
|
+
constructor(deps) {
|
|
34
|
+
this.deps = deps;
|
|
35
|
+
this.merge = new FirstRunMerge({ sessionId: () => deps.session().state.sessionId });
|
|
36
|
+
this.plan = new PlanFlow({
|
|
37
|
+
dialog: {
|
|
38
|
+
...deps.dialog,
|
|
39
|
+
displayPath: (path) => deps.displayPath(path),
|
|
40
|
+
render: () => deps.render(),
|
|
41
|
+
editExternal: (text, kind) => editExternally(text, kind === "plan" ? "plan.md" : "feedback.md", {
|
|
42
|
+
env: deps.env,
|
|
43
|
+
suspend: () => deps.tui.stop(),
|
|
44
|
+
resume: () => {
|
|
45
|
+
deps.tui.start();
|
|
46
|
+
deps.tui.forceFullRedraw();
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
switchSession: (request) => deps.switchSession(request),
|
|
51
|
+
notice: (level, text) => deps.notice(level, text),
|
|
52
|
+
prompt: (text) => deps.prompt(text),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** 新会话(启动、/new /resume /fork):接管计划审批,转发外部 Agent notice。 */
|
|
56
|
+
attach(session) {
|
|
57
|
+
this.deps.subagents.clear();
|
|
58
|
+
this.stopTicker();
|
|
59
|
+
this.plan.attach(session);
|
|
60
|
+
this.routeNotices(session);
|
|
61
|
+
}
|
|
62
|
+
detach() {
|
|
63
|
+
this.restoreLog?.();
|
|
64
|
+
this.restoreLog = undefined;
|
|
65
|
+
this.stopTicker();
|
|
66
|
+
}
|
|
67
|
+
followBackground() {
|
|
68
|
+
const running = this.deps.subagents.running().filter((s) => s.background);
|
|
69
|
+
if (running.length === 0)
|
|
70
|
+
return this.stopTicker();
|
|
71
|
+
if (this.ticker !== undefined)
|
|
72
|
+
return;
|
|
73
|
+
this.ticker = setInterval(() => {
|
|
74
|
+
const live = this.deps.subagents.running().filter((s) => s.background);
|
|
75
|
+
if (live.length === 0)
|
|
76
|
+
return this.stopTicker();
|
|
77
|
+
for (const state of live)
|
|
78
|
+
this.deps.tools().get(state.parentToolCallId)?.refresh();
|
|
79
|
+
this.deps.render();
|
|
80
|
+
}, 1000);
|
|
81
|
+
this.ticker.unref?.();
|
|
82
|
+
}
|
|
83
|
+
stopTicker() {
|
|
84
|
+
if (this.ticker !== undefined)
|
|
85
|
+
clearInterval(this.ticker);
|
|
86
|
+
this.ticker = undefined;
|
|
87
|
+
}
|
|
88
|
+
routeNotices(session) {
|
|
89
|
+
this.detach();
|
|
90
|
+
const holder = session.options;
|
|
91
|
+
if (holder === undefined)
|
|
92
|
+
return;
|
|
93
|
+
const original = holder.log;
|
|
94
|
+
const routed = (level, message) => {
|
|
95
|
+
const match = TASK_NOTICE.exec(message);
|
|
96
|
+
if (match !== null && (level === "info" || level === "warn")) {
|
|
97
|
+
const taskId = match[1];
|
|
98
|
+
const agent = taskRegistryView(session.state.sessionId)?.get(taskId)?.agent ?? "task";
|
|
99
|
+
this.deps.notice(level === "warn" ? "warn" : "info", `[${agent} · ${taskId}] ${match[2]}`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
original?.call(holder, level, message);
|
|
103
|
+
};
|
|
104
|
+
holder.log = routed;
|
|
105
|
+
this.restoreLog = () => {
|
|
106
|
+
if (holder.log !== routed)
|
|
107
|
+
return;
|
|
108
|
+
if (original === undefined)
|
|
109
|
+
delete holder.log;
|
|
110
|
+
else
|
|
111
|
+
holder.log = original;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** 返回 true:事件已处理(调用方只需重画)。 */
|
|
115
|
+
onEvent(event) {
|
|
116
|
+
switch (event.type) {
|
|
117
|
+
case "subagent_start":
|
|
118
|
+
case "subagent_update":
|
|
119
|
+
case "subagent_end": {
|
|
120
|
+
const state = this.deps.subagents.onEvent(event);
|
|
121
|
+
if (state === undefined)
|
|
122
|
+
return true;
|
|
123
|
+
this.deps.tools().get(state.parentToolCallId)?.refresh();
|
|
124
|
+
// 后台任务:完成时 <task-notification> 那一行就是提示;失败 / 停止另给一行
|
|
125
|
+
if (event.type === "subagent_end" && state.background && state.status !== "completed")
|
|
126
|
+
this.deps.notice("warn", backgroundEndText(state));
|
|
127
|
+
this.followBackground();
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
case "limit_reached":
|
|
131
|
+
this.deps.notice("warn", limitReachedText(event));
|
|
132
|
+
return true;
|
|
133
|
+
case "model_fallback":
|
|
134
|
+
this.deps.notice("warn", modelFallbackText(event));
|
|
135
|
+
return false;
|
|
136
|
+
case "background_job":
|
|
137
|
+
this.deps.notice("info", backgroundJobText(event));
|
|
138
|
+
return true;
|
|
139
|
+
default:
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** 交互界面自己处理的第五波命令;不是返回 false。 */
|
|
144
|
+
async command(name, args) {
|
|
145
|
+
if (args !== "")
|
|
146
|
+
return false;
|
|
147
|
+
switch (name) {
|
|
148
|
+
case "plan":
|
|
149
|
+
await this.showPlan();
|
|
150
|
+
return true;
|
|
151
|
+
case "tasks":
|
|
152
|
+
await this.tasks();
|
|
153
|
+
return true;
|
|
154
|
+
case "agents":
|
|
155
|
+
this.deps.panel(agentsPanel(this.deps.session().state.sessionId, this.deps.theme));
|
|
156
|
+
return true;
|
|
157
|
+
case "paste":
|
|
158
|
+
await this.paste();
|
|
159
|
+
return true;
|
|
160
|
+
default:
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async showPlan() {
|
|
165
|
+
const session = this.deps.session();
|
|
166
|
+
const controller = planController(session);
|
|
167
|
+
if (controller === undefined) {
|
|
168
|
+
this.deps.notice("info", "当前会话没有 Plan 能力");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
this.deps.panel(planPanel(controller, session, this.deps.theme, this.deps.displayPath));
|
|
172
|
+
const pending = controller.pending();
|
|
173
|
+
if (pending !== undefined && !session.state.isStreaming)
|
|
174
|
+
await this.plan.ask(session, pending);
|
|
175
|
+
}
|
|
176
|
+
async tasks() {
|
|
177
|
+
const sessionId = this.deps.session().state.sessionId;
|
|
178
|
+
const tasks = listTasks(sessionId);
|
|
179
|
+
if (tasks.length === 0) {
|
|
180
|
+
this.deps.notice("info", "还没有子 Agent 任务");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const now = this.deps.now();
|
|
184
|
+
const picked = await this.deps.pick({
|
|
185
|
+
title: "子 Agent 任务",
|
|
186
|
+
items: [...tasks]
|
|
187
|
+
.reverse()
|
|
188
|
+
.map((task) => ({ value: task.taskId, label: taskLine(task, now) })),
|
|
189
|
+
footer: "↑↓ 选择 · Enter 查看 · Esc 取消",
|
|
190
|
+
});
|
|
191
|
+
if (picked === undefined)
|
|
192
|
+
return;
|
|
193
|
+
const task = taskRegistryView(sessionId)?.get(picked.value);
|
|
194
|
+
if (task === undefined)
|
|
195
|
+
return;
|
|
196
|
+
if (task.status === "running") {
|
|
197
|
+
const action = await this.deps.pick({
|
|
198
|
+
title: `任务 ${task.taskId}`,
|
|
199
|
+
items: [
|
|
200
|
+
{ value: "output", label: "查看已有输出" },
|
|
201
|
+
{ value: "stop", label: "停止任务" },
|
|
202
|
+
],
|
|
203
|
+
numberKeys: true,
|
|
204
|
+
});
|
|
205
|
+
if (action === undefined)
|
|
206
|
+
return;
|
|
207
|
+
if (action.value === "stop") {
|
|
208
|
+
await stopTask(sessionId, task.taskId);
|
|
209
|
+
this.deps.notice("info", `已停止 ${task.taskId}`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const fresh = taskRegistryView(sessionId)?.get(task.taskId) ?? task;
|
|
214
|
+
const output = taskOutput(sessionId, task.taskId);
|
|
215
|
+
const { theme, displayPath } = this.deps;
|
|
216
|
+
this.deps.panel(taskOutputPanel(fresh, output, theme, this.deps.now(), displayPath));
|
|
217
|
+
}
|
|
218
|
+
/** `Ctrl+V` / `/paste`:光标处插入 `@<路径>`。 */
|
|
219
|
+
async paste() {
|
|
220
|
+
this.deps.hint("读取剪贴板…");
|
|
221
|
+
const pasted = await pasteImage(this.deps.dataDir, this.deps.clipboard);
|
|
222
|
+
if (!pasted.ok) {
|
|
223
|
+
this.deps.hint("");
|
|
224
|
+
this.deps.notice("info", pasted.message);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const editor = this.deps.editor;
|
|
228
|
+
const before = editor.getText();
|
|
229
|
+
const lead = before === "" || /\s$/.test(before) ? "" : " ";
|
|
230
|
+
editor.insertText(`${lead}${pasted.ref} `);
|
|
231
|
+
this.deps.hint("已插入剪贴板图片");
|
|
232
|
+
this.deps.render();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 审批对话框(设计 §12.6):模式层的 UI `ApprovalBroker`,底部覆盖层。[B7]
|
|
3
3
|
*
|
|
4
|
-
* -
|
|
4
|
+
* - 标题行:来源标注 + 工具名 + 原因标签。来源(W5-U):外部 Agent 的请求(`context.origin`)标
|
|
5
|
+
* `[claude · 会话 abc12345]`,正文取 `origin.toolCall` 的标题 / 种类 / 路径;task 子 Agent(`depth > 0`)标
|
|
6
|
+
* `[task:<agent>]`(类型名经 `ApprovalHost.taskAgent` 查注册表,查不到为 `[task]`);外部 Agent 首次运行确认
|
|
7
|
+
* (`toolName: "task"` 且输入带 `note`)标题为「首次运行外部 Agent」,正文是说明与模式;选项始终是
|
|
8
|
+
* 允许 / 本会话允许 / 拒绝三项(外部 Agent 的 `reject_always` 不由这里选);
|
|
5
9
|
* - 输入:bash 显示命令全文,write 显示路径与行数,edit 显示路径与每处修改的 −/+ 摘要,其它显示
|
|
6
10
|
* 一行摘要;`v` 展开 / 收起完整输入(JSON);
|
|
7
11
|
* - 执行前预览(W3-B9a-2,`request.preview`):输入摘要之后列出会碰到的路径与规模,danger 红、
|
|
@@ -21,6 +25,10 @@ import type { ApprovalBroker, ApprovalDecision, ApprovalRequest } from "../../pe
|
|
|
21
25
|
import { type Component, type Keybindings, type OverlayHandle, type Theme } from "../../tui.js";
|
|
22
26
|
export interface ApprovalHost {
|
|
23
27
|
theme: Theme;
|
|
28
|
+
/** task 子 Agent 的类型名(`[task:<agent>]`);查不到返回 undefined。 */
|
|
29
|
+
taskAgent?(taskId: string): string | undefined;
|
|
30
|
+
/** `task` 调用的 agent 是外部 Agent 时返回 runner(见 approval-merge.ts)。 */
|
|
31
|
+
externalRunner?(agent: string): string | undefined;
|
|
24
32
|
keybindings?: Keybindings;
|
|
25
33
|
/** 路径显示的基准。 */
|
|
26
34
|
cwd?: string;
|
|
@@ -39,9 +47,18 @@ export interface DescribeOptions {
|
|
|
39
47
|
expanded?: boolean;
|
|
40
48
|
/** 紧凑(窄屏):首行只放工具名(原因已在标题上)。 */
|
|
41
49
|
compact?: boolean;
|
|
50
|
+
/** task 子 Agent 的类型名(来源标注)。 */
|
|
51
|
+
taskAgent?: TaskAgentLookup;
|
|
52
|
+
/** `task` 调用的 agent 是外部 Agent 时返回 runner(正文写明首次运行一并确认)。 */
|
|
53
|
+
externalRunner?(agent: string): string | undefined;
|
|
42
54
|
}
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
export type TaskAgentLookup = (taskId: string) => string | undefined;
|
|
56
|
+
/** 外部 Agent 首次运行确认(`requestApproval` 发来的 `task` 请求,输入带 `note`)。 */
|
|
57
|
+
export declare function isFirstRunRequest(request: ApprovalRequest): boolean;
|
|
58
|
+
/** 来源标注:`[claude · 会话 abc12345]` / `[task:explore]` / `[task]`;主会话发起为 undefined。 */
|
|
59
|
+
export declare function sourceLabel(request: ApprovalRequest, taskAgent?: TaskAgentLookup): string | undefined;
|
|
60
|
+
/** 原因 → 标题文字(带来源标注)。 */
|
|
61
|
+
export declare function approvalTitle(request: ApprovalRequest, taskAgent?: TaskAgentLookup): string;
|
|
45
62
|
/** 对话框正文(不含选项与按键行)。 */
|
|
46
63
|
export declare function describeRequest(request: ApprovalRequest, theme: Theme, options?: DescribeOptions): string[];
|
|
47
64
|
interface ApprovalOption {
|
|
@@ -60,5 +77,5 @@ export declare class ApprovalDialogBroker implements ApprovalBroker {
|
|
|
60
77
|
ask(request: ApprovalRequest, signal: AbortSignal): Promise<ApprovalDecision | undefined>;
|
|
61
78
|
}
|
|
62
79
|
/** 结果的一行说明(消息区)。 */
|
|
63
|
-
export declare function approvalOutcomeText(request: ApprovalRequest, outcome: ApprovalDecision | "cancelled"): string;
|
|
80
|
+
export declare function approvalOutcomeText(request: ApprovalRequest, outcome: ApprovalDecision | "cancelled", taskAgent?: TaskAgentLookup): string;
|
|
64
81
|
export {};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 审批对话框(设计 §12.6):模式层的 UI `ApprovalBroker`,底部覆盖层。[B7]
|
|
3
3
|
*
|
|
4
|
-
* -
|
|
4
|
+
* - 标题行:来源标注 + 工具名 + 原因标签。来源(W5-U):外部 Agent 的请求(`context.origin`)标
|
|
5
|
+
* `[claude · 会话 abc12345]`,正文取 `origin.toolCall` 的标题 / 种类 / 路径;task 子 Agent(`depth > 0`)标
|
|
6
|
+
* `[task:<agent>]`(类型名经 `ApprovalHost.taskAgent` 查注册表,查不到为 `[task]`);外部 Agent 首次运行确认
|
|
7
|
+
* (`toolName: "task"` 且输入带 `note`)标题为「首次运行外部 Agent」,正文是说明与模式;选项始终是
|
|
8
|
+
* 允许 / 本会话允许 / 拒绝三项(外部 Agent 的 `reject_always` 不由这里选);
|
|
5
9
|
* - 输入:bash 显示命令全文,write 显示路径与行数,edit 显示路径与每处修改的 −/+ 摘要,其它显示
|
|
6
10
|
* 一行摘要;`v` 展开 / 收起完整输入(JSON);
|
|
7
11
|
* - 执行前预览(W3-B9a-2,`request.preview`):输入摘要之后列出会碰到的路径与规模,danger 红、
|
|
@@ -20,6 +24,7 @@
|
|
|
20
24
|
import { AUTO_LAYER_TEXT, isPermissionMode, permissionModeLabel } from "../../permissions/modes.js";
|
|
21
25
|
import { previewDisplayLines } from "../../permissions/preview.js";
|
|
22
26
|
import { Box, defaultKeybindings, matchesKey, padToWidth, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "../../tui.js";
|
|
27
|
+
import { displayPath, flat } from "./tool-summary.js";
|
|
23
28
|
import { toolSummary } from "./tool-view.js";
|
|
24
29
|
const COMMAND_LINES = 8;
|
|
25
30
|
const EDIT_LINES = 3;
|
|
@@ -33,30 +38,83 @@ function lines(text, max) {
|
|
|
33
38
|
function record(input) {
|
|
34
39
|
return typeof input === "object" && input !== null ? input : {};
|
|
35
40
|
}
|
|
36
|
-
/**
|
|
37
|
-
export function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
/** 外部 Agent 首次运行确认(`requestApproval` 发来的 `task` 请求,输入带 `note`)。 */
|
|
42
|
+
export function isFirstRunRequest(request) {
|
|
43
|
+
return request.toolName === "task" && typeof record(request.input)["note"] === "string";
|
|
44
|
+
}
|
|
45
|
+
/** 来源标注:`[claude · 会话 abc12345]` / `[task:explore]` / `[task]`;主会话发起为 undefined。 */
|
|
46
|
+
export function sourceLabel(request, taskAgent) {
|
|
47
|
+
const context = request.context;
|
|
48
|
+
const origin = context?.origin;
|
|
49
|
+
if (origin !== undefined)
|
|
50
|
+
return `[${origin.agent} · 会话 ${origin.sessionId.slice(0, 8)}]`;
|
|
51
|
+
if ((context?.depth ?? 0) <= 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
const taskId = context?.taskId;
|
|
54
|
+
const agent = taskId === undefined ? undefined : taskAgent?.(taskId);
|
|
55
|
+
return agent === undefined ? "[task]" : `[task:${agent}]`;
|
|
56
|
+
}
|
|
57
|
+
/** 原因 → 标题文字(带来源标注)。 */
|
|
58
|
+
export function approvalTitle(request, taskAgent) {
|
|
59
|
+
const label = sourceLabel(request, taskAgent);
|
|
60
|
+
const reason = isFirstRunRequest(request)
|
|
61
|
+
? "首次运行外部 Agent"
|
|
62
|
+
: request.reason === "dangerous"
|
|
63
|
+
? "危险命令"
|
|
64
|
+
: request.reason === "hook"
|
|
65
|
+
? "Hook 要求确认"
|
|
66
|
+
: "需要确认";
|
|
67
|
+
return label === undefined ? reason : `${label} ${reason}`;
|
|
68
|
+
}
|
|
69
|
+
/** 外部 Agent 请求的正文:标题 + 种类、涉及路径、输入摘要。 */
|
|
70
|
+
function originLines(origin, theme, cwd) {
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const path of (origin.toolCall.locations ?? []).slice(0, 3))
|
|
73
|
+
out.push(theme.fg("muted", displayPath(path, cwd)));
|
|
74
|
+
const hidden = (origin.toolCall.locations?.length ?? 0) - 3;
|
|
75
|
+
if (hidden > 0)
|
|
76
|
+
out.push(theme.fg("dim", `… 另 ${hidden} 个路径`));
|
|
77
|
+
const summary = origin.toolCall.inputSummary;
|
|
78
|
+
if (summary !== undefined && summary.trim() !== "") {
|
|
79
|
+
const { shown, hidden: more } = lines(summary, COMMAND_LINES);
|
|
80
|
+
out.push(...shown.map((l) => theme.fg("code", l)));
|
|
81
|
+
if (more > 0)
|
|
82
|
+
out.push(theme.fg("dim", `… 另 ${more} 行`));
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
45
85
|
}
|
|
46
86
|
/** 对话框正文(不含选项与按键行)。 */
|
|
47
87
|
export function describeRequest(request, theme, options = {}) {
|
|
48
88
|
const out = [];
|
|
49
|
-
const
|
|
89
|
+
const label = sourceLabel(request, options.taskAgent);
|
|
90
|
+
const task = label === undefined ? "" : theme.fg("warning", `${label} `);
|
|
91
|
+
const origin = request.context?.origin;
|
|
92
|
+
const firstRun = isFirstRunRequest(request);
|
|
50
93
|
const tag = request.reason === "dangerous"
|
|
51
94
|
? theme.fg("error", "危险命令")
|
|
52
95
|
: request.reason === "hook"
|
|
53
96
|
? theme.fg("warning", "Hook 要求确认")
|
|
54
|
-
:
|
|
55
|
-
|
|
56
|
-
|
|
97
|
+
: origin !== undefined
|
|
98
|
+
? theme.fg("dim", origin.toolCall.kind)
|
|
99
|
+
: theme.fg("dim", firstRun ? "首次运行" : "需要确认");
|
|
57
100
|
const input = record(request.input);
|
|
101
|
+
const name = origin !== undefined
|
|
102
|
+
? theme.bold(theme.fg("tool", flat(origin.toolCall.title)))
|
|
103
|
+
: firstRun
|
|
104
|
+
? theme.bold(theme.fg("tool", `task ${String(input["agent"] ?? "")}`.trim()))
|
|
105
|
+
: theme.bold(theme.fg("tool", request.toolName));
|
|
106
|
+
out.push(options.compact === true ? `${task}${name}` : `${task}${name} ${tag}`);
|
|
58
107
|
const more = (n) => theme.fg("dim", `… 另 ${n} 行(v 查看)`);
|
|
59
|
-
if (options.expanded
|
|
108
|
+
if (origin !== undefined && options.expanded !== true) {
|
|
109
|
+
out.push(...originLines(origin, theme, options.cwd));
|
|
110
|
+
}
|
|
111
|
+
else if (firstRun && options.expanded !== true) {
|
|
112
|
+
out.push(String(input["note"]));
|
|
113
|
+
const mode = input["mode"];
|
|
114
|
+
if (typeof mode === "string")
|
|
115
|
+
out.push(theme.fg("dim", `模式 ${isPermissionMode(mode) ? permissionModeLabel(mode) : mode}`));
|
|
116
|
+
}
|
|
117
|
+
else if (options.expanded === true) {
|
|
60
118
|
const json = JSON.stringify(request.input, null, 2) ?? String(request.input);
|
|
61
119
|
const { shown, hidden } = lines(json, FULL_INPUT_LINES);
|
|
62
120
|
out.push(...shown.map((l) => theme.fg("code", l)));
|
|
@@ -96,12 +154,26 @@ export function describeRequest(request, theme, options = {}) {
|
|
|
96
154
|
const summary = toolSummary(request.toolName, input, options.cwd);
|
|
97
155
|
if (summary !== "")
|
|
98
156
|
out.push(summary);
|
|
157
|
+
const agent = input["agent"];
|
|
158
|
+
const runner = request.toolName === "task" && typeof agent === "string"
|
|
159
|
+
? options.externalRunner?.(agent)
|
|
160
|
+
: undefined;
|
|
161
|
+
// 与首次运行确认合并(approval-merge.ts):允许即同意本会话以你的登录运行该 CLI
|
|
162
|
+
if (runner !== undefined)
|
|
163
|
+
out.push(theme.fg("warning", `外部 Agent ${agent}:以你在 ${runner} CLI 的登录运行(含本会话首次运行确认)`));
|
|
99
164
|
}
|
|
100
165
|
const severity = request.preview?.severity;
|
|
101
166
|
const color = severity === "danger" ? "error" : severity === "warn" ? "warning" : "dim";
|
|
102
167
|
out.push(...previewDisplayLines(request.preview).map((l) => theme.fg(color, l)));
|
|
103
168
|
const auto = request.autoDecision;
|
|
104
|
-
if (
|
|
169
|
+
if (origin !== undefined || firstRun) {
|
|
170
|
+
// 外部 Agent 的请求由它自己的策略决定要问;首次运行确认的说明已在正文
|
|
171
|
+
if (origin !== undefined)
|
|
172
|
+
out.push(theme.fg("dim", `${origin.agent} 请求确认`));
|
|
173
|
+
else
|
|
174
|
+
out.push(theme.fg("dim", "本会话首次以你的登录运行该 CLI"));
|
|
175
|
+
}
|
|
176
|
+
else if (request.reason === "hook") {
|
|
105
177
|
out.push(theme.fg("warning", `Hook:${request.hookReason ?? "(无说明)"}`));
|
|
106
178
|
}
|
|
107
179
|
else if (request.reason === "dangerous") {
|
|
@@ -174,6 +246,10 @@ class ApprovalDialog {
|
|
|
174
246
|
};
|
|
175
247
|
if (this.host.cwd !== undefined)
|
|
176
248
|
options.cwd = this.host.cwd;
|
|
249
|
+
if (this.host.taskAgent !== undefined)
|
|
250
|
+
options.taskAgent = this.host.taskAgent;
|
|
251
|
+
if (this.host.externalRunner !== undefined)
|
|
252
|
+
options.externalRunner = this.host.externalRunner;
|
|
177
253
|
const body = describeRequest(this.request, theme, options);
|
|
178
254
|
const lines = [];
|
|
179
255
|
// 输入摘要(标题 + 命令)与预览 / 原因之间空一行
|
|
@@ -252,7 +328,7 @@ export class ApprovalDialogBroker {
|
|
|
252
328
|
this.host.onOpen?.();
|
|
253
329
|
const severity = request.preview?.severity;
|
|
254
330
|
handle = this.host.showOverlay(new Box(dialog, {
|
|
255
|
-
title: approvalTitle(request),
|
|
331
|
+
title: approvalTitle(request, this.host.taskAgent),
|
|
256
332
|
theme: this.host.theme,
|
|
257
333
|
...(severity === "danger"
|
|
258
334
|
? { borderColor: "error" }
|
|
@@ -265,9 +341,11 @@ export class ApprovalDialogBroker {
|
|
|
265
341
|
}
|
|
266
342
|
}
|
|
267
343
|
/** 结果的一行说明(消息区)。 */
|
|
268
|
-
export function approvalOutcomeText(request, outcome) {
|
|
269
|
-
const
|
|
270
|
-
const
|
|
344
|
+
export function approvalOutcomeText(request, outcome, taskAgent) {
|
|
345
|
+
const label = sourceLabel(request, taskAgent);
|
|
346
|
+
const origin = request.context?.origin;
|
|
347
|
+
const tool = origin !== undefined ? flat(origin.toolCall.title, 60) : request.toolName;
|
|
348
|
+
const what = `${label === undefined ? "" : `${label} `}${tool}`;
|
|
271
349
|
switch (outcome) {
|
|
272
350
|
case "allow":
|
|
273
351
|
return `已允许 ${what}`;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* default 模式下 `task(agent="claude")` 会问两次:task 工具调用一次,外部 Agent 本会话首次运行再一次
|
|
3
|
+
* (agents/external.ts 的 `firstRun`)。界面层把它们合并成一次确认。[W5-U]
|
|
4
|
+
*
|
|
5
|
+
* 规则(不放宽权限语义):
|
|
6
|
+
* - 用户在对话框里**允许**了一次 `task` 工具调用,且它的 `agent` 是外部 Agent(类型的 runner 不是
|
|
7
|
+
* ama)——对话框已把「以你在该 CLI 的登录运行」写在正文里——记下 `{agent, 时刻}`;
|
|
8
|
+
* - **紧接着**的下一个审批请求若是同一 Agent 的首次运行确认(`toolName: "task"`、输入带 `note`),其
|
|
9
|
+
* `context.taskId` 对应的任务是该 Agent、且在允许之后才建立(同一次调用产生的任务),并在 60 s 内
|
|
10
|
+
* 到达,就以「允许」自动答复,不再弹框;
|
|
11
|
+
* - 记录只用一次;中间夹了任何别的审批请求、拒绝、超时,都作废——首次运行确认照常弹出。
|
|
12
|
+
*/
|
|
13
|
+
import type { ApprovalBroker, ApprovalDecision, ApprovalRequest } from "../../permissions/types.js";
|
|
14
|
+
export declare const MERGE_WINDOW_MS = 60000;
|
|
15
|
+
export interface FirstRunMergeDeps {
|
|
16
|
+
sessionId(): string;
|
|
17
|
+
now?(): number;
|
|
18
|
+
/** 类型名 → runner(`ama` / `claude` / …);缺省查会话的类型表。 */
|
|
19
|
+
runnerOf?(agent: string): string | undefined;
|
|
20
|
+
/** 任务 id → 类型名与建立时刻;缺省查注册表。 */
|
|
21
|
+
taskOf?(taskId: string): {
|
|
22
|
+
agent: string;
|
|
23
|
+
startedAt: number;
|
|
24
|
+
} | undefined;
|
|
25
|
+
windowMs?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare class FirstRunMerge {
|
|
28
|
+
private readonly deps;
|
|
29
|
+
private pending;
|
|
30
|
+
constructor(deps: FirstRunMergeDeps);
|
|
31
|
+
private now;
|
|
32
|
+
/** `task` 调用的 agent 是外部 Agent 时返回它的 runner。 */
|
|
33
|
+
externalRunner(agent: string): string | undefined;
|
|
34
|
+
private taskOf;
|
|
35
|
+
/** 请求到来:命中合并返回 `allow`(不弹框);否则返回 undefined,并作废记录。 */
|
|
36
|
+
intercept(request: ApprovalRequest): ApprovalDecision | undefined;
|
|
37
|
+
/** 用户答复之后:允许了外部 Agent 的 task 调用就记下。 */
|
|
38
|
+
record(request: ApprovalRequest, decision: ApprovalDecision | undefined): void;
|
|
39
|
+
}
|
|
40
|
+
/** 把合并规则套在界面 broker 外面;自动答复时调 `onMerged`(消息区一行说明)。 */
|
|
41
|
+
export declare function mergingBroker(inner: ApprovalBroker, merge: FirstRunMerge, onMerged?: (request: ApprovalRequest) => void): ApprovalBroker;
|