@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,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 操作系统沙箱能力探测(docs/sandbox.md「探测与降级」)。
|
|
3
|
+
*
|
|
4
|
+
* - 「存在」不等于「可用」(嵌套在别的沙箱里、容器里没有用户命名空间):用目标配置跑一次最小探针,退出码 0
|
|
5
|
+
* 才算可用;
|
|
6
|
+
* - macOS:`/usr/bin/sandbox-exec`;Linux:PATH 里的 `bwrap`,失败再试 `unshare -r -n`(只隔离网络);
|
|
7
|
+
* 其它平台:none;
|
|
8
|
+
* - 开关:`sandbox.enabled: off` 或环境变量 `AMA_SANDBOX=off`;
|
|
9
|
+
* - 结果在进程内缓存(按开关分两份);`configureOsSandbox` 记下组装根读到的配置,供拿不到配置的只读调用方
|
|
10
|
+
* (状态栏)得到一致的结论。
|
|
11
|
+
*/
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
import { accessSync, constants } from "node:fs";
|
|
14
|
+
import { posix, win32 } from "node:path";
|
|
15
|
+
import { buildBwrapArgs, buildSbplProfile, buildUnshareArgs, } from "./profile.js";
|
|
16
|
+
export const NO_OS_SANDBOX = Object.freeze({
|
|
17
|
+
kind: "none",
|
|
18
|
+
isolatesNetwork: false,
|
|
19
|
+
restrictsWrites: false,
|
|
20
|
+
detail: "没有可用的操作系统沙箱",
|
|
21
|
+
});
|
|
22
|
+
export const SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
|
|
23
|
+
export const PROBE_TIMEOUT_MS = 5000;
|
|
24
|
+
/** 探针用的策略:拒绝网络、不可写——与 codemode 实际使用的最严配置相同。 */
|
|
25
|
+
const PROBE_POLICY = { network: "deny", writable: [] };
|
|
26
|
+
function isExecutable(path) {
|
|
27
|
+
try {
|
|
28
|
+
accessSync(path, constants.X_OK);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export const REAL_PROBE_DEPS = {
|
|
36
|
+
platform: process.platform,
|
|
37
|
+
env: process.env,
|
|
38
|
+
isExecutable,
|
|
39
|
+
run(command, args) {
|
|
40
|
+
const result = spawnSync(command, args, {
|
|
41
|
+
stdio: "ignore",
|
|
42
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
43
|
+
env: { PATH: process.env["PATH"] ?? "/usr/bin:/bin" },
|
|
44
|
+
});
|
|
45
|
+
return result.error !== undefined ? null : result.status;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/** 在 PATH 里找可执行文件(按目标平台的路径规则)。 */
|
|
49
|
+
export function findExecutable(name, env, check, platform = process.platform) {
|
|
50
|
+
const path = platform === "win32" ? win32 : posix;
|
|
51
|
+
for (const dir of (env["PATH"] ?? "").split(path.delimiter)) {
|
|
52
|
+
if (dir === "")
|
|
53
|
+
continue;
|
|
54
|
+
const candidate = path.join(dir, name);
|
|
55
|
+
if (check(candidate))
|
|
56
|
+
return candidate;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
/** 生效的开关:环境变量 `AMA_SANDBOX=off` 优先于配置。 */
|
|
61
|
+
export function resolveSandboxEnabled(configured, env = process.env) {
|
|
62
|
+
const fromEnv = env["AMA_SANDBOX"]?.trim().toLowerCase();
|
|
63
|
+
if (fromEnv === "off" || fromEnv === "0" || fromEnv === "false")
|
|
64
|
+
return "off";
|
|
65
|
+
return configured ?? "auto";
|
|
66
|
+
}
|
|
67
|
+
/** 不缓存的探测(测试注入 deps)。 */
|
|
68
|
+
export function probeOsSandbox(enabled, deps = REAL_PROBE_DEPS) {
|
|
69
|
+
if (enabled === "off")
|
|
70
|
+
return { ...NO_OS_SANDBOX, detail: "已关闭(sandbox.enabled: off 或 AMA_SANDBOX=off)" };
|
|
71
|
+
if (deps.platform === "darwin") {
|
|
72
|
+
if (!deps.isExecutable(SANDBOX_EXEC_PATH))
|
|
73
|
+
return { ...NO_OS_SANDBOX, detail: `没有 ${SANDBOX_EXEC_PATH}` };
|
|
74
|
+
const code = deps.run(SANDBOX_EXEC_PATH, [
|
|
75
|
+
"-p",
|
|
76
|
+
buildSbplProfile(PROBE_POLICY),
|
|
77
|
+
"/usr/bin/true",
|
|
78
|
+
]);
|
|
79
|
+
if (code === 0)
|
|
80
|
+
return {
|
|
81
|
+
kind: "sandbox-exec",
|
|
82
|
+
path: SANDBOX_EXEC_PATH,
|
|
83
|
+
isolatesNetwork: true,
|
|
84
|
+
restrictsWrites: true,
|
|
85
|
+
detail: "macOS sandbox-exec",
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
...NO_OS_SANDBOX,
|
|
89
|
+
detail: `sandbox-exec 探针失败(${code === null ? "无法启动" : `退出码 ${code}`};可能已在别的沙箱里)`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (deps.platform === "linux") {
|
|
93
|
+
const failures = [];
|
|
94
|
+
const bwrap = findExecutable("bwrap", deps.env, deps.isExecutable, deps.platform);
|
|
95
|
+
if (bwrap !== undefined) {
|
|
96
|
+
const code = deps.run(bwrap, [...buildBwrapArgs(PROBE_POLICY), "true"]);
|
|
97
|
+
if (code === 0)
|
|
98
|
+
return {
|
|
99
|
+
kind: "bwrap",
|
|
100
|
+
path: bwrap,
|
|
101
|
+
isolatesNetwork: true,
|
|
102
|
+
restrictsWrites: true,
|
|
103
|
+
detail: "Linux bubblewrap",
|
|
104
|
+
};
|
|
105
|
+
failures.push(`bwrap 探针失败(${code === null ? "无法启动" : `退出码 ${code}`})`);
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
failures.push("没有 bwrap");
|
|
109
|
+
const unshare = findExecutable("unshare", deps.env, deps.isExecutable, deps.platform);
|
|
110
|
+
if (unshare !== undefined) {
|
|
111
|
+
const code = deps.run(unshare, [...buildUnshareArgs(), "true"]);
|
|
112
|
+
if (code === 0)
|
|
113
|
+
return {
|
|
114
|
+
kind: "unshare",
|
|
115
|
+
path: unshare,
|
|
116
|
+
isolatesNetwork: true,
|
|
117
|
+
restrictsWrites: false,
|
|
118
|
+
detail: `Linux unshare -r -n(只隔离网络;${failures.join(",")})`,
|
|
119
|
+
};
|
|
120
|
+
failures.push(`unshare 探针失败(${code === null ? "无法启动" : `退出码 ${code}`};可能不允许非特权用户命名空间)`);
|
|
121
|
+
}
|
|
122
|
+
else
|
|
123
|
+
failures.push("没有 unshare");
|
|
124
|
+
return { ...NO_OS_SANDBOX, detail: failures.join(",") };
|
|
125
|
+
}
|
|
126
|
+
return { ...NO_OS_SANDBOX, detail: `${deps.platform} 上没有操作系统沙箱实现` };
|
|
127
|
+
}
|
|
128
|
+
const cache = new Map();
|
|
129
|
+
let configured;
|
|
130
|
+
/** 组装根读到配置后记下(拿不到配置的只读调用方据此得到一致结论);返回生效的状态。 */
|
|
131
|
+
export function configureOsSandbox(enabled) {
|
|
132
|
+
configured = enabled;
|
|
133
|
+
return osSandboxStatus(enabled);
|
|
134
|
+
}
|
|
135
|
+
/** 进程内缓存的探测结果;不传开关时用 `configureOsSandbox` 记下的配置(缺省 auto)。 */
|
|
136
|
+
export function osSandboxStatus(enabled = configured) {
|
|
137
|
+
const effective = resolveSandboxEnabled(enabled);
|
|
138
|
+
let status = cache.get(effective);
|
|
139
|
+
if (status === undefined) {
|
|
140
|
+
status = probeOsSandbox(effective);
|
|
141
|
+
cache.set(effective, status);
|
|
142
|
+
}
|
|
143
|
+
return status;
|
|
144
|
+
}
|
|
145
|
+
/** 测试用:清空缓存与记下的配置。 */
|
|
146
|
+
export function resetOsSandboxForTests() {
|
|
147
|
+
cache.clear();
|
|
148
|
+
configured = undefined;
|
|
149
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** 操作系统级沙箱(docs/sandbox.md):探测、参数生成与命令包装。 */
|
|
2
|
+
export { NO_OS_SANDBOX, configureOsSandbox, osSandboxStatus, probeOsSandbox, resetOsSandboxForTests, resolveSandboxEnabled, type OsSandboxStatus, type ProbeDeps, type SandboxEnabled, } from "./detect.js";
|
|
3
|
+
export { buildBwrapArgs, buildSbplProfile, buildUnshareArgs, type OsSandboxKind, type OsSandboxPolicy, } from "./profile.js";
|
|
4
|
+
export { resolveExtras, resolveWritable, wrapCommand, type WrappedCommand } from "./wrap.js";
|
|
5
|
+
export { DEFAULT_BASH_SANDBOX_MODE, DEFAULT_SANDBOX_NETWORK, NO_BASH_SANDBOX, bashSandboxPolicy, looksLikeSandboxDenial, resolveBashSandbox, runsSandboxed, sandboxDenialHint, wantsUnsandboxed, wrapBashCommand, type BashCallPlace, type BashSandbox, type BashSandboxConfig, type BashSandboxMode, type SandboxNetwork, } from "./bash.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** 操作系统级沙箱(docs/sandbox.md):探测、参数生成与命令包装。 */
|
|
2
|
+
export { NO_OS_SANDBOX, configureOsSandbox, osSandboxStatus, probeOsSandbox, resetOsSandboxForTests, resolveSandboxEnabled, } from "./detect.js";
|
|
3
|
+
export { buildBwrapArgs, buildSbplProfile, buildUnshareArgs, } from "./profile.js";
|
|
4
|
+
export { resolveExtras, resolveWritable, wrapCommand } from "./wrap.js";
|
|
5
|
+
export { DEFAULT_BASH_SANDBOX_MODE, DEFAULT_SANDBOX_NETWORK, NO_BASH_SANDBOX, bashSandboxPolicy, looksLikeSandboxDenial, resolveBashSandbox, runsSandboxed, sandboxDenialHint, wantsUnsandboxed, wrapBashCommand, } from "./bash.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 操作系统沙箱的参数生成(docs/sandbox.md「各平台实现」):纯函数,不探测、不读文件系统。
|
|
3
|
+
*
|
|
4
|
+
* - `buildSbplProfile`:macOS `sandbox-exec -p` 的 SBPL 配置;
|
|
5
|
+
* - `buildBwrapArgs`:Linux bubblewrap 的参数(到 `--` 为止);
|
|
6
|
+
* - `buildUnshareArgs`:Linux `unshare -r -n`(只隔离网络)。
|
|
7
|
+
*
|
|
8
|
+
* 路径由调用方先取 realpath(wrap.ts);这里只做转义与校验。
|
|
9
|
+
*/
|
|
10
|
+
export type OsSandboxKind = "sandbox-exec" | "bwrap" | "unshare";
|
|
11
|
+
export interface OsSandboxPolicy {
|
|
12
|
+
/** deny:拒绝一切网络(含 Unix 域套接字与 DNS)。 */
|
|
13
|
+
network: "deny" | "allow";
|
|
14
|
+
/** 允许写入的目录(绝对路径,已 realpath);其余位置一律不可写。 */
|
|
15
|
+
writable: readonly string[];
|
|
16
|
+
/** 可写目录里仍然只读的路径(已 realpath;bash 用:项目 `.ama/`、`.git/hooks`、`.git/config`)。 */
|
|
17
|
+
readOnly?: readonly string[];
|
|
18
|
+
/** 不可读的目录(已 realpath;bash 用:`~/.ssh` 等凭据目录)。 */
|
|
19
|
+
hiddenDirs?: readonly string[];
|
|
20
|
+
/** 不可读的文件(已 realpath;bash 用:`~/.netrc`、ama 的 `auth.json` 等)。 */
|
|
21
|
+
hiddenFiles?: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
/** 沙箱里始终可写的设备文件(stdio 是继承的描述符,不经路径检查)。 */
|
|
24
|
+
export declare const SBPL_DEVICE_WRITES: readonly ["/dev/null", "/dev/zero", "/dev/tty", "/dev/dtracehelper"];
|
|
25
|
+
/** 含控制字符的路径不进配置(SBPL 字符串 / 命令行里都难以安全表达)。 */
|
|
26
|
+
export declare function assertSafePath(path: string): void;
|
|
27
|
+
/** SBPL 字符串字面量:转义反斜杠与双引号。 */
|
|
28
|
+
export declare function sbplString(value: string): string;
|
|
29
|
+
export declare function buildSbplProfile(policy: OsSandboxPolicy): string;
|
|
30
|
+
/** bwrap 参数(不含可执行文件与目标命令):只读根 + 最小 /dev + 可写目录逐个绑回。 */
|
|
31
|
+
export declare function buildBwrapArgs(policy: OsSandboxPolicy): string[];
|
|
32
|
+
/** unshare 参数(不含可执行文件与目标命令):只隔离网络,不限制写入。 */
|
|
33
|
+
export declare function buildUnshareArgs(): string[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 操作系统沙箱的参数生成(docs/sandbox.md「各平台实现」):纯函数,不探测、不读文件系统。
|
|
3
|
+
*
|
|
4
|
+
* - `buildSbplProfile`:macOS `sandbox-exec -p` 的 SBPL 配置;
|
|
5
|
+
* - `buildBwrapArgs`:Linux bubblewrap 的参数(到 `--` 为止);
|
|
6
|
+
* - `buildUnshareArgs`:Linux `unshare -r -n`(只隔离网络)。
|
|
7
|
+
*
|
|
8
|
+
* 路径由调用方先取 realpath(wrap.ts);这里只做转义与校验。
|
|
9
|
+
*/
|
|
10
|
+
/** 沙箱里始终可写的设备文件(stdio 是继承的描述符,不经路径检查)。 */
|
|
11
|
+
export const SBPL_DEVICE_WRITES = [
|
|
12
|
+
"/dev/null",
|
|
13
|
+
"/dev/zero",
|
|
14
|
+
"/dev/tty",
|
|
15
|
+
"/dev/dtracehelper",
|
|
16
|
+
];
|
|
17
|
+
/** 含控制字符的路径不进配置(SBPL 字符串 / 命令行里都难以安全表达)。 */
|
|
18
|
+
export function assertSafePath(path) {
|
|
19
|
+
// eslint-disable-next-line no-control-regex
|
|
20
|
+
if (path === "" || /[\u0000-\u001f\u007f]/.test(path))
|
|
21
|
+
throw new Error(`sandbox: unsupported path ${JSON.stringify(path)}`);
|
|
22
|
+
}
|
|
23
|
+
/** SBPL 字符串字面量:转义反斜杠与双引号。 */
|
|
24
|
+
export function sbplString(value) {
|
|
25
|
+
assertSafePath(value);
|
|
26
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
27
|
+
}
|
|
28
|
+
export function buildSbplProfile(policy) {
|
|
29
|
+
const lines = ["(version 1)", "(allow default)"];
|
|
30
|
+
if (policy.network === "deny") {
|
|
31
|
+
lines.push("(deny network*)");
|
|
32
|
+
// DNS 经 mDNSResponder:Unix 套接字已被 network* 拒绝,mach 服务也拒绝,堵住经 DNS 查询外带数据。
|
|
33
|
+
lines.push('(deny mach-lookup (global-name "com.apple.dnssd.service"))');
|
|
34
|
+
}
|
|
35
|
+
lines.push("(deny file-write*)");
|
|
36
|
+
for (const dir of policy.writable)
|
|
37
|
+
lines.push(`(allow file-write* (subpath ${sbplString(dir)}))`);
|
|
38
|
+
const devices = SBPL_DEVICE_WRITES.map((d) => `(literal ${sbplString(d)})`).join(" ");
|
|
39
|
+
lines.push(`(allow file-write* ${devices} (regex #"^/dev/fd/"))`);
|
|
40
|
+
// SBPL 后出现的规则优先:只读与不可读放在最后,覆盖前面的允许。
|
|
41
|
+
for (const path of policy.readOnly ?? [])
|
|
42
|
+
lines.push(`(deny file-write* (subpath ${sbplString(path)}))`);
|
|
43
|
+
for (const path of [...(policy.hiddenDirs ?? []), ...(policy.hiddenFiles ?? [])])
|
|
44
|
+
lines.push(`(deny file-read* file-write* (subpath ${sbplString(path)}))`);
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
}
|
|
47
|
+
/** bwrap 参数(不含可执行文件与目标命令):只读根 + 最小 /dev + 可写目录逐个绑回。 */
|
|
48
|
+
export function buildBwrapArgs(policy) {
|
|
49
|
+
const args = ["--die-with-parent", "--ro-bind", "/", "/", "--dev", "/dev"];
|
|
50
|
+
if (policy.network === "deny")
|
|
51
|
+
args.push("--unshare-net");
|
|
52
|
+
for (const dir of policy.writable) {
|
|
53
|
+
assertSafePath(dir);
|
|
54
|
+
args.push("--bind", dir, dir);
|
|
55
|
+
}
|
|
56
|
+
// 之后的挂载盖住前面的:只读路径重新只读绑定,不可读目录换成空 tmpfs、文件换成 /dev/null。
|
|
57
|
+
// bwrap 只能挂到已存在的路径上,调用方只传存在的。
|
|
58
|
+
for (const path of policy.readOnly ?? []) {
|
|
59
|
+
assertSafePath(path);
|
|
60
|
+
args.push("--ro-bind", path, path);
|
|
61
|
+
}
|
|
62
|
+
for (const dir of policy.hiddenDirs ?? []) {
|
|
63
|
+
assertSafePath(dir);
|
|
64
|
+
args.push("--tmpfs", dir);
|
|
65
|
+
}
|
|
66
|
+
for (const file of policy.hiddenFiles ?? []) {
|
|
67
|
+
assertSafePath(file);
|
|
68
|
+
args.push("--ro-bind", "/dev/null", file);
|
|
69
|
+
}
|
|
70
|
+
args.push("--");
|
|
71
|
+
return args;
|
|
72
|
+
}
|
|
73
|
+
/** unshare 参数(不含可执行文件与目标命令):只隔离网络,不限制写入。 */
|
|
74
|
+
export function buildUnshareArgs() {
|
|
75
|
+
return ["-r", "-n", "--"];
|
|
76
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 包装命令行(docs/sandbox.md「分层」):输入命令、参数与策略 → 输出经 OS 沙箱启动的命令行。使用方自己
|
|
3
|
+
* spawn(进程组、stdio、环境都不变)。
|
|
4
|
+
*
|
|
5
|
+
* - 可写目录取 realpath(SBPL 按真实路径匹配,macOS 的 /tmp、/var 是符号链接;bwrap 绑定也要求存在),
|
|
6
|
+
* 不存在的丢弃;
|
|
7
|
+
* - `unshare` 只能隔离网络:策略要求联网时等于不包装;
|
|
8
|
+
* - `none`:原样返回(调用方按 `sandboxed: false` 决定是否继续);
|
|
9
|
+
* - 只读路径(`readOnly`):存在的取 realpath;不存在的按「父目录 realpath + 名字」只给 SBPL(挡住新建),
|
|
10
|
+
* bwrap 只能挂到已存在的路径,跳过;不可读路径(`hiddenDirs` / `hiddenFiles`)只保留存在的,并按实际
|
|
11
|
+
* 类型重新归类(bwrap 对目录挂 tmpfs、对文件挂 /dev/null)。
|
|
12
|
+
*/
|
|
13
|
+
import type { OsSandboxStatus } from "./detect.js";
|
|
14
|
+
import { type OsSandboxPolicy } from "./profile.js";
|
|
15
|
+
export interface WrappedCommand {
|
|
16
|
+
command: string;
|
|
17
|
+
args: string[];
|
|
18
|
+
/** 实际经 OS 沙箱启动。 */
|
|
19
|
+
sandboxed: boolean;
|
|
20
|
+
/** 本次包装拒绝了网络。 */
|
|
21
|
+
networkDenied: boolean;
|
|
22
|
+
/** 本次包装把写入限制在 `writable`。 */
|
|
23
|
+
writesRestricted: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** 可写目录 → realpath,去掉不存在的与重复的。 */
|
|
26
|
+
export declare function resolveWritable(dirs: readonly string[], realpath?: (path: string) => string): string[];
|
|
27
|
+
/** 解析只读与不可读路径(见文件头)。`missingOk`:不存在的只读路径按父目录解析保留(SBPL)。 */
|
|
28
|
+
export declare function resolveExtras(policy: OsSandboxPolicy, missingOk: boolean, realpath?: (path: string) => string, isDir?: (path: string) => boolean): Pick<OsSandboxPolicy, "readOnly" | "hiddenDirs" | "hiddenFiles">;
|
|
29
|
+
export declare function wrapCommand(status: Pick<OsSandboxStatus, "kind" | "path">, command: string, args: readonly string[], policy: OsSandboxPolicy, realpath?: (path: string) => string, isDir?: (path: string) => boolean): WrappedCommand;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 包装命令行(docs/sandbox.md「分层」):输入命令、参数与策略 → 输出经 OS 沙箱启动的命令行。使用方自己
|
|
3
|
+
* spawn(进程组、stdio、环境都不变)。
|
|
4
|
+
*
|
|
5
|
+
* - 可写目录取 realpath(SBPL 按真实路径匹配,macOS 的 /tmp、/var 是符号链接;bwrap 绑定也要求存在),
|
|
6
|
+
* 不存在的丢弃;
|
|
7
|
+
* - `unshare` 只能隔离网络:策略要求联网时等于不包装;
|
|
8
|
+
* - `none`:原样返回(调用方按 `sandboxed: false` 决定是否继续);
|
|
9
|
+
* - 只读路径(`readOnly`):存在的取 realpath;不存在的按「父目录 realpath + 名字」只给 SBPL(挡住新建),
|
|
10
|
+
* bwrap 只能挂到已存在的路径,跳过;不可读路径(`hiddenDirs` / `hiddenFiles`)只保留存在的,并按实际
|
|
11
|
+
* 类型重新归类(bwrap 对目录挂 tmpfs、对文件挂 /dev/null)。
|
|
12
|
+
*/
|
|
13
|
+
import { realpathSync, statSync } from "node:fs";
|
|
14
|
+
import { basename, dirname, join } from "node:path";
|
|
15
|
+
import { buildBwrapArgs, buildSbplProfile, buildUnshareArgs, } from "./profile.js";
|
|
16
|
+
/** 可写目录 → realpath,去掉不存在的与重复的。 */
|
|
17
|
+
export function resolveWritable(dirs, realpath = realpathSync) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const dir of dirs) {
|
|
20
|
+
let real;
|
|
21
|
+
try {
|
|
22
|
+
real = realpath(dir);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!out.includes(real))
|
|
28
|
+
out.push(real);
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
function isDirectory(path) {
|
|
33
|
+
try {
|
|
34
|
+
return statSync(path).isDirectory();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** 解析只读与不可读路径(见文件头)。`missingOk`:不存在的只读路径按父目录解析保留(SBPL)。 */
|
|
41
|
+
export function resolveExtras(policy, missingOk, realpath = realpathSync, isDir = isDirectory) {
|
|
42
|
+
const readOnly = [];
|
|
43
|
+
for (const path of policy.readOnly ?? []) {
|
|
44
|
+
let real;
|
|
45
|
+
try {
|
|
46
|
+
real = realpath(path);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
if (missingOk) {
|
|
50
|
+
try {
|
|
51
|
+
real = join(realpath(dirname(path)), basename(path));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
real = undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (real !== undefined && !readOnly.includes(real))
|
|
59
|
+
readOnly.push(real);
|
|
60
|
+
}
|
|
61
|
+
const hidden = resolveWritable([...(policy.hiddenDirs ?? []), ...(policy.hiddenFiles ?? [])], realpath);
|
|
62
|
+
const out = {};
|
|
63
|
+
if (readOnly.length > 0)
|
|
64
|
+
out.readOnly = readOnly;
|
|
65
|
+
const dirs = hidden.filter((p) => isDir(p));
|
|
66
|
+
const files = hidden.filter((p) => !dirs.includes(p));
|
|
67
|
+
if (dirs.length > 0)
|
|
68
|
+
out.hiddenDirs = dirs;
|
|
69
|
+
if (files.length > 0)
|
|
70
|
+
out.hiddenFiles = files;
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
export function wrapCommand(status, command, args, policy, realpath, isDir) {
|
|
74
|
+
const plain = {
|
|
75
|
+
command,
|
|
76
|
+
args: [...args],
|
|
77
|
+
sandboxed: false,
|
|
78
|
+
networkDenied: false,
|
|
79
|
+
writesRestricted: false,
|
|
80
|
+
};
|
|
81
|
+
if (status.kind === "none" || status.path === undefined)
|
|
82
|
+
return plain;
|
|
83
|
+
const resolved = {
|
|
84
|
+
network: policy.network,
|
|
85
|
+
writable: resolveWritable(policy.writable, realpath),
|
|
86
|
+
...resolveExtras(policy, status.kind === "sandbox-exec", realpath, isDir),
|
|
87
|
+
};
|
|
88
|
+
const networkDenied = policy.network === "deny";
|
|
89
|
+
switch (status.kind) {
|
|
90
|
+
case "sandbox-exec":
|
|
91
|
+
return {
|
|
92
|
+
command: status.path,
|
|
93
|
+
args: ["-p", buildSbplProfile(resolved), command, ...args],
|
|
94
|
+
sandboxed: true,
|
|
95
|
+
networkDenied,
|
|
96
|
+
writesRestricted: true,
|
|
97
|
+
};
|
|
98
|
+
case "bwrap":
|
|
99
|
+
return {
|
|
100
|
+
command: status.path,
|
|
101
|
+
args: [...buildBwrapArgs(resolved), command, ...args],
|
|
102
|
+
sandboxed: true,
|
|
103
|
+
networkDenied,
|
|
104
|
+
writesRestricted: true,
|
|
105
|
+
};
|
|
106
|
+
case "unshare":
|
|
107
|
+
if (!networkDenied)
|
|
108
|
+
return plain;
|
|
109
|
+
return {
|
|
110
|
+
command: status.path,
|
|
111
|
+
args: [...buildUnshareArgs(), command, ...args],
|
|
112
|
+
sandboxed: true,
|
|
113
|
+
networkDenied: true,
|
|
114
|
+
writesRestricted: false,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
package/dist/sdk.d.ts
CHANGED
|
@@ -6,13 +6,20 @@
|
|
|
6
6
|
* 也可以直接给 `argv`(与 `ama` 命令行同语法)。
|
|
7
7
|
* - `createAgentSession(options)`:不读文件系统配置的轻量装配——内存会话(或传入的
|
|
8
8
|
* SessionManager)、选定的工具、规则与回调审批,直接返回 `AgentSessionImpl`
|
|
9
|
-
* (含 `addTool / updateSystem / navigate`
|
|
9
|
+
* (含 `addTool / updateSystem / navigate` 等扩展方法;回滚 `rewindPoints / rewind /
|
|
10
|
+
* summarizeFrom / summarizeUpTo / undoAbortedTurn`,内存会话只能仅对话)。
|
|
10
11
|
*
|
|
11
12
|
* 两者共用组装根(cli/compose*.ts),审批链、系统提示装配、工具预设与缓存行为与 CLI 一致。
|
|
13
|
+
* - [W5-F] 计划(docs/plan.md):`CreateSessionOptions.plan`(plan.* 配置 + `onProposed` 审批回调);
|
|
14
|
+
* 返回的会话带 `plan.current() / respond() / todos()`。没有 `onProposed` 时按 `plan.unattended`
|
|
15
|
+
* (缺省 stop:计划落盘后停下,不替人批准)。
|
|
12
16
|
*/
|
|
13
17
|
import type { ApiRegistry } from "./ai/apis/api.js";
|
|
14
18
|
import type { ModelThinkingLevel, ProviderData } from "./ai/types.js";
|
|
15
19
|
import type { AgentSessionImpl } from "./agent/session.js";
|
|
20
|
+
import { type PlanDecision, type PlanResponse, type PlanResponseResult } from "./agent/session-plan.js";
|
|
21
|
+
import type { PlanData, TodoItemView } from "./agent/types.js";
|
|
22
|
+
import type { PlanConfig } from "./config/types-w5.js";
|
|
16
23
|
import { type LogFn } from "./cli/compose-session.js";
|
|
17
24
|
import { type ComposeOptions } from "./cli/compose.js";
|
|
18
25
|
import type { Runtime } from "./cli/runtime.js";
|
|
@@ -24,6 +31,8 @@ import { SessionManager } from "./session/manager.js";
|
|
|
24
31
|
import type { ToolDefinition } from "./tools/types.js";
|
|
25
32
|
/** [W3-C2] 统计类型:`session.getStats()`、RPC `get_session_stats`、`-p --output-format json` 的 `cache`。 */
|
|
26
33
|
export type { SessionCacheStats, SessionStats } from "./agent/types.js";
|
|
34
|
+
/** [RW-B] 回滚:`session.rewindPoints()`、`session.rewind()`、`summarizeFrom / summarizeUpTo`。 */
|
|
35
|
+
export type { CodeRestoreResult, RewindMode, RewindPoint, RewindRequest, RewindResult, } from "./checkpoints/types.js";
|
|
27
36
|
export type { CacheMiss, CacheMissReason, CacheReporting, WarmerStatus, WarmingMode, } from "./ai/cache/types.js";
|
|
28
37
|
export interface RuntimeOptions {
|
|
29
38
|
cwd?: string;
|
|
@@ -46,6 +55,26 @@ export interface RuntimeOptions {
|
|
|
46
55
|
compose?: ComposeOptions;
|
|
47
56
|
}
|
|
48
57
|
export declare function createRuntime(options?: RuntimeOptions): Promise<Runtime>;
|
|
58
|
+
/** [W5-F] 计划:类型与 RPC 同形(`plan_proposed` / `plan_response`)。 */
|
|
59
|
+
export type { PlanData, PlanDecision, PlanResponse, PlanResponseResult, TodoItemView };
|
|
60
|
+
/** [W5-F] `CreateSessionOptions.plan`:plan.* 配置 + 审批回调。 */
|
|
61
|
+
export interface SessionPlanOptions extends PlanConfig {
|
|
62
|
+
/**
|
|
63
|
+
* 计划待审批时调用(运行结束后;不阻塞运行)。返回回答即生效;返回 undefined 留待
|
|
64
|
+
* `session.plan.respond()`。不给时按 `unattended`(缺省 stop)。
|
|
65
|
+
*/
|
|
66
|
+
onProposed?(plan: PlanData): Promise<PlanDecision | undefined>;
|
|
67
|
+
}
|
|
68
|
+
/** [W5-F] `session.plan`。 */
|
|
69
|
+
export interface SessionPlanApi {
|
|
70
|
+
/** 分支上最近的计划;没有为 null。 */
|
|
71
|
+
current(): PlanData | null;
|
|
72
|
+
respond(response: PlanResponse): Promise<PlanResponseResult>;
|
|
73
|
+
todos(): TodoItemView[];
|
|
74
|
+
}
|
|
75
|
+
export type SdkAgentSession = AgentSessionImpl & {
|
|
76
|
+
readonly plan: SessionPlanApi;
|
|
77
|
+
};
|
|
49
78
|
export type SessionAuth = {
|
|
50
79
|
kind: "file";
|
|
51
80
|
path: string;
|
|
@@ -97,8 +126,10 @@ export interface CreateSessionOptions {
|
|
|
97
126
|
/** 命令式 Hook(缺省不加载文件系统里的 hooks.json)。 */
|
|
98
127
|
hooks?: HookConfig | false;
|
|
99
128
|
config?: Partial<AmaConfig>;
|
|
129
|
+
/** [W5-F] 计划(plan.* 配置与审批回调),见 docs/plan.md。 */
|
|
130
|
+
plan?: SessionPlanOptions;
|
|
100
131
|
unattended?: boolean;
|
|
101
132
|
onWarning?: (message: string) => void;
|
|
102
133
|
log?: LogFn;
|
|
103
134
|
}
|
|
104
|
-
export declare function createAgentSession(options?: CreateSessionOptions): Promise<
|
|
135
|
+
export declare function createAgentSession(options?: CreateSessionOptions): Promise<SdkAgentSession>;
|
package/dist/sdk.js
CHANGED
|
@@ -6,12 +6,17 @@
|
|
|
6
6
|
* 也可以直接给 `argv`(与 `ama` 命令行同语法)。
|
|
7
7
|
* - `createAgentSession(options)`:不读文件系统配置的轻量装配——内存会话(或传入的
|
|
8
8
|
* SessionManager)、选定的工具、规则与回调审批,直接返回 `AgentSessionImpl`
|
|
9
|
-
* (含 `addTool / updateSystem / navigate`
|
|
9
|
+
* (含 `addTool / updateSystem / navigate` 等扩展方法;回滚 `rewindPoints / rewind /
|
|
10
|
+
* summarizeFrom / summarizeUpTo / undoAbortedTurn`,内存会话只能仅对话)。
|
|
10
11
|
*
|
|
11
12
|
* 两者共用组装根(cli/compose*.ts),审批链、系统提示装配、工具预设与缓存行为与 CLI 一致。
|
|
13
|
+
* - [W5-F] 计划(docs/plan.md):`CreateSessionOptions.plan`(plan.* 配置 + `onProposed` 审批回调);
|
|
14
|
+
* 返回的会话带 `plan.current() / respond() / todos()`。没有 `onProposed` 时按 `plan.unattended`
|
|
15
|
+
* (缺省 stop:计划落盘后停下,不替人批准)。
|
|
12
16
|
*/
|
|
13
17
|
import { resolve } from "node:path";
|
|
14
18
|
import { ProviderRegistry } from "./ai/providers/registry.js";
|
|
19
|
+
import { planController, } from "./agent/session-plan.js";
|
|
15
20
|
import { parseArgs } from "./cli/args.js";
|
|
16
21
|
import { bootstrap } from "./cli/bootstrap.js";
|
|
17
22
|
import { withExtraProviders } from "./cli/compose-providers.js";
|
|
@@ -81,6 +86,9 @@ export async function createAgentSession(options = {}) {
|
|
|
81
86
|
const cwd = resolve(options.cwd ?? process.cwd());
|
|
82
87
|
const warn = options.onWarning ?? (() => undefined);
|
|
83
88
|
let config = mergeConfig(DEFAULT_CONFIG, options.config);
|
|
89
|
+
const { onProposed, ...planConfig } = options.plan ?? {};
|
|
90
|
+
if (options.plan !== undefined)
|
|
91
|
+
config = { ...config, plan: { ...config.plan, ...planConfig } };
|
|
84
92
|
if (options.toolsPreset !== undefined)
|
|
85
93
|
config = mergeConfig(config, { tools: { preset: options.toolsPreset } });
|
|
86
94
|
const providers = new ProviderRegistry({
|
|
@@ -199,5 +207,19 @@ export async function createAgentSession(options = {}) {
|
|
|
199
207
|
warn,
|
|
200
208
|
};
|
|
201
209
|
session = composeSession(assembly, state, { log: options.log ?? (() => undefined) });
|
|
202
|
-
return session;
|
|
210
|
+
return withPlanApi(session, onProposed);
|
|
211
|
+
}
|
|
212
|
+
/** SDK 会话:审批交给 `onProposed`(没有则无人值守策略),并挂上 `session.plan`。 */
|
|
213
|
+
function withPlanApi(session, onProposed) {
|
|
214
|
+
const controller = planController(session);
|
|
215
|
+
controller?.setAttendance(onProposed === undefined ? "unattended" : "callback", onProposed);
|
|
216
|
+
const missing = () => {
|
|
217
|
+
throw new AmaError("not_implemented", "该会话没有装配 plan 扩展");
|
|
218
|
+
};
|
|
219
|
+
const plan = {
|
|
220
|
+
current: () => controller?.current() ?? null,
|
|
221
|
+
respond: (response) => (controller ?? missing()).respond(response),
|
|
222
|
+
todos: () => controller?.todos() ?? [],
|
|
223
|
+
};
|
|
224
|
+
return Object.assign(session, { plan });
|
|
203
225
|
}
|
package/dist/session/types.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* `append()` 同步:返回带 id 的条目并把叶子移到它(落盘可延迟到首条提示,§11.1 第 16 步)。
|
|
15
15
|
* - (W3-C0)第三波 §1.7 / A7:新增 `usage` 条目(保温等不进上下文的请求用量,计入统计)与
|
|
16
16
|
* 非条目的 `leaf` 行(`/tree` 位置落盘);都是 v1 可选行,格式版本不升。
|
|
17
|
+
* - (W5-C0)`context_edit.reason` 加 `image_budget`(第五波 §4);格式版本不升。
|
|
17
18
|
*/
|
|
18
19
|
import type { ContentBlock, Message, ModelThinkingLevel, Usage } from "../ai/types.js";
|
|
19
20
|
export declare const SESSION_FORMAT_VERSION: 1;
|
|
@@ -60,7 +61,8 @@ export interface BranchSummaryEntry extends EntryBase {
|
|
|
60
61
|
usage?: Usage;
|
|
61
62
|
details?: FileOpsDetails;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
+
/** [W5-C0] `image_budget`:单请求图片总量超预算时把旧图换成占位文本(wave5-plan §4)。 */
|
|
65
|
+
export type ContextEditReason = "prune" | "abort" | "retry" | "overflow" | "manual" | "image_budget";
|
|
64
66
|
/** 同一目标最新一条赢;replacement 为 null 表示从上下文剔除。 */
|
|
65
67
|
export interface ContextEditEntry extends EntryBase {
|
|
66
68
|
type: "context_edit";
|
package/dist/session/types.js
CHANGED
|
@@ -14,5 +14,6 @@
|
|
|
14
14
|
* `append()` 同步:返回带 id 的条目并把叶子移到它(落盘可延迟到首条提示,§11.1 第 16 步)。
|
|
15
15
|
* - (W3-C0)第三波 §1.7 / A7:新增 `usage` 条目(保温等不进上下文的请求用量,计入统计)与
|
|
16
16
|
* 非条目的 `leaf` 行(`/tree` 位置落盘);都是 v1 可选行,格式版本不升。
|
|
17
|
+
* - (W5-C0)`context_edit.reason` 加 `image_budget`(第五波 §4);格式版本不升。
|
|
17
18
|
*/
|
|
18
19
|
export const SESSION_FORMAT_VERSION = 1;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内置 Skill(替代把 ama 自身文档塞进系统提示)。
|
|
3
|
+
*
|
|
4
|
+
* 系统提示里只常驻索引的一条(名字 + 一句描述,约 60 token),正文按需读取。单文件 bundle 没有
|
|
5
|
+
* docs 目录,所以正文是内联的配置速查(≤ 3 KB),会话发现时写到 `<dataDir>/builtin/<名>.md`(路径短,进前缀)
|
|
6
|
+
* (内容不变不重写),模型用 read 读、用户用 `/skill:ama-docs` 调用都走现成路径。
|
|
7
|
+
*
|
|
8
|
+
* - 排在全部用户 / 项目 Skill 之后:同名时用户的优先,内置的静默让位(不报重名 warning)。
|
|
9
|
+
* - 路径只取决于数据目录,会话开始时确定、会话内不变,不影响前缀稳定(设计 §9.1)。
|
|
10
|
+
* - 写盘失败只记 warning 并跳过该内置 Skill。
|
|
11
|
+
*/
|
|
12
|
+
import type { Skill } from "./discover.js";
|
|
13
|
+
export declare const BUILTIN_SKILLS_DIR = "builtin";
|
|
14
|
+
export interface BuiltinSkill {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
body: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const BUILTIN_SKILLS: readonly BuiltinSkill[];
|
|
20
|
+
export declare function builtinSkillPath(dataDir: string, name: string): string;
|
|
21
|
+
/** 是否内置 Skill(启动头的「已加载」只数用户 / 项目的)。 */
|
|
22
|
+
export declare function isBuiltinSkill(skill: {
|
|
23
|
+
name: string;
|
|
24
|
+
location: string;
|
|
25
|
+
}): boolean;
|
|
26
|
+
export declare function builtinSkillText(skill: BuiltinSkill): string;
|
|
27
|
+
/** 在已发现的 Skill 之后追加内置 Skill(同名让位给已有的);返回新数组与 warning。 */
|
|
28
|
+
export declare function withBuiltinSkills(skills: readonly Skill[], dataDir: string, builtins?: readonly BuiltinSkill[]): Promise<{
|
|
29
|
+
skills: Skill[];
|
|
30
|
+
warnings: string[];
|
|
31
|
+
}>;
|