@armadra/agent 0.3.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 +257 -0
- package/README.md +263 -95
- 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/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.d.ts +5 -0
- package/dist/agent/session-cache.js +18 -2
- package/dist/agent/session-classifier.d.ts +18 -0
- package/dist/agent/session-classifier.js +105 -0
- package/dist/agent/session-compaction.d.ts +35 -6
- package/dist/agent/session-compaction.js +157 -31
- package/dist/agent/session-core.d.ts +40 -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 +63 -3
- package/dist/agent/session.d.ts +33 -29
- package/dist/agent/session.js +91 -126
- 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 +72 -6
- package/dist/agent/types-w5.d.ts +192 -0
- package/dist/agent/types-w5.js +10 -0
- package/dist/agent/types.d.ts +55 -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-messages.js +3 -2
- 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/google-generative-ai.js +3 -2
- package/dist/ai/apis/openai-compat.d.ts +4 -1
- package/dist/ai/apis/openai-compat.js +8 -1
- package/dist/ai/apis/openai-completions.js +3 -2
- package/dist/ai/apis/openai-responses.js +3 -2
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- 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 +28 -11
- package/dist/ai/providers/registry.js +112 -65
- package/dist/ai/providers/suggest.d.ts +18 -0
- package/dist/ai/providers/suggest.js +72 -0
- package/dist/ai/sse.d.ts +6 -2
- package/dist/ai/sse.js +22 -2
- package/dist/ai/types.d.ts +37 -4
- package/dist/ai/types.js +5 -0
- package/dist/bundle/ama.cjs +41979 -21147
- 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 +26 -7
- package/dist/cli/args.js +75 -82
- package/dist/cli/bootstrap.js +40 -4
- package/dist/cli/codemode-notice.d.ts +21 -0
- package/dist/cli/codemode-notice.js +56 -0
- 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 +8 -0
- package/dist/cli/compose-session.js +76 -2
- package/dist/cli/compose-store.d.ts +1 -1
- package/dist/cli/compose-store.js +3 -1
- package/dist/cli/compose.d.ts +17 -4
- package/dist/cli/compose.js +60 -14
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +42 -0
- package/dist/cli/exit-codes.d.ts +12 -0
- package/dist/cli/exit-codes.js +15 -0
- package/dist/cli/fake-visibility.d.ts +13 -0
- package/dist/cli/fake-visibility.js +26 -0
- package/dist/cli/from-prompt.d.ts +18 -0
- package/dist/cli/from-prompt.js +49 -0
- package/dist/cli/help-text.d.ts +4 -0
- package/dist/cli/help-text.js +102 -0
- package/dist/cli/main.d.ts +9 -2
- package/dist/cli/main.js +77 -3
- package/dist/cli/proxy.d.ts +51 -0
- package/dist/cli/proxy.js +135 -0
- package/dist/cli/startup-screen.d.ts +29 -0
- package/dist/cli/startup-screen.js +51 -2
- package/dist/cli/startup-steps.d.ts +1 -1
- package/dist/cli/startup-steps.js +21 -13
- package/dist/cli/subcommands/config.d.ts +22 -3
- package/dist/cli/subcommands/config.js +119 -21
- package/dist/cli/subcommands/context.js +4 -1
- package/dist/cli/subcommands/doctor.js +33 -1
- package/dist/cli/subcommands/init.js +2 -1
- package/dist/cli/subcommands/models-discover.d.ts +15 -9
- package/dist/cli/subcommands/models-discover.js +58 -52
- package/dist/cli/subcommands/models.d.ts +2 -0
- package/dist/cli/subcommands/models.js +33 -14
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-probe.d.ts +34 -0
- package/dist/cli/subcommands/providers-probe.js +87 -0
- package/dist/cli/subcommands/providers.d.ts +3 -2
- package/dist/cli/subcommands/providers.js +53 -51
- package/dist/cli/subcommands/sessions-export.d.ts +10 -0
- package/dist/cli/subcommands/sessions-export.js +59 -0
- package/dist/cli/subcommands/sessions-search.d.ts +13 -0
- package/dist/cli/subcommands/sessions-search.js +103 -0
- package/dist/cli/subcommands/sessions.d.ts +6 -3
- package/dist/cli/subcommands/sessions.js +58 -3
- package/dist/cli/subcommands/stats.d.ts +17 -0
- package/dist/cli/subcommands/stats.js +198 -0
- package/dist/cli/system-prompt-arg.d.ts +11 -0
- package/dist/cli/system-prompt-arg.js +34 -0
- 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/modes.d.ts +4 -11
- package/dist/codemode/modes.js +5 -27
- package/dist/codemode/tool.d.ts +18 -14
- package/dist/codemode/tool.js +76 -31
- 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/init.d.ts +6 -2
- package/dist/config/init.js +12 -5
- package/dist/config/json-schema.d.ts +1 -0
- package/dist/config/json-schema.js +98 -4
- package/dist/config/key-docs.d.ts +22 -0
- package/dist/config/key-docs.js +174 -0
- package/dist/config/merge.d.ts +12 -9
- package/dist/config/merge.js +102 -13
- 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 +91 -118
- package/dist/config/types-w5.d.ts +118 -0
- package/dist/config/types-w5.js +20 -0
- package/dist/config/types.d.ts +84 -7
- package/dist/config/types.js +29 -1
- 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 +61 -5
- 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 +40 -7
- package/dist/modes/interactive/approval-dialog.js +192 -35
- 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 +20 -3
- package/dist/modes/interactive/commands.js +75 -16
- 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 +16 -2
- package/dist/modes/interactive/interactive-mode.js +184 -202
- package/dist/modes/interactive/key-dispatch.d.ts +18 -2
- package/dist/modes/interactive/key-dispatch.js +53 -6
- package/dist/modes/interactive/line/line-mode.d.ts +3 -1
- package/dist/modes/interactive/line/line-mode.js +28 -9
- package/dist/modes/interactive/line/line-render.d.ts +6 -1
- package/dist/modes/interactive/line/line-render.js +66 -8
- package/dist/modes/interactive/message-view.d.ts +49 -10
- package/dist/modes/interactive/message-view.js +262 -46
- package/dist/modes/interactive/panels.d.ts +35 -0
- package/dist/modes/interactive/panels.js +157 -0
- package/dist/modes/interactive/pickers.d.ts +23 -2
- package/dist/modes/interactive/pickers.js +48 -15
- 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/run-indicator.d.ts +51 -0
- package/dist/modes/interactive/run-indicator.js +189 -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-header.d.ts +40 -0
- package/dist/modes/interactive/startup-header.js +169 -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 +107 -16
- package/dist/modes/interactive/status-bar.js +306 -81
- 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-summary.d.ts +46 -0
- package/dist/modes/interactive/tool-summary.js +218 -0
- package/dist/modes/interactive/tool-view.d.ts +58 -15
- package/dist/modes/interactive/tool-view.js +233 -145
- package/dist/modes/print/print-mode.d.ts +40 -4
- package/dist/modes/print/print-mode.js +161 -8
- package/dist/modes/rpc/commands.d.ts +9 -0
- package/dist/modes/rpc/commands.js +49 -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 +65 -0
- package/dist/permissions/auto-safe.js +547 -0
- package/dist/permissions/classifier.d.ts +68 -0
- package/dist/permissions/classifier.js +186 -0
- package/dist/permissions/dangerous.d.ts +5 -0
- package/dist/permissions/dangerous.js +1 -1
- package/dist/permissions/modes.d.ts +30 -0
- package/dist/permissions/modes.js +78 -0
- package/dist/permissions/pipeline.d.ts +72 -6
- package/dist/permissions/pipeline.js +306 -11
- package/dist/permissions/protected.d.ts +19 -0
- package/dist/permissions/protected.js +74 -0
- package/dist/permissions/readonly-bash.d.ts +30 -0
- package/dist/permissions/readonly-bash.js +118 -0
- package/dist/permissions/rules.js +3 -0
- package/dist/permissions/types.d.ts +92 -3
- package/dist/permissions/types.js +3 -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 +42 -5
- package/dist/sdk.js +34 -4
- package/dist/session/export.d.ts +32 -0
- package/dist/session/export.js +187 -0
- package/dist/session/redact.d.ts +15 -0
- package/dist/session/redact.js +55 -0
- package/dist/session/reuse.d.ts +33 -0
- package/dist/session/reuse.js +86 -0
- package/dist/session/scan.d.ts +34 -0
- package/dist/session/scan.js +140 -0
- package/dist/session/search.d.ts +52 -0
- package/dist/session/search.js +211 -0
- package/dist/session/stats-aggregate.d.ts +63 -0
- package/dist/session/stats-aggregate.js +163 -0
- package/dist/session/stats-index.d.ts +26 -0
- package/dist/session/stats-index.js +91 -0
- package/dist/session/stats-scan.d.ts +54 -0
- package/dist/session/stats-scan.js +236 -0
- 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 +40 -8
- package/dist/tools/presets.js +65 -19
- 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/component.d.ts +8 -2
- package/dist/tui/component.js +3 -1
- package/dist/tui/components/box.d.ts +6 -1
- package/dist/tui/components/box.js +16 -6
- package/dist/tui/components/card.d.ts +23 -0
- package/dist/tui/components/card.js +37 -0
- package/dist/tui/components/editor-history.d.ts +6 -0
- package/dist/tui/components/editor-history.js +45 -0
- package/dist/tui/components/editor-paste.d.ts +1 -1
- package/dist/tui/components/editor-paste.js +4 -4
- package/dist/tui/components/editor.d.ts +11 -5
- package/dist/tui/components/editor.js +52 -58
- package/dist/tui/components/key-value.d.ts +3 -0
- package/dist/tui/components/key-value.js +16 -6
- package/dist/tui/components/loader.d.ts +37 -7
- package/dist/tui/components/loader.js +84 -21
- package/dist/tui/components/markdown.d.ts +5 -1
- package/dist/tui/components/markdown.js +45 -21
- package/dist/tui/components/meter.d.ts +3 -3
- package/dist/tui/components/meter.js +13 -11
- package/dist/tui/components/select-list.d.ts +23 -1
- package/dist/tui/components/select-list.js +76 -13
- package/dist/tui/glyphs.d.ts +72 -0
- package/dist/tui/glyphs.js +118 -0
- package/dist/tui/keybindings.d.ts +5 -0
- package/dist/tui/keybindings.js +5 -0
- package/dist/tui/theme.d.ts +30 -6
- package/dist/tui/theme.js +103 -17
- package/dist/tui.d.ts +4 -2
- package/dist/tui.js +3 -1
- package/docs/acp.md +78 -0
- package/docs/agents.md +250 -0
- package/docs/codemode.md +28 -10
- package/docs/hooks.md +36 -32
- package/docs/permissions.md +212 -0
- package/docs/plan.md +107 -0
- package/docs/providers.md +261 -62
- package/docs/rewind-plan.md +181 -0
- package/docs/rpc.md +127 -40
- package/docs/sandbox.md +215 -0
- package/docs/session-format.md +43 -22
- package/docs/sessions.md +174 -0
- package/docs/tui-design.md +751 -0
- package/docs/tui.md +260 -64
- package/package.json +15 -2
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话导出(`ama sessions export`):md(给人读)/ json(结构化)/ jsonl(原格式子集)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - 范围:`leaf` = 根 → 当前叶子的分支(同 `/tree` 当前位置),`all` = 文件里全部条目(文件顺序)。
|
|
5
|
+
* - 三种格式都先脱敏(redact.ts):json / jsonl 对每个字符串值做(图片 base64 保留),md 对成文做。
|
|
6
|
+
* - md:用户 / 助手消息、工具调用(参数 JSON 截到 500 字符)与结果(截到 2000 字符)、压缩与分支摘要、
|
|
7
|
+
* 末尾用量合计;思考块与 system 消息不写;图片写成占位 `[图片 image/png]`。
|
|
8
|
+
* - jsonl:头 + 所选条目,形状与会话文件相同(可以再被 ama 读),不带 leaf 行。
|
|
9
|
+
*/
|
|
10
|
+
import { contentImages, contentText, numberUserMessages } from "./reuse.js";
|
|
11
|
+
import { redactSecrets, redactValue } from "./redact.js";
|
|
12
|
+
import { indexEntries, pathToRoot } from "./tree.js";
|
|
13
|
+
export function selectEntries(input, branch) {
|
|
14
|
+
return branch === "all"
|
|
15
|
+
? [...input.entries]
|
|
16
|
+
: pathToRoot(indexEntries(input.entries), input.leaf);
|
|
17
|
+
}
|
|
18
|
+
export function usageTotals(entries) {
|
|
19
|
+
const totals = { requests: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
20
|
+
let cost = 0;
|
|
21
|
+
let costed = 0;
|
|
22
|
+
const add = (usage) => {
|
|
23
|
+
if (usage === undefined)
|
|
24
|
+
return;
|
|
25
|
+
totals.requests++;
|
|
26
|
+
totals.input += usage.input;
|
|
27
|
+
totals.output += usage.output;
|
|
28
|
+
totals.cacheRead += usage.cacheRead;
|
|
29
|
+
totals.cacheWrite += usage.cacheWrite;
|
|
30
|
+
if (usage.cost !== undefined) {
|
|
31
|
+
cost += usage.cost.total;
|
|
32
|
+
costed++;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
if (entry.type === "message" && entry.message.role === "assistant")
|
|
37
|
+
add(entry.message.usage);
|
|
38
|
+
else if (entry.type === "usage")
|
|
39
|
+
add(entry.usage);
|
|
40
|
+
else if (entry.type === "compaction" || entry.type === "branch_summary")
|
|
41
|
+
add(entry.usage);
|
|
42
|
+
}
|
|
43
|
+
if (costed > 0)
|
|
44
|
+
totals.cost = cost;
|
|
45
|
+
return totals;
|
|
46
|
+
}
|
|
47
|
+
function sessionName(entries) {
|
|
48
|
+
let name;
|
|
49
|
+
for (const entry of entries)
|
|
50
|
+
if (entry.type === "session_info")
|
|
51
|
+
name = entry.name;
|
|
52
|
+
return name;
|
|
53
|
+
}
|
|
54
|
+
function truncate(text, max) {
|
|
55
|
+
return text.length > max ? `${text.slice(0, max)}\n…(截断,原长 ${text.length} 字符)` : text;
|
|
56
|
+
}
|
|
57
|
+
function fence(text, lang = "") {
|
|
58
|
+
let ticks = "```";
|
|
59
|
+
while (text.includes(ticks))
|
|
60
|
+
ticks += "`";
|
|
61
|
+
return `${ticks}${lang}\n${text}\n${ticks}`;
|
|
62
|
+
}
|
|
63
|
+
function time(iso) {
|
|
64
|
+
return iso.replace("T", " ").replace(/\.\d+Z$|Z$/, "");
|
|
65
|
+
}
|
|
66
|
+
function imagesNote(content) {
|
|
67
|
+
return contentImages(content)
|
|
68
|
+
.map((image) => `[图片 ${image.mimeType}]`)
|
|
69
|
+
.join(" ");
|
|
70
|
+
}
|
|
71
|
+
export function renderMarkdown(input, branch) {
|
|
72
|
+
const entries = selectEntries(input, branch);
|
|
73
|
+
const numbers = new Map(numberUserMessages(input.entries).map((u) => [u.entryId, u.n]));
|
|
74
|
+
const name = sessionName(input.entries);
|
|
75
|
+
const out = [
|
|
76
|
+
`# ${name ?? `会话 ${input.header.id.slice(0, 8)}`}`,
|
|
77
|
+
"",
|
|
78
|
+
`- id:${input.header.id}`,
|
|
79
|
+
`- 目录:${input.header.cwd}`,
|
|
80
|
+
`- 创建:${time(input.header.timestamp)}`,
|
|
81
|
+
`- 范围:${branch === "all" ? "全部条目" : "当前分支"}(${entries.length} 条条目)`,
|
|
82
|
+
...(input.header.parentSession !== undefined
|
|
83
|
+
? [`- 来源会话:${input.header.parentSession}`]
|
|
84
|
+
: []),
|
|
85
|
+
"",
|
|
86
|
+
];
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
switch (entry.type) {
|
|
89
|
+
case "message": {
|
|
90
|
+
const m = entry.message;
|
|
91
|
+
if (m.role === "user") {
|
|
92
|
+
const n = numbers.get(entry.id);
|
|
93
|
+
const origin = m.origin;
|
|
94
|
+
out.push(`## 用户${n !== undefined ? ` #${n}` : ""}${origin !== undefined ? `(${origin})` : ""} · ${time(entry.timestamp)}`, "");
|
|
95
|
+
const text = contentText(m.content);
|
|
96
|
+
if (text !== "")
|
|
97
|
+
out.push(text, "");
|
|
98
|
+
const images = imagesNote(m.content);
|
|
99
|
+
if (images !== "")
|
|
100
|
+
out.push(images, "");
|
|
101
|
+
}
|
|
102
|
+
else if (m.role === "assistant") {
|
|
103
|
+
out.push(`## 助手 · ${m.provider}/${m.model} · ${time(entry.timestamp)}`, "");
|
|
104
|
+
for (const block of m.content) {
|
|
105
|
+
if (block.type === "text" && block.text.trim() !== "")
|
|
106
|
+
out.push(block.text, "");
|
|
107
|
+
if (block.type === "toolCall") {
|
|
108
|
+
out.push(`**工具调用** \`${block.name}\``, "", fence(truncate(JSON.stringify(block.arguments, null, 2), 500), "json"), "");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (m.stopReason === "error" || m.stopReason === "aborted") {
|
|
112
|
+
out.push(`> 停止:${m.stopReason}${m.errorMessage !== undefined ? ` · ${m.errorMessage}` : ""}`, "");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (m.role === "toolResult") {
|
|
116
|
+
out.push(`**结果** \`${m.toolName}\`${m.isError ? "(出错)" : ""}`, "", fence(truncate(contentText(m.content), 2000)), "");
|
|
117
|
+
const images = imagesNote(m.content);
|
|
118
|
+
if (images !== "")
|
|
119
|
+
out.push(images, "");
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "compaction":
|
|
124
|
+
out.push(`## 压缩摘要 · ${time(entry.timestamp)}`, "", entry.summary, "");
|
|
125
|
+
break;
|
|
126
|
+
case "branch_summary":
|
|
127
|
+
out.push(`## 分支摘要 · ${time(entry.timestamp)}`, "", entry.summary, "");
|
|
128
|
+
break;
|
|
129
|
+
case "custom_message":
|
|
130
|
+
if (entry.display) {
|
|
131
|
+
out.push(`## ${entry.customType} · ${time(entry.timestamp)}`, "");
|
|
132
|
+
out.push(contentText(entry.content), "");
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
case "model_change":
|
|
136
|
+
out.push(`_切换模型:${entry.provider}/${entry.modelId}${entry.channel !== undefined ? `@${entry.channel}` : ""}_`, "");
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const usage = usageTotals(entries);
|
|
143
|
+
out.push("## 用量", "", "| 请求 | 输入 | 输出 | 缓存读 | 缓存写 | 费用 |", "| ---: | ---: | ---: | ---: | ---: | ---: |", `| ${usage.requests} | ${usage.input} | ${usage.output} | ${usage.cacheRead} | ${usage.cacheWrite} | ${usage.cost !== undefined ? `$${usage.cost.toFixed(4)}` : "—"} |`, "");
|
|
144
|
+
return redactSecrets(out.join("\n"));
|
|
145
|
+
}
|
|
146
|
+
export function renderJson(input, branch) {
|
|
147
|
+
const entries = selectEntries(input, branch);
|
|
148
|
+
const selected = new Set(entries.map((e) => e.id));
|
|
149
|
+
const name = sessionName(input.entries);
|
|
150
|
+
const session = {
|
|
151
|
+
id: input.header.id,
|
|
152
|
+
cwd: input.header.cwd,
|
|
153
|
+
createdAt: input.header.timestamp,
|
|
154
|
+
agent: input.header.agent,
|
|
155
|
+
};
|
|
156
|
+
if (name !== undefined)
|
|
157
|
+
session["name"] = name;
|
|
158
|
+
if (input.header.parentSession !== undefined)
|
|
159
|
+
session["parentSession"] = input.header.parentSession;
|
|
160
|
+
const doc = {
|
|
161
|
+
format: "ama.session-export",
|
|
162
|
+
version: 1,
|
|
163
|
+
session,
|
|
164
|
+
branch,
|
|
165
|
+
leafId: input.leaf,
|
|
166
|
+
userMessages: numberUserMessages(input.entries)
|
|
167
|
+
.filter((u) => selected.has(u.entryId))
|
|
168
|
+
.map((u) => ({ n: u.n, entryId: u.entryId })),
|
|
169
|
+
usage: usageTotals(entries),
|
|
170
|
+
entries,
|
|
171
|
+
};
|
|
172
|
+
return JSON.stringify(redactValue(doc), null, 2) + "\n";
|
|
173
|
+
}
|
|
174
|
+
export function renderJsonl(input, branch) {
|
|
175
|
+
const lines = [input.header, ...selectEntries(input, branch)].map((line) => JSON.stringify(redactValue(line)));
|
|
176
|
+
return lines.join("\n") + "\n";
|
|
177
|
+
}
|
|
178
|
+
export function renderExport(input, format, branch) {
|
|
179
|
+
switch (format) {
|
|
180
|
+
case "md":
|
|
181
|
+
return renderMarkdown(input, branch);
|
|
182
|
+
case "json":
|
|
183
|
+
return renderJson(input, branch);
|
|
184
|
+
case "jsonl":
|
|
185
|
+
return renderJsonl(input, branch);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 导出前的机密脱敏(`ama sessions export`)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* 只认「形态」:常见 key / token 前缀(sk-…、sk-ant-…、ghp_…、github_pat_…、xox?-…、AIza…、
|
|
5
|
+
* AKIA…、npm_…)、JWT、`Bearer` / `Basic` 凭据、PEM 私钥块,以及 `apiKey` / `secret` / `token` /
|
|
6
|
+
* `password` / `authorization` 之后紧跟 `:` 或 `=` 的值。命中的部分换成 `[REDACTED]`
|
|
7
|
+
* (键名与 Bearer 之类的前缀保留,便于读者知道那里原来有什么)。宁可多遮,不做熵判断。
|
|
8
|
+
*/
|
|
9
|
+
export declare const REDACTED = "[REDACTED]";
|
|
10
|
+
export declare function redactSecrets(text: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* 深拷贝并对全部字符串值脱敏;键名像机密(如 `apiKey`、`authToken`)的字符串值整段遮掉;
|
|
13
|
+
* 图片块的 base64 `data` 原样保留(不会是机密,替换会弄坏图片)。
|
|
14
|
+
*/
|
|
15
|
+
export declare function redactValue<T>(value: T): T;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 导出前的机密脱敏(`ama sessions export`)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* 只认「形态」:常见 key / token 前缀(sk-…、sk-ant-…、ghp_…、github_pat_…、xox?-…、AIza…、
|
|
5
|
+
* AKIA…、npm_…)、JWT、`Bearer` / `Basic` 凭据、PEM 私钥块,以及 `apiKey` / `secret` / `token` /
|
|
6
|
+
* `password` / `authorization` 之后紧跟 `:` 或 `=` 的值。命中的部分换成 `[REDACTED]`
|
|
7
|
+
* (键名与 Bearer 之类的前缀保留,便于读者知道那里原来有什么)。宁可多遮,不做熵判断。
|
|
8
|
+
*/
|
|
9
|
+
export const REDACTED = "[REDACTED]";
|
|
10
|
+
const TOKEN_PATTERNS = [
|
|
11
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
12
|
+
/\bsk-[A-Za-z0-9_-]{16,}/g,
|
|
13
|
+
/\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})/g,
|
|
14
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
|
|
15
|
+
/\bAIza[0-9A-Za-z_-]{30,}/g,
|
|
16
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
17
|
+
/\bnpm_[A-Za-z0-9]{30,}/g,
|
|
18
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
19
|
+
];
|
|
20
|
+
const SCHEME = /\b(Bearer|Basic)(\s+)[A-Za-z0-9._~+/=-]{12,}/g;
|
|
21
|
+
const ASSIGNMENT = /\b([A-Za-z0-9_-]*(?:api[_-]?key|apikey|secret|token|password|passwd|authorization))(["']?\s*[:=]\s*["']?)([^\s"',;}{)]{8,})/gi;
|
|
22
|
+
export function redactSecrets(text) {
|
|
23
|
+
let out = text;
|
|
24
|
+
for (const pattern of TOKEN_PATTERNS)
|
|
25
|
+
out = out.replace(pattern, REDACTED);
|
|
26
|
+
out = out.replace(SCHEME, (_m, scheme, space) => `${scheme}${space}${REDACTED}`);
|
|
27
|
+
out = out.replace(ASSIGNMENT, (match, key, sep, value) => value === REDACTED || value.startsWith(REDACTED) ? match : `${key}${sep}${REDACTED}`);
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
const SECRET_KEY = /(?:api[_-]?key|apikey|secret|token|password|passwd|authorization)$/i;
|
|
31
|
+
/**
|
|
32
|
+
* 深拷贝并对全部字符串值脱敏;键名像机密(如 `apiKey`、`authToken`)的字符串值整段遮掉;
|
|
33
|
+
* 图片块的 base64 `data` 原样保留(不会是机密,替换会弄坏图片)。
|
|
34
|
+
*/
|
|
35
|
+
export function redactValue(value) {
|
|
36
|
+
if (typeof value === "string")
|
|
37
|
+
return redactSecrets(value);
|
|
38
|
+
if (Array.isArray(value))
|
|
39
|
+
return value.map((item) => redactValue(item));
|
|
40
|
+
if (typeof value === "object" && value !== null) {
|
|
41
|
+
const record = value;
|
|
42
|
+
const image = record["type"] === "image";
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [key, item] of Object.entries(record)) {
|
|
45
|
+
if (image && key === "data")
|
|
46
|
+
out[key] = item;
|
|
47
|
+
else if (typeof item === "string" && item.length >= 8 && SECRET_KEY.test(key))
|
|
48
|
+
out[key] = REDACTED;
|
|
49
|
+
else
|
|
50
|
+
out[key] = redactValue(item);
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话复用:用户消息编号与 `--from <id>[#n]`。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - 编号 = 文件里第几条 `role: "user"` 的消息(1 起,含 steer / followUp / host 注入),与分支无关、
|
|
5
|
+
* 只追加所以稳定;`ama sessions show` 列出,`ama sessions search` 的命中也给出同样的编号。
|
|
6
|
+
* - `--from <id>`(不带 `#n`)取文件里最后一条用户消息。
|
|
7
|
+
* - 取出的是那条消息的文本(多个文本块用空行连接)与图片块。
|
|
8
|
+
*/
|
|
9
|
+
import type { ImageBlock } from "../ai/types.js";
|
|
10
|
+
import type { SessionEntry } from "./types.js";
|
|
11
|
+
export interface NumberedUserMessage {
|
|
12
|
+
n: number;
|
|
13
|
+
entryId: string;
|
|
14
|
+
timestamp: string;
|
|
15
|
+
text: string;
|
|
16
|
+
images: ImageBlock[];
|
|
17
|
+
origin?: string;
|
|
18
|
+
}
|
|
19
|
+
/** 内容 → 纯文本(文本块用空行连接;不含图片)。 */
|
|
20
|
+
export declare function contentText(content: unknown): string;
|
|
21
|
+
export declare function contentImages(content: unknown): ImageBlock[];
|
|
22
|
+
export declare function numberUserMessages(entries: readonly SessionEntry[]): NumberedUserMessage[];
|
|
23
|
+
/** `abc123#4` → `{ id: "abc123", n: 4 }`;`abc123` → `{ id }`。 */
|
|
24
|
+
export declare function parseFromSpec(spec: string): {
|
|
25
|
+
id: string;
|
|
26
|
+
n?: number;
|
|
27
|
+
};
|
|
28
|
+
export interface FromMessage extends NumberedUserMessage {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
file: string;
|
|
31
|
+
}
|
|
32
|
+
/** 在会话目录里找 `--from` 指向的用户消息;找不到会话 → session_not_found,编号越界 → 用法错误。 */
|
|
33
|
+
export declare function resolveFromMessage(root: string, spec: string, cwd?: string): FromMessage;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话复用:用户消息编号与 `--from <id>[#n]`。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - 编号 = 文件里第几条 `role: "user"` 的消息(1 起,含 steer / followUp / host 注入),与分支无关、
|
|
5
|
+
* 只追加所以稳定;`ama sessions show` 列出,`ama sessions search` 的命中也给出同样的编号。
|
|
6
|
+
* - `--from <id>`(不带 `#n`)取文件里最后一条用户消息。
|
|
7
|
+
* - 取出的是那条消息的文本(多个文本块用空行连接)与图片块。
|
|
8
|
+
*/
|
|
9
|
+
import { AmaError } from "../errors.js";
|
|
10
|
+
import { findSessionFileReadOnly, readSessionReadOnly } from "./scan.js";
|
|
11
|
+
/** 内容 → 纯文本(文本块用空行连接;不含图片)。 */
|
|
12
|
+
export function contentText(content) {
|
|
13
|
+
if (typeof content === "string")
|
|
14
|
+
return content;
|
|
15
|
+
if (!Array.isArray(content))
|
|
16
|
+
return "";
|
|
17
|
+
return content
|
|
18
|
+
.filter((block) => typeof block === "object" &&
|
|
19
|
+
block !== null &&
|
|
20
|
+
block.type === "text" &&
|
|
21
|
+
typeof block.text === "string")
|
|
22
|
+
.map((block) => block.text)
|
|
23
|
+
.join("\n\n");
|
|
24
|
+
}
|
|
25
|
+
export function contentImages(content) {
|
|
26
|
+
if (!Array.isArray(content))
|
|
27
|
+
return [];
|
|
28
|
+
return content.filter((block) => typeof block === "object" &&
|
|
29
|
+
block !== null &&
|
|
30
|
+
block.type === "image" &&
|
|
31
|
+
typeof block.data === "string");
|
|
32
|
+
}
|
|
33
|
+
export function numberUserMessages(entries) {
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
if (entry.type !== "message" || entry.message.role !== "user")
|
|
37
|
+
continue;
|
|
38
|
+
const message = entry.message;
|
|
39
|
+
const item = {
|
|
40
|
+
n: out.length + 1,
|
|
41
|
+
entryId: entry.id,
|
|
42
|
+
timestamp: entry.timestamp,
|
|
43
|
+
text: contentText(message.content),
|
|
44
|
+
images: contentImages(message.content),
|
|
45
|
+
};
|
|
46
|
+
if (typeof message.origin === "string")
|
|
47
|
+
item.origin = message.origin;
|
|
48
|
+
out.push(item);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/** `abc123#4` → `{ id: "abc123", n: 4 }`;`abc123` → `{ id }`。 */
|
|
53
|
+
export function parseFromSpec(spec) {
|
|
54
|
+
const at = spec.lastIndexOf("#");
|
|
55
|
+
if (at < 0) {
|
|
56
|
+
if (spec.trim() === "")
|
|
57
|
+
throw new AmaError("invalid_arguments", "--from 需要会话 id", { exitCode: 2 });
|
|
58
|
+
return { id: spec };
|
|
59
|
+
}
|
|
60
|
+
const id = spec.slice(0, at);
|
|
61
|
+
const raw = spec.slice(at + 1);
|
|
62
|
+
const n = Number(raw);
|
|
63
|
+
if (id === "" || !/^\d+$/.test(raw) || n < 1) {
|
|
64
|
+
throw new AmaError("invalid_arguments", `--from 应为 <会话 id> 或 <会话 id>#<编号>(收到 ${spec})`, {
|
|
65
|
+
exitCode: 2,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return { id, n };
|
|
69
|
+
}
|
|
70
|
+
/** 在会话目录里找 `--from` 指向的用户消息;找不到会话 → session_not_found,编号越界 → 用法错误。 */
|
|
71
|
+
export function resolveFromMessage(root, spec, cwd) {
|
|
72
|
+
const { id, n } = parseFromSpec(spec);
|
|
73
|
+
const file = findSessionFileReadOnly(root, id, cwd);
|
|
74
|
+
const session = readSessionReadOnly(file);
|
|
75
|
+
const messages = numberUserMessages(session.entries);
|
|
76
|
+
if (messages.length === 0) {
|
|
77
|
+
throw new AmaError("invalid_arguments", `会话 ${session.header.id} 没有用户消息`, {
|
|
78
|
+
exitCode: 2,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const picked = n === undefined ? messages.at(-1) : messages[n - 1];
|
|
82
|
+
if (picked === undefined) {
|
|
83
|
+
throw new AmaError("invalid_arguments", `会话 ${session.header.id} 只有 ${messages.length} 条用户消息(收到 #${n})`, { exitCode: 2 });
|
|
84
|
+
}
|
|
85
|
+
return { ...picked, sessionId: session.header.id, file };
|
|
86
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话目录的只读扫描(`ama stats`、`ama sessions search|export`、`--from`)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* 与 store.ts / manager.ts 的区别:这里**从不加锁、不修复、不写文件**——正在运行的会话也能读。
|
|
5
|
+
* - `sessionFilesInScope`:按 cwd(只看它的编码子目录)或全部子目录(跳过 `.trash`)列文件,最新在前;
|
|
6
|
+
* - `forEachLine`:按行回调(不先 split 出整个数组);
|
|
7
|
+
* - `lineType`:ama 写入的行以 `{"type":"…"` 开头(manager 的 append 把 type 放在第一个键),
|
|
8
|
+
* 据此不解析就能跳过不需要的行;其它程序写的行退回 JSON.parse;
|
|
9
|
+
* - `readSessionReadOnly`:头 + 条目 + 叶子(同 migrate.ts 的规则),末尾半行忽略、中间坏行抛错。
|
|
10
|
+
*/
|
|
11
|
+
import { type MigratedSession } from "./migrate.js";
|
|
12
|
+
import type { SessionLine } from "./types.js";
|
|
13
|
+
/** 根目录下全部 cwd 子目录(不含 trash)。 */
|
|
14
|
+
export declare function sessionSubdirs(root: string): string[];
|
|
15
|
+
/** 范围内的会话文件:给 cwd 只看该目录,否则全部;按文件名(创建时间)倒序。 */
|
|
16
|
+
export declare function sessionFilesInScope(root: string, cwd?: string): string[];
|
|
17
|
+
/** 按 id 或唯一前缀找会话文件;本 cwd 目录优先(同 compose-store 的规则,但只读、不 import 组装根)。 */
|
|
18
|
+
export declare function findSessionFileReadOnly(root: string, id: string, cwd?: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* 逐行回调(不含行尾 `\n` / `\r`,跳过空行);`last` 表示文件最后一行且没有换行结尾
|
|
21
|
+
* (可能是写入中途的半行)。回调返回 false 提前结束。
|
|
22
|
+
*/
|
|
23
|
+
export declare function forEachLine(file: string, visit: (line: string, index: number, last: boolean) => boolean | void): void;
|
|
24
|
+
/** ama 写的行:不解析就取出 type(与消息的 role,若紧随其后);不是这种形状时 undefined。 */
|
|
25
|
+
export declare function lineType(line: string): {
|
|
26
|
+
type: string;
|
|
27
|
+
role?: string;
|
|
28
|
+
} | undefined;
|
|
29
|
+
/** 解析一行;失败时:末尾半行 → undefined,其它 → 抛 session_corrupt。 */
|
|
30
|
+
export declare function parseLine(file: string, line: string, index: number, last: boolean): SessionLine | undefined;
|
|
31
|
+
/** 只读读取整个会话(不加锁、不修复):头、全部条目、叶子 id。 */
|
|
32
|
+
export declare function readSessionReadOnly(file: string): MigratedSession & {
|
|
33
|
+
leaf: string | null;
|
|
34
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话目录的只读扫描(`ama stats`、`ama sessions search|export`、`--from`)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* 与 store.ts / manager.ts 的区别:这里**从不加锁、不修复、不写文件**——正在运行的会话也能读。
|
|
5
|
+
* - `sessionFilesInScope`:按 cwd(只看它的编码子目录)或全部子目录(跳过 `.trash`)列文件,最新在前;
|
|
6
|
+
* - `forEachLine`:按行回调(不先 split 出整个数组);
|
|
7
|
+
* - `lineType`:ama 写入的行以 `{"type":"…"` 开头(manager 的 append 把 type 放在第一个键),
|
|
8
|
+
* 据此不解析就能跳过不需要的行;其它程序写的行退回 JSON.parse;
|
|
9
|
+
* - `readSessionReadOnly`:头 + 条目 + 叶子(同 migrate.ts 的规则),末尾半行忽略、中间坏行抛错。
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
12
|
+
import { basename, join } from "node:path";
|
|
13
|
+
import { AmaError } from "../errors.js";
|
|
14
|
+
import { migrateSessionLines } from "./migrate.js";
|
|
15
|
+
import { SESSION_FILE_SUFFIX, TRASH_DIR_NAME, listSessionFiles, sessionDirForCwd, sessionIdFromFileName, } from "./store.js";
|
|
16
|
+
/** 根目录下全部 cwd 子目录(不含 trash)。 */
|
|
17
|
+
export function sessionSubdirs(root) {
|
|
18
|
+
if (!existsSync(root))
|
|
19
|
+
return [];
|
|
20
|
+
return readdirSync(root)
|
|
21
|
+
.filter((name) => name !== TRASH_DIR_NAME)
|
|
22
|
+
.map((name) => join(root, name))
|
|
23
|
+
.filter((dir) => {
|
|
24
|
+
try {
|
|
25
|
+
return statSync(dir).isDirectory();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** 范围内的会话文件:给 cwd 只看该目录,否则全部;按文件名(创建时间)倒序。 */
|
|
33
|
+
export function sessionFilesInScope(root, cwd) {
|
|
34
|
+
const dirs = cwd === undefined ? sessionSubdirs(root) : [sessionDirForCwd(root, cwd)];
|
|
35
|
+
const files = dirs.flatMap((dir) => listSessionFiles(dir));
|
|
36
|
+
return files.sort((a, b) => basename(b).localeCompare(basename(a)));
|
|
37
|
+
}
|
|
38
|
+
/** 按 id 或唯一前缀找会话文件;本 cwd 目录优先(同 compose-store 的规则,但只读、不 import 组装根)。 */
|
|
39
|
+
export function findSessionFileReadOnly(root, id, cwd) {
|
|
40
|
+
const dirs = cwd === undefined
|
|
41
|
+
? sessionSubdirs(root)
|
|
42
|
+
: [sessionDirForCwd(root, cwd), ...sessionSubdirs(root)];
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
const prefix = [];
|
|
45
|
+
for (const dir of dirs) {
|
|
46
|
+
for (const file of listSessionFiles(dir)) {
|
|
47
|
+
if (seen.has(file))
|
|
48
|
+
continue;
|
|
49
|
+
seen.add(file);
|
|
50
|
+
const fileId = sessionIdFromFileName(file);
|
|
51
|
+
if (fileId === id)
|
|
52
|
+
return file;
|
|
53
|
+
if (fileId?.startsWith(id) === true)
|
|
54
|
+
prefix.push(file);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (prefix.length > 1) {
|
|
58
|
+
const ids = prefix.map((f) => sessionIdFromFileName(f) ?? f).slice(0, 10);
|
|
59
|
+
throw new AmaError("invalid_arguments", `会话 id 前缀 ${id} 不唯一,候选:${ids.join(", ")}`, {
|
|
60
|
+
exitCode: 5,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const [file] = prefix;
|
|
64
|
+
if (file === undefined)
|
|
65
|
+
throw new AmaError("session_not_found", `会话不存在:${id}`, { exitCode: 5 });
|
|
66
|
+
return file;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 逐行回调(不含行尾 `\n` / `\r`,跳过空行);`last` 表示文件最后一行且没有换行结尾
|
|
70
|
+
* (可能是写入中途的半行)。回调返回 false 提前结束。
|
|
71
|
+
*/
|
|
72
|
+
export function forEachLine(file, visit) {
|
|
73
|
+
const text = readFileSync(file, "utf8");
|
|
74
|
+
let start = 0;
|
|
75
|
+
let index = 0;
|
|
76
|
+
while (start < text.length) {
|
|
77
|
+
let end = text.indexOf("\n", start);
|
|
78
|
+
const last = end === -1;
|
|
79
|
+
if (last)
|
|
80
|
+
end = text.length;
|
|
81
|
+
let line = text.slice(start, end);
|
|
82
|
+
if (line.endsWith("\r"))
|
|
83
|
+
line = line.slice(0, -1);
|
|
84
|
+
start = end + 1;
|
|
85
|
+
if (line.trim() === "")
|
|
86
|
+
continue;
|
|
87
|
+
if (visit(line, index++, last) === false)
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const TYPE_PREFIX = '{"type":"';
|
|
92
|
+
/** ama 写的行:不解析就取出 type(与消息的 role,若紧随其后);不是这种形状时 undefined。 */
|
|
93
|
+
export function lineType(line) {
|
|
94
|
+
if (!line.startsWith(TYPE_PREFIX))
|
|
95
|
+
return undefined;
|
|
96
|
+
const end = line.indexOf('"', TYPE_PREFIX.length);
|
|
97
|
+
if (end < 0)
|
|
98
|
+
return undefined;
|
|
99
|
+
const type = line.slice(TYPE_PREFIX.length, end);
|
|
100
|
+
if (type !== "message")
|
|
101
|
+
return { type };
|
|
102
|
+
const rolePrefix = ',"message":{"role":"';
|
|
103
|
+
if (!line.startsWith(rolePrefix, end + 1))
|
|
104
|
+
return { type };
|
|
105
|
+
const roleStart = end + 1 + rolePrefix.length;
|
|
106
|
+
const roleEnd = line.indexOf('"', roleStart);
|
|
107
|
+
return roleEnd < 0 ? { type } : { type, role: line.slice(roleStart, roleEnd) };
|
|
108
|
+
}
|
|
109
|
+
/** 解析一行;失败时:末尾半行 → undefined,其它 → 抛 session_corrupt。 */
|
|
110
|
+
export function parseLine(file, line, index, last) {
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(line);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (last)
|
|
116
|
+
return undefined;
|
|
117
|
+
throw new AmaError("session_corrupt", `${file}:${index + 1}: invalid JSON line`, {
|
|
118
|
+
cause: error,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** 只读读取整个会话(不加锁、不修复):头、全部条目、叶子 id。 */
|
|
123
|
+
export function readSessionReadOnly(file) {
|
|
124
|
+
const lines = [];
|
|
125
|
+
try {
|
|
126
|
+
forEachLine(file, (line, index, last) => {
|
|
127
|
+
const parsed = parseLine(file, line, index, last);
|
|
128
|
+
if (parsed !== undefined)
|
|
129
|
+
lines.push(parsed);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof AmaError)
|
|
134
|
+
throw error;
|
|
135
|
+
throw new AmaError("session_not_found", `cannot read session file ${file}`, { cause: error });
|
|
136
|
+
}
|
|
137
|
+
const migrated = migrateSessionLines(lines, file);
|
|
138
|
+
const leaf = migrated.leafId !== undefined ? migrated.leafId : (migrated.entries.at(-1)?.id ?? null);
|
|
139
|
+
return { ...migrated, leaf };
|
|
140
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨会话全文检索(`ama sessions search`)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - 模式:普通关键词(不区分大小写的子串)或 `/正则/标志`;
|
|
5
|
+
* - 检索的文本:user = 文本块;assistant = 文本块 + 工具调用(`名字 参数 JSON`);tool = 工具结果文本。
|
|
6
|
+
* 思考块、system、custom 不检索;
|
|
7
|
+
* - 关键词不含 `"`、`\` 与控制字符时,先在原始行上做子串预筛(JSON 只转义这些字符,中文原样),
|
|
8
|
+
* 不命中的行不解析;正则没法预筛,逐行解析消息行;
|
|
9
|
+
* - 每条命中带:会话 id、条目时间、项目(头的 cwd)、角色、条目序号(文件里第几条条目,1 起)、
|
|
10
|
+
* user 的消息编号(同 `sessions show` / `--from`)、片段与命中区间(展示层负责高亮)。
|
|
11
|
+
* - 文件按创建时间倒序(最新在前),文件内按条目顺序;到 limit 停。
|
|
12
|
+
*/
|
|
13
|
+
export type SearchRole = "user" | "assistant" | "tool";
|
|
14
|
+
export interface SearchHit {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
file: string;
|
|
17
|
+
cwd: string;
|
|
18
|
+
timestamp: string;
|
|
19
|
+
role: SearchRole;
|
|
20
|
+
/** 文件里第几条条目(1 起,不含头与 leaf 行)。 */
|
|
21
|
+
entryIndex: number;
|
|
22
|
+
entryId: string;
|
|
23
|
+
/** user 的消息编号。 */
|
|
24
|
+
userN?: number;
|
|
25
|
+
snippet: string;
|
|
26
|
+
/** snippet 里的命中区间 `[start, end)`。 */
|
|
27
|
+
ranges: Array<[number, number]>;
|
|
28
|
+
}
|
|
29
|
+
export interface SearchQuery {
|
|
30
|
+
/** 返回 `[start, end)` 区间;没有命中返回空数组。 */
|
|
31
|
+
match(text: string): Array<[number, number]>;
|
|
32
|
+
/** 原始行一定不含时返回 false(可跳过解析);不确定返回 true。 */
|
|
33
|
+
mayContain(line: string): boolean;
|
|
34
|
+
}
|
|
35
|
+
/** `/re/flags` → 正则;其它 → 不区分大小写的子串。非法正则抛 SyntaxError。 */
|
|
36
|
+
export declare function compileQuery(pattern: string): SearchQuery;
|
|
37
|
+
/** 命中附近的单行片段(空白压平),区间换算到片段坐标。 */
|
|
38
|
+
export declare function makeSnippet(text: string, ranges: ReadonlyArray<[number, number]>, context?: number): {
|
|
39
|
+
snippet: string;
|
|
40
|
+
ranges: Array<[number, number]>;
|
|
41
|
+
};
|
|
42
|
+
export interface SearchOptions {
|
|
43
|
+
roles?: ReadonlySet<SearchRole>;
|
|
44
|
+
/** ISO 时间下限(含);比较条目 timestamp。 */
|
|
45
|
+
sinceIso?: string;
|
|
46
|
+
limit: number;
|
|
47
|
+
}
|
|
48
|
+
/** 在一批文件里检索;返回命中(最多 limit 条)与是否到了上限(可能还有更多)。 */
|
|
49
|
+
export declare function searchSessions(files: readonly string[], query: SearchQuery, options: SearchOptions): {
|
|
50
|
+
hits: SearchHit[];
|
|
51
|
+
truncated: boolean;
|
|
52
|
+
};
|