@bastani/atomic 0.9.2 → 0.9.3-alpha.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 +57 -0
- package/README.md +2 -2
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/direct-tools.ts +4 -2
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/proxy-call.ts +3 -1
- package/dist/builtin/mcp/utils.ts +18 -7
- package/dist/builtin/subagents/CHANGELOG.md +11 -0
- package/dist/builtin/subagents/README.md +6 -6
- package/dist/builtin/subagents/agents/code-simplifier.md +7 -6
- package/dist/builtin/subagents/agents/codebase-analyzer.md +5 -4
- package/dist/builtin/subagents/agents/codebase-locator.md +3 -3
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +10 -10
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +4 -4
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +3 -3
- package/dist/builtin/subagents/agents/codebase-research-locator.md +4 -4
- package/dist/builtin/subagents/agents/debugger.md +5 -5
- package/dist/builtin/subagents/agents/worker.md +56 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
- package/dist/builtin/subagents/src/agents/agent-loaders.ts +3 -5
- package/dist/builtin/subagents/src/agents/agent-management-helpers.ts +3 -3
- package/dist/builtin/subagents/src/extension/schemas.ts +2 -2
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -3
- package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +1 -1
- package/dist/builtin/subagents/src/runs/shared/nested-render.ts +2 -2
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -1
- package/dist/builtin/subagents/src/shared/types-depth.ts +5 -5
- package/dist/builtin/subagents/src/shared/types-runtime.ts +2 -1
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +13 -0
- package/dist/builtin/workflows/README.md +2 -2
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +11 -6
- package/dist/builtin/workflows/builtin/goal-ledger.ts +33 -1
- package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -28
- package/dist/builtin/workflows/builtin/goal-reducer.ts +2 -2
- package/dist/builtin/workflows/builtin/goal-reports.ts +2 -5
- package/dist/builtin/workflows/builtin/goal-review.ts +1 -1
- package/dist/builtin/workflows/builtin/goal-runner.ts +10 -17
- package/dist/builtin/workflows/builtin/open-claude-design-feedback.ts +3 -3
- package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +1 -3
- package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +1 -1
- package/dist/builtin/workflows/builtin/ralph-core.ts +7 -17
- package/dist/builtin/workflows/builtin/ralph-runner.ts +11 -18
- package/dist/builtin/workflows/builtin/shared-prompts.ts +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/config-loader.ts +35 -15
- package/dist/builtin/workflows/src/extension/discovery.ts +20 -8
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +1 -2
- package/dist/builtin/workflows/src/extension/wiring.ts +1 -1
- package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +11 -10
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/config-self-update.d.ts.map +1 -1
- package/dist/config-self-update.js +3 -4
- package/dist/config-self-update.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -5
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-bash.d.ts +1 -0
- package/dist/core/agent-session-bash.d.ts.map +1 -1
- package/dist/core/agent-session-bash.js +1 -0
- package/dist/core/agent-session-bash.js.map +1 -1
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +23 -0
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -0
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +1 -0
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +29 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +36 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/context-compaction-metrics.d.ts +14 -2
- package/dist/core/compaction/context-compaction-metrics.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-metrics.js +50 -1
- package/dist/core/compaction/context-compaction-metrics.js.map +1 -1
- package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-prompt.js +2 -0
- package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
- package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
- package/dist/core/compaction/context-compaction-runner.js +1 -1
- package/dist/core/compaction/context-compaction-runner.js.map +1 -1
- package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-application.js +5 -5
- package/dist/core/compaction/context-deletion-application.js.map +1 -1
- package/dist/core/compaction/context-deletion-targets.d.ts +2 -0
- package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-targets.js +23 -3
- package/dist/core/compaction/context-deletion-targets.js.map +1 -1
- package/dist/core/compaction/context-deletion-tool-definitions.d.ts +6 -0
- package/dist/core/compaction/context-deletion-tool-definitions.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-tool-definitions.js.map +1 -1
- package/dist/core/compaction/context-deletion-tools.d.ts.map +1 -1
- package/dist/core/compaction/context-deletion-tools.js +18 -10
- package/dist/core/compaction/context-deletion-tools.js.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
- package/dist/core/compaction/context-transcript-analysis.js +2 -4
- package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
- package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
- package/dist/core/copilot-gemini-tool-arguments.js +2 -60
- package/dist/core/copilot-gemini-tool-arguments.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +2 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +11 -3
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +11 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/tool-events.d.ts +13 -13
- package/dist/core/extensions/tool-events.d.ts.map +1 -1
- package/dist/core/extensions/tool-events.js +3 -3
- package/dist/core/extensions/tool-events.js.map +1 -1
- package/dist/core/extensions/types.d.ts +1 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/flattened-tool-arguments.d.ts +18 -0
- package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
- package/dist/core/flattened-tool-arguments.js +104 -0
- package/dist/core/flattened-tool-arguments.js.map +1 -1
- package/dist/core/sdk-exports.d.ts +1 -1
- package/dist/core/sdk-exports.d.ts.map +1 -1
- package/dist/core/sdk-exports.js +1 -1
- package/dist/core/sdk-exports.js.map +1 -1
- package/dist/core/sdk-types.d.ts +2 -2
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +4 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +18 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-manager-resource-accessors.d.ts +4 -0
- package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-resource-accessors.js +15 -0
- package/dist/core/settings-manager-resource-accessors.js.map +1 -1
- package/dist/core/settings-types.d.ts +11 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +3 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/artifact-protocol.d.ts +11 -0
- package/dist/core/tools/artifact-protocol.d.ts.map +1 -0
- package/dist/core/tools/artifact-protocol.js +76 -0
- package/dist/core/tools/artifact-protocol.js.map +1 -0
- package/dist/core/tools/artifacts.d.ts +18 -0
- package/dist/core/tools/artifacts.d.ts.map +1 -0
- package/dist/core/tools/artifacts.js +90 -0
- package/dist/core/tools/artifacts.js.map +1 -0
- package/dist/core/tools/bash-async-jobs.d.ts +20 -0
- package/dist/core/tools/bash-async-jobs.d.ts.map +1 -0
- package/dist/core/tools/bash-async-jobs.js +59 -0
- package/dist/core/tools/bash-async-jobs.js.map +1 -0
- package/dist/core/tools/bash-async-output.d.ts +10 -0
- package/dist/core/tools/bash-async-output.d.ts.map +1 -0
- package/dist/core/tools/bash-async-output.js +80 -0
- package/dist/core/tools/bash-async-output.js.map +1 -0
- package/dist/core/tools/bash-interceptor.d.ts +10 -0
- package/dist/core/tools/bash-interceptor.d.ts.map +1 -0
- package/dist/core/tools/bash-interceptor.js +39 -0
- package/dist/core/tools/bash-interceptor.js.map +1 -0
- package/dist/core/tools/bash-leading-cd.d.ts +7 -0
- package/dist/core/tools/bash-leading-cd.d.ts.map +1 -0
- package/dist/core/tools/bash-leading-cd.js +59 -0
- package/dist/core/tools/bash-leading-cd.js.map +1 -0
- package/dist/core/tools/bash-pty-native.d.ts +14 -0
- package/dist/core/tools/bash-pty-native.d.ts.map +1 -0
- package/dist/core/tools/bash-pty-native.js +71 -0
- package/dist/core/tools/bash-pty-native.js.map +1 -0
- package/dist/core/tools/bash.d.ts +28 -17
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +152 -35
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/block-resolver.d.ts +16 -0
- package/dist/core/tools/block-resolver.d.ts.map +1 -0
- package/dist/core/tools/block-resolver.js +74 -0
- package/dist/core/tools/block-resolver.js.map +1 -0
- package/dist/core/tools/conflict-registry.d.ts +16 -0
- package/dist/core/tools/conflict-registry.d.ts.map +1 -0
- package/dist/core/tools/conflict-registry.js +44 -0
- package/dist/core/tools/conflict-registry.js.map +1 -0
- package/dist/core/tools/directory-tree.d.ts +13 -0
- package/dist/core/tools/directory-tree.d.ts.map +1 -0
- package/dist/core/tools/directory-tree.js +81 -0
- package/dist/core/tools/directory-tree.js.map +1 -0
- package/dist/core/tools/edit.d.ts +4 -29
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +136 -228
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/fetch-url.d.ts +74 -0
- package/dist/core/tools/fetch-url.d.ts.map +1 -0
- package/dist/core/tools/fetch-url.js +518 -0
- package/dist/core/tools/fetch-url.js.map +1 -0
- package/dist/core/tools/find.d.ts +27 -9
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +400 -176
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/glob-path-utils.d.ts +8 -0
- package/dist/core/tools/glob-path-utils.d.ts.map +1 -0
- package/dist/core/tools/glob-path-utils.js +26 -0
- package/dist/core/tools/glob-path-utils.js.map +1 -0
- package/dist/core/tools/grep.d.ts +12 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +141 -17
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/hashline-engine/apply.d.ts +11 -0
- package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/apply.js +752 -0
- package/dist/core/tools/hashline-engine/apply.js.map +1 -0
- package/dist/core/tools/hashline-engine/block.d.ts +40 -0
- package/dist/core/tools/hashline-engine/block.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/block.js +117 -0
- package/dist/core/tools/hashline-engine/block.js.map +1 -0
- package/dist/core/tools/hashline-engine/diff-preview.d.ts +15 -0
- package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/diff-preview.js +98 -0
- package/dist/core/tools/hashline-engine/diff-preview.js.map +1 -0
- package/dist/core/tools/hashline-engine/format.d.ts +71 -0
- package/dist/core/tools/hashline-engine/format.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/format.js +178 -0
- package/dist/core/tools/hashline-engine/format.js.map +1 -0
- package/dist/core/tools/hashline-engine/fs.d.ts +81 -0
- package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/fs.js +143 -0
- package/dist/core/tools/hashline-engine/fs.js.map +1 -0
- package/dist/core/tools/hashline-engine/index.d.ts +18 -0
- package/dist/core/tools/hashline-engine/index.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/index.js +20 -0
- package/dist/core/tools/hashline-engine/index.js.map +1 -0
- package/dist/core/tools/hashline-engine/input.d.ts +101 -0
- package/dist/core/tools/hashline-engine/input.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/input.js +398 -0
- package/dist/core/tools/hashline-engine/input.js.map +1 -0
- package/dist/core/tools/hashline-engine/messages.d.ts +99 -0
- package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/messages.js +144 -0
- package/dist/core/tools/hashline-engine/messages.js.map +1 -0
- package/dist/core/tools/hashline-engine/mismatch.d.ts +45 -0
- package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/mismatch.js +90 -0
- package/dist/core/tools/hashline-engine/mismatch.js.map +1 -0
- package/dist/core/tools/hashline-engine/normalize.d.ts +21 -0
- package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/normalize.js +33 -0
- package/dist/core/tools/hashline-engine/normalize.js.map +1 -0
- package/dist/core/tools/hashline-engine/parser.d.ts +24 -0
- package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/parser.js +381 -0
- package/dist/core/tools/hashline-engine/parser.js.map +1 -0
- package/dist/core/tools/hashline-engine/patcher.d.ts +118 -0
- package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/patcher.js +341 -0
- package/dist/core/tools/hashline-engine/patcher.js.map +1 -0
- package/dist/core/tools/hashline-engine/prefixes.d.ts +43 -0
- package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/prefixes.js +135 -0
- package/dist/core/tools/hashline-engine/prefixes.js.map +1 -0
- package/dist/core/tools/hashline-engine/recovery.d.ts +41 -0
- package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/recovery.js +168 -0
- package/dist/core/tools/hashline-engine/recovery.js.map +1 -0
- package/dist/core/tools/hashline-engine/snapshots.d.ts +65 -0
- package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/snapshots.js +108 -0
- package/dist/core/tools/hashline-engine/snapshots.js.map +1 -0
- package/dist/core/tools/hashline-engine/stream.d.ts +3 -0
- package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/stream.js +111 -0
- package/dist/core/tools/hashline-engine/stream.js.map +1 -0
- package/dist/core/tools/hashline-engine/tokenizer.d.ts +69 -0
- package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/tokenizer.js +430 -0
- package/dist/core/tools/hashline-engine/tokenizer.js.map +1 -0
- package/dist/core/tools/hashline-engine/types.d.ts +166 -0
- package/dist/core/tools/hashline-engine/types.d.ts.map +1 -0
- package/dist/core/tools/hashline-engine/types.js +9 -0
- package/dist/core/tools/hashline-engine/types.js.map +1 -0
- package/dist/core/tools/hashline.d.ts +29 -0
- package/dist/core/tools/hashline.d.ts.map +1 -0
- package/dist/core/tools/hashline.js +110 -0
- package/dist/core/tools/hashline.js.map +1 -0
- package/dist/core/tools/index.d.ts +6 -4
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +52 -35
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/notebook.d.ts +38 -0
- package/dist/core/tools/notebook.d.ts.map +1 -0
- package/dist/core/tools/notebook.js +125 -0
- package/dist/core/tools/notebook.js.map +1 -0
- package/dist/core/tools/read-document-extract.d.ts +9 -0
- package/dist/core/tools/read-document-extract.d.ts.map +1 -0
- package/dist/core/tools/read-document-extract.js +212 -0
- package/dist/core/tools/read-document-extract.js.map +1 -0
- package/dist/core/tools/read-selectors.d.ts +24 -0
- package/dist/core/tools/read-selectors.d.ts.map +1 -0
- package/dist/core/tools/read-selectors.js +277 -0
- package/dist/core/tools/read-selectors.js.map +1 -0
- package/dist/core/tools/read-url.d.ts +37 -0
- package/dist/core/tools/read-url.d.ts.map +1 -0
- package/dist/core/tools/read-url.js +39 -0
- package/dist/core/tools/read-url.js.map +1 -0
- package/dist/core/tools/read.d.ts +11 -11
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +224 -94
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/resource-selectors.d.ts +44 -0
- package/dist/core/tools/resource-selectors.d.ts.map +1 -0
- package/dist/core/tools/resource-selectors.js +808 -0
- package/dist/core/tools/resource-selectors.js.map +1 -0
- package/dist/core/tools/search-details.d.ts +26 -0
- package/dist/core/tools/search-details.d.ts.map +1 -0
- package/dist/core/tools/search-details.js +24 -0
- package/dist/core/tools/search-details.js.map +1 -0
- package/dist/core/tools/search-line-ranges.d.ts +11 -0
- package/dist/core/tools/search-line-ranges.d.ts.map +1 -0
- package/dist/core/tools/search-line-ranges.js +65 -0
- package/dist/core/tools/search-line-ranges.js.map +1 -0
- package/dist/core/tools/search-native.d.ts +97 -0
- package/dist/core/tools/search-native.d.ts.map +1 -0
- package/dist/core/tools/search-native.js +27 -0
- package/dist/core/tools/search-native.js.map +1 -0
- package/dist/core/tools/search.d.ts +24 -0
- package/dist/core/tools/search.d.ts.map +1 -0
- package/dist/core/tools/search.js +573 -0
- package/dist/core/tools/search.js.map +1 -0
- package/dist/core/tools/truncate.d.ts +4 -4
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js +3 -3
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/url-ip-guards.d.ts +4 -0
- package/dist/core/tools/url-ip-guards.d.ts.map +1 -0
- package/dist/core/tools/url-ip-guards.js +126 -0
- package/dist/core/tools/url-ip-guards.js.map +1 -0
- package/dist/core/tools/write.d.ts +12 -2
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +166 -14
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -3
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/index-extensions.d.ts +2 -2
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +9 -2
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-handlers.js +3 -0
- package/dist/modes/interactive/components/settings-selector-handlers.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-items.js +7 -0
- package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector-content.js +0 -5
- package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
- package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-auth-login.js +1 -0
- package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +80 -2
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.js +3 -0
- package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +51 -0
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +5 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +12 -0
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +4 -1
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-onboarding.d.ts +11 -0
- package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-onboarding.js +220 -0
- package/dist/modes/interactive/interactive-onboarding.js.map +1 -0
- package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-selectors.js +4 -0
- package/dist/modes/interactive/interactive-selectors.js.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.js +6 -0
- package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-slash-commands.js +9 -4
- package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +28 -0
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +21 -1
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/markit.d.ts +8 -0
- package/dist/utils/markit.d.ts.map +1 -0
- package/dist/utils/markit.js +53 -0
- package/dist/utils/markit.js.map +1 -0
- package/dist/utils/paths.d.ts +2 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -1
- package/dist/utils/paths.js.map +1 -1
- package/docs/compaction.md +16 -1
- package/docs/containerization.md +1 -1
- package/docs/docs.json +1 -0
- package/docs/extensions.md +25 -36
- package/docs/quickstart.md +11 -6
- package/docs/sdk.md +5 -5
- package/docs/settings.md +7 -0
- package/docs/subagents.md +3 -2
- package/docs/tools.md +49 -0
- package/docs/usage.md +3 -3
- package/docs/workflows.md +7 -5
- package/examples/extensions/subagent/README.md +5 -5
- package/examples/extensions/subagent/agents/planner.md +1 -1
- package/examples/extensions/subagent/agents/reviewer.md +1 -1
- package/examples/extensions/subagent/agents/scout.md +2 -2
- package/examples/extensions/subagent/display.ts +3 -3
- package/examples/sdk/05-tools.ts +3 -3
- package/examples/sdk/README.md +1 -1
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\nconst DEFAULT_PROMPT_TOOLS = [\n \"read\",\n \"bash\",\n \"edit\",\n \"write\",\n \"ask_user_question\",\n \"todo\",\n] as const;\n\nexport interface SystemPromptModel {\n /** Provider identifier for the selected model. */\n provider: string;\n /** Stable provider-specific model identifier. */\n id: string;\n /** Human-readable model name, when available. */\n name?: string;\n}\n\nexport interface BuildSystemPromptOptions {\n /** Custom system prompt (replaces default). */\n customPrompt?: string;\n /** Tools to include in prompt. Default: [read, bash, edit, write, ask_user_question, todo] */\n selectedTools?: string[];\n /** Tool names explicitly excluded by the caller and omitted from generated guidance. */\n excludedTools?: string[];\n /** Optional one-line tool snippets keyed by tool name. */\n toolSnippets?: Record<string, string>;\n /** Additional guideline bullets appended to the default system prompt guidelines. */\n promptGuidelines?: string[];\n /** Text to append to system prompt. */\n appendSystemPrompt?: string;\n /** Working directory. */\n cwd: string;\n /** Currently selected model, used for model-aware prompt metadata. */\n selectedModel?: SystemPromptModel;\n /** Current reasoning/thinking level for the selected model. */\n selectedThinkingLevel?: string;\n /** Pre-loaded context files. */\n contextFiles?: Array<{ path: string; content: string }>;\n /** Pre-loaded skills. */\n skills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n const {\n customPrompt,\n selectedTools,\n excludedTools,\n toolSnippets,\n promptGuidelines,\n appendSystemPrompt,\n cwd,\n selectedModel,\n selectedThinkingLevel,\n contextFiles: providedContextFiles,\n skills: providedSkills,\n } = options;\n const resolvedCwd = cwd;\n const promptCwd = resolvedCwd.replace(/\\\\/g, \"/\");\n\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, \"0\");\n const day = String(now.getDate()).padStart(2, \"0\");\n const date = `${year}-${month}-${day}`;\n\n const appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n const modelName =\n selectedModel?.name?.trim() || selectedModel?.id || \"unknown\";\n const modelReasoningLevel = selectedThinkingLevel?.trim() || \"off\";\n\n const contextFiles = providedContextFiles ?? [];\n const skills = providedSkills ?? [];\n const explicitlyExcludedTools = new Set(excludedTools ?? []);\n const isPromptToolAvailable = (name: string): boolean =>\n (!selectedTools || selectedTools.includes(name)) &&\n !explicitlyExcludedTools.has(name);\n\n if (customPrompt) {\n let prompt = customPrompt;\n\n if (appendSection) {\n prompt += appendSection;\n }\n\n // Append project context files\n if (contextFiles.length > 0) {\n prompt += \"\\n\\n# Project Context\\n\\n\";\n prompt += \"Project-specific instructions and guidelines:\\n\\n\";\n for (const { path: filePath, content } of contextFiles) {\n prompt += `<context_file path=\\\"${filePath}\\\">\\n${content}\\n</context_file>\\n\\n`;\n }\n }\n\n // Append skills section (only if read tool is available)\n if (isPromptToolAvailable(\"read\") && skills.length > 0) {\n prompt += formatSkillsForPrompt(skills);\n }\n\n // Add model metadata, date, and working directory last\n prompt += `\\nModel name (used for commit attribution): ${modelName}`;\n prompt += `\\nModel reasoning level: ${modelReasoningLevel}`;\n prompt += `\\nCurrent date: ${date}`;\n prompt += `\\nCurrent working directory: ${promptCwd}`;\n\n return prompt;\n }\n\n // Get absolute paths to documentation and examples\n const readmePath = getReadmePath();\n const docsPath = getDocsPath();\n const examplesPath = getExamplesPath();\n\n // Build tools list based on selected tools.\n // A tool appears in Available tools only when the caller provides a one-line snippet.\n const tools = (selectedTools ?? DEFAULT_PROMPT_TOOLS).filter(\n (name) => !explicitlyExcludedTools.has(name),\n );\n const visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n const toolsList =\n visibleTools.length > 0\n ? visibleTools\n .map((name) => `- ${name}: ${toolSnippets![name]}`)\n .join(\"\\n\")\n : \"(none)\";\n\n // Build guidelines based on which tools are actually available\n const guidelinesList: string[] = [];\n const guidelinesSet = new Set<string>();\n const addGuideline = (guideline: string): void => {\n if (guidelinesSet.has(guideline)) {\n return;\n }\n guidelinesSet.add(guideline);\n guidelinesList.push(guideline);\n };\n\n const hasBash = tools.includes(\"bash\");\n const hasGrep = tools.includes(\"grep\");\n const hasFind = tools.includes(\"find\");\n const hasLs = tools.includes(\"ls\");\n const hasRead = tools.includes(\"read\");\n const shouldIncludeAskUserFallbackGuidance =\n selectedTools !== undefined &&\n tools.length > 0 &&\n !tools.includes(\"ask_user_question\") &&\n !explicitlyExcludedTools.has(\"ask_user_question\");\n\n // File exploration guidelines\n if (hasBash && !hasGrep && !hasFind && !hasLs) {\n addGuideline(\"Use bash for file operations like ls, rg, find\");\n }\n if (shouldIncludeAskUserFallbackGuidance) {\n addGuideline(\"Clarify ambiguous requirements using the ask_user_question tool if available.\");\n }\n\n for (const guideline of promptGuidelines ?? []) {\n const normalized = guideline.trim();\n if (normalized.length > 0) {\n addGuideline(normalized);\n }\n }\n\n // Always include these\n addGuideline(\"Be concise in your responses\");\n addGuideline(\"Show file paths clearly when working with files\");\n\n const guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n let prompt = `You are an expert coding assistant operating named Atomic, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nAtomic documentation (read only when the user asks about customizing Atomic itself, its SDK, creating workflows, packages, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- Docs/examples references above must be resolved against these absolute roots; e.g. docs/foo.md means ${docsPath}/foo.md and examples/bar means ${examplesPath}/bar.\n- When asked about: atomic workflows (docs/workflows.md), extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), atomic packages (docs/packages.md)\n- When working on Atomic topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read Atomic .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n if (appendSection) {\n prompt += appendSection;\n }\n\n // Append project context files\n if (contextFiles.length > 0) {\n prompt += \"\\n\\n# Project Context\\n\\n\";\n prompt += \"Project-specific instructions and guidelines:\\n\\n\";\n for (const { path: filePath, content } of contextFiles) {\n prompt += `<context_file path=\\\"${filePath}\\\">\\n${content}\\n</context_file>\\n\\n`;\n }\n }\n\n // Append skills section (only if read tool is available)\n if (hasRead && skills.length > 0) {\n prompt += formatSkillsForPrompt(skills);\n }\n\n // Add model metadata, date, and working directory last\n prompt += `\\nModel name (used for commit attribution): ${modelName}`;\n prompt += `\\nModel reasoning level: ${modelReasoningLevel}`;\n prompt += `\\nCurrent date: ${date}`;\n prompt += `\\nCurrent working directory: ${promptCwd}`;\n\n return prompt;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\nconst DEFAULT_PROMPT_TOOLS = [\n \"read\",\n \"bash\",\n \"edit\",\n \"write\",\n \"find\",\n \"search\",\n \"ask_user_question\",\n \"todo\",\n] as const;\n\nexport interface SystemPromptModel {\n /** Provider identifier for the selected model. */\n provider: string;\n /** Stable provider-specific model identifier. */\n id: string;\n /** Human-readable model name, when available. */\n name?: string;\n}\n\nexport interface BuildSystemPromptOptions {\n /** Custom system prompt (replaces default). */\n customPrompt?: string;\n /** Tools to include in prompt. Default: [read, bash, edit, write, find, search, ask_user_question, todo] */\n selectedTools?: string[];\n /** Tool names explicitly excluded by the caller and omitted from generated guidance. */\n excludedTools?: string[];\n /** Optional one-line tool snippets keyed by tool name. */\n toolSnippets?: Record<string, string>;\n /** Additional guideline bullets appended to the default system prompt guidelines. */\n promptGuidelines?: string[];\n /** Text to append to system prompt. */\n appendSystemPrompt?: string;\n /** Working directory. */\n cwd: string;\n /** Currently selected model, used for model-aware prompt metadata. */\n selectedModel?: SystemPromptModel;\n /** Current reasoning/thinking level for the selected model. */\n selectedThinkingLevel?: string;\n /** Pre-loaded context files. */\n contextFiles?: Array<{ path: string; content: string }>;\n /** Pre-loaded skills. */\n skills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n const {\n customPrompt,\n selectedTools,\n excludedTools,\n toolSnippets,\n promptGuidelines,\n appendSystemPrompt,\n cwd,\n selectedModel,\n selectedThinkingLevel,\n contextFiles: providedContextFiles,\n skills: providedSkills,\n } = options;\n const resolvedCwd = cwd;\n const promptCwd = resolvedCwd.replace(/\\\\/g, \"/\");\n\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, \"0\");\n const day = String(now.getDate()).padStart(2, \"0\");\n const date = `${year}-${month}-${day}`;\n\n const appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n const modelName =\n selectedModel?.name?.trim() || selectedModel?.id || \"unknown\";\n const modelReasoningLevel = selectedThinkingLevel?.trim() || \"off\";\n\n const contextFiles = providedContextFiles ?? [];\n const skills = providedSkills ?? [];\n const explicitlyExcludedTools = new Set(excludedTools ?? []);\n const isPromptToolAvailable = (name: string): boolean =>\n (!selectedTools || selectedTools.includes(name)) &&\n !explicitlyExcludedTools.has(name);\n\n if (customPrompt) {\n let prompt = customPrompt;\n\n if (appendSection) {\n prompt += appendSection;\n }\n\n // Append project context files\n if (contextFiles.length > 0) {\n prompt += \"\\n\\n# Project Context\\n\\n\";\n prompt += \"Project-specific instructions and guidelines:\\n\\n\";\n for (const { path: filePath, content } of contextFiles) {\n prompt += `<context_file path=\\\"${filePath}\\\">\\n${content}\\n</context_file>\\n\\n`;\n }\n }\n\n // Append skills section (only if read tool is available)\n if (isPromptToolAvailable(\"read\") && skills.length > 0) {\n prompt += formatSkillsForPrompt(skills);\n }\n\n // Add model metadata, date, and working directory last\n prompt += `\\nModel name (used for commit attribution): ${modelName}`;\n prompt += `\\nModel reasoning level: ${modelReasoningLevel}`;\n prompt += `\\nCurrent date: ${date}`;\n prompt += `\\nCurrent working directory: ${promptCwd}`;\n\n return prompt;\n }\n\n // Get absolute paths to documentation and examples\n const readmePath = getReadmePath();\n const docsPath = getDocsPath();\n const examplesPath = getExamplesPath();\n\n // Build tools list based on selected tools.\n // A tool appears in Available tools only when the caller provides a one-line snippet.\n const tools = (selectedTools ?? DEFAULT_PROMPT_TOOLS).filter(\n (name) => !explicitlyExcludedTools.has(name),\n );\n const visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n const toolsList =\n visibleTools.length > 0\n ? visibleTools\n .map((name) => `- ${name}: ${toolSnippets![name]}`)\n .join(\"\\n\")\n : \"(none)\";\n\n // Build guidelines based on which tools are actually available\n const guidelinesList: string[] = [];\n const guidelinesSet = new Set<string>();\n const addGuideline = (guideline: string): void => {\n if (guidelinesSet.has(guideline)) {\n return;\n }\n guidelinesSet.add(guideline);\n guidelinesList.push(guideline);\n };\n\n const hasBash = tools.includes(\"bash\");\n const hasFind = tools.includes(\"find\");\n const hasLs = tools.includes(\"ls\");\n const hasRead = tools.includes(\"read\");\n const shouldIncludeAskUserFallbackGuidance =\n selectedTools !== undefined &&\n tools.length > 0 &&\n !tools.includes(\"ask_user_question\") &&\n !explicitlyExcludedTools.has(\"ask_user_question\");\n\n // File exploration guidelines\n if (hasBash && !hasFind && !hasLs) {\n addGuideline(\"Use bash for file operations like ls, rg, find\");\n }\n if (shouldIncludeAskUserFallbackGuidance) {\n addGuideline(\"Clarify ambiguous requirements using the ask_user_question tool if available.\");\n }\n\n for (const guideline of promptGuidelines ?? []) {\n const normalized = guideline.trim();\n if (normalized.length > 0) {\n addGuideline(normalized);\n }\n }\n\n // Always include these\n addGuideline(\"Be concise in your responses\");\n addGuideline(\"Show file paths clearly when working with files\");\n\n const guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n let prompt = `You are an expert coding assistant operating named Atomic, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nAtomic documentation (read only when the user asks about customizing Atomic itself, its SDK, creating workflows, packages, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- Docs/examples references above must be resolved against these absolute roots; e.g. docs/foo.md means ${docsPath}/foo.md and examples/bar means ${examplesPath}/bar.\n- When asked about: atomic workflows (docs/workflows.md), extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), atomic packages (docs/packages.md)\n- When working on Atomic topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read Atomic .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n if (appendSection) {\n prompt += appendSection;\n }\n\n // Append project context files\n if (contextFiles.length > 0) {\n prompt += \"\\n\\n# Project Context\\n\\n\";\n prompt += \"Project-specific instructions and guidelines:\\n\\n\";\n for (const { path: filePath, content } of contextFiles) {\n prompt += `<context_file path=\\\"${filePath}\\\">\\n${content}\\n</context_file>\\n\\n`;\n }\n }\n\n // Append skills section (only if read tool is available)\n if (hasRead && skills.length > 0) {\n prompt += formatSkillsForPrompt(skills);\n }\n\n // Add model metadata, date, and working directory last\n prompt += `\\nModel name (used for commit attribution): ${modelName}`;\n prompt += `\\nModel reasoning level: ${modelReasoningLevel}`;\n prompt += `\\nCurrent date: ${date}`;\n prompt += `\\nCurrent working directory: ${promptCwd}`;\n\n return prompt;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InternalResourceRouter } from "./resource-selectors.ts";
|
|
2
|
+
export declare function registerArtifactDir(dir: string): void;
|
|
3
|
+
export declare function unregisterArtifactDir(dir: string): void;
|
|
4
|
+
export declare function artifactIdFromUrl(url: string): string | undefined;
|
|
5
|
+
/** Resolve an artifact:// url to a backing file path across known dirs. */
|
|
6
|
+
export declare function resolveArtifactUrl(url: string, pinnedDirs?: readonly string[]): string | undefined;
|
|
7
|
+
/** Build an InternalResourceRouter that resolves/reads artifact:// urls. */
|
|
8
|
+
export declare function createArtifactRouter(getPinnedDirs: () => readonly string[]): InternalResourceRouter;
|
|
9
|
+
/** Resolve and read an artifact directly from a context's artifact dirs. */
|
|
10
|
+
export declare function readArtifactUrl(url: string, dirs?: readonly string[]): string;
|
|
11
|
+
//# sourceMappingURL=artifact-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-protocol.d.ts","sourceRoot":"","sources":["../../../src/core/tools/artifact-protocol.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAItE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGjE;AAMD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,GAAG,SAAS,CActG;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,SAAS,MAAM,EAAE,GAAG,sBAAsB,CAanG;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAIjF","sourcesContent":["/**\n * artifact:// protocol resolution, mirrored from oh-my-pi's\n * `packages/coding-agent/src/internal-urls/artifact-protocol.ts` at 15b5c1397fc.\n *\n * `artifact://<numericId>` resolves to an artifact file by matching the\n * `${id}.` prefix. The calling session's artifacts dir is searched first,\n * then any other registered active dirs (so subagents can see parent\n * artifacts). Artifacts are immutable reads.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { getArtifactManager } from \"./artifacts.ts\";\nimport type { InternalResourceRouter } from \"./resource-selectors.ts\";\n\nconst activeArtifactDirs = new Set<string>();\n\nexport function registerArtifactDir(dir: string): void {\n\tactiveArtifactDirs.add(dir);\n}\n\nexport function unregisterArtifactDir(dir: string): void {\n\tactiveArtifactDirs.delete(dir);\n}\n\nexport function artifactIdFromUrl(url: string): string | undefined {\n\tconst match = url.match(/^artifact:\\/\\/([^/]+)/i);\n\treturn match?.[1];\n}\n\nfunction isValidId(id: string): boolean {\n\treturn /^\\d+$/.test(id);\n}\n\n/** Resolve an artifact:// url to a backing file path across known dirs. */\nexport function resolveArtifactUrl(url: string, pinnedDirs: readonly string[] = []): string | undefined {\n\tconst id = artifactIdFromUrl(url);\n\tif (id === undefined) throw new Error(\"artifact:// URL requires a numeric ID: artifact://0\");\n\tif (!isValidId(id)) throw new Error(`artifact:// ID must be numeric, got: ${id}`);\n\tconst ordered = [...pinnedDirs, ...activeArtifactDirs];\n\tconst seen = new Set<string>();\n\tfor (const dir of ordered) {\n\t\tif (seen.has(dir)) continue;\n\t\tseen.add(dir);\n\t\tconst resolved = getArtifactManager(dir).resolve(id);\n\t\tif (resolved) return resolved;\n\t}\n\tconst available = [...new Set([...pinnedDirs, ...activeArtifactDirs])].flatMap((dir) => getArtifactManager(dir).list());\n\tthrow new Error(`Artifact ${id} not found. Available: ${available.join(\", \") || \"none\"}`);\n}\n\n/** Build an InternalResourceRouter that resolves/reads artifact:// urls. */\nexport function createArtifactRouter(getPinnedDirs: () => readonly string[]): InternalResourceRouter {\n\treturn {\n\t\tresolve: (url) => {\n\t\t\tif (!/^artifact:\\/\\//i.test(url)) return undefined;\n\t\t\ttry { return resolveArtifactUrl(url, getPinnedDirs()); } catch { return undefined; }\n\t\t},\n\t\tread: (url) => {\n\t\t\tif (!/^artifact:\\/\\//i.test(url)) return undefined;\n\t\t\tconst path = resolveArtifactUrl(url, getPinnedDirs());\n\t\t\tif (!path || !existsSync(path)) throw new Error(`Artifact not found: ${url}`);\n\t\t\treturn readFileSync(path, \"utf8\");\n\t\t},\n\t};\n}\n\n/** Resolve and read an artifact directly from a context's artifact dirs. */\nexport function readArtifactUrl(url: string, dirs: readonly string[] = []): string {\n\tconst path = resolveArtifactUrl(url, dirs);\n\tif (!path || !existsSync(path)) throw new Error(`Artifact not found: ${url}`);\n\treturn readFileSync(path, \"utf8\");\n}\n"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* artifact:// protocol resolution, mirrored from oh-my-pi's
|
|
3
|
+
* `packages/coding-agent/src/internal-urls/artifact-protocol.ts` at 15b5c1397fc.
|
|
4
|
+
*
|
|
5
|
+
* `artifact://<numericId>` resolves to an artifact file by matching the
|
|
6
|
+
* `${id}.` prefix. The calling session's artifacts dir is searched first,
|
|
7
|
+
* then any other registered active dirs (so subagents can see parent
|
|
8
|
+
* artifacts). Artifacts are immutable reads.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { getArtifactManager } from "./artifacts.js";
|
|
12
|
+
const activeArtifactDirs = new Set();
|
|
13
|
+
export function registerArtifactDir(dir) {
|
|
14
|
+
activeArtifactDirs.add(dir);
|
|
15
|
+
}
|
|
16
|
+
export function unregisterArtifactDir(dir) {
|
|
17
|
+
activeArtifactDirs.delete(dir);
|
|
18
|
+
}
|
|
19
|
+
export function artifactIdFromUrl(url) {
|
|
20
|
+
const match = url.match(/^artifact:\/\/([^/]+)/i);
|
|
21
|
+
return match?.[1];
|
|
22
|
+
}
|
|
23
|
+
function isValidId(id) {
|
|
24
|
+
return /^\d+$/.test(id);
|
|
25
|
+
}
|
|
26
|
+
/** Resolve an artifact:// url to a backing file path across known dirs. */
|
|
27
|
+
export function resolveArtifactUrl(url, pinnedDirs = []) {
|
|
28
|
+
const id = artifactIdFromUrl(url);
|
|
29
|
+
if (id === undefined)
|
|
30
|
+
throw new Error("artifact:// URL requires a numeric ID: artifact://0");
|
|
31
|
+
if (!isValidId(id))
|
|
32
|
+
throw new Error(`artifact:// ID must be numeric, got: ${id}`);
|
|
33
|
+
const ordered = [...pinnedDirs, ...activeArtifactDirs];
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
for (const dir of ordered) {
|
|
36
|
+
if (seen.has(dir))
|
|
37
|
+
continue;
|
|
38
|
+
seen.add(dir);
|
|
39
|
+
const resolved = getArtifactManager(dir).resolve(id);
|
|
40
|
+
if (resolved)
|
|
41
|
+
return resolved;
|
|
42
|
+
}
|
|
43
|
+
const available = [...new Set([...pinnedDirs, ...activeArtifactDirs])].flatMap((dir) => getArtifactManager(dir).list());
|
|
44
|
+
throw new Error(`Artifact ${id} not found. Available: ${available.join(", ") || "none"}`);
|
|
45
|
+
}
|
|
46
|
+
/** Build an InternalResourceRouter that resolves/reads artifact:// urls. */
|
|
47
|
+
export function createArtifactRouter(getPinnedDirs) {
|
|
48
|
+
return {
|
|
49
|
+
resolve: (url) => {
|
|
50
|
+
if (!/^artifact:\/\//i.test(url))
|
|
51
|
+
return undefined;
|
|
52
|
+
try {
|
|
53
|
+
return resolveArtifactUrl(url, getPinnedDirs());
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
read: (url) => {
|
|
60
|
+
if (!/^artifact:\/\//i.test(url))
|
|
61
|
+
return undefined;
|
|
62
|
+
const path = resolveArtifactUrl(url, getPinnedDirs());
|
|
63
|
+
if (!path || !existsSync(path))
|
|
64
|
+
throw new Error(`Artifact not found: ${url}`);
|
|
65
|
+
return readFileSync(path, "utf8");
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Resolve and read an artifact directly from a context's artifact dirs. */
|
|
70
|
+
export function readArtifactUrl(url, dirs = []) {
|
|
71
|
+
const path = resolveArtifactUrl(url, dirs);
|
|
72
|
+
if (!path || !existsSync(path))
|
|
73
|
+
throw new Error(`Artifact not found: ${url}`);
|
|
74
|
+
return readFileSync(path, "utf8");
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=artifact-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-protocol.js","sourceRoot":"","sources":["../../../src/core/tools/artifact-protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * artifact:// protocol resolution, mirrored from oh-my-pi's\n * `packages/coding-agent/src/internal-urls/artifact-protocol.ts` at 15b5c1397fc.\n *\n * `artifact://<numericId>` resolves to an artifact file by matching the\n * `${id}.` prefix. The calling session's artifacts dir is searched first,\n * then any other registered active dirs (so subagents can see parent\n * artifacts). Artifacts are immutable reads.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { getArtifactManager } from \"./artifacts.ts\";\nimport type { InternalResourceRouter } from \"./resource-selectors.ts\";\n\nconst activeArtifactDirs = new Set<string>();\n\nexport function registerArtifactDir(dir: string): void {\n\tactiveArtifactDirs.add(dir);\n}\n\nexport function unregisterArtifactDir(dir: string): void {\n\tactiveArtifactDirs.delete(dir);\n}\n\nexport function artifactIdFromUrl(url: string): string | undefined {\n\tconst match = url.match(/^artifact:\\/\\/([^/]+)/i);\n\treturn match?.[1];\n}\n\nfunction isValidId(id: string): boolean {\n\treturn /^\\d+$/.test(id);\n}\n\n/** Resolve an artifact:// url to a backing file path across known dirs. */\nexport function resolveArtifactUrl(url: string, pinnedDirs: readonly string[] = []): string | undefined {\n\tconst id = artifactIdFromUrl(url);\n\tif (id === undefined) throw new Error(\"artifact:// URL requires a numeric ID: artifact://0\");\n\tif (!isValidId(id)) throw new Error(`artifact:// ID must be numeric, got: ${id}`);\n\tconst ordered = [...pinnedDirs, ...activeArtifactDirs];\n\tconst seen = new Set<string>();\n\tfor (const dir of ordered) {\n\t\tif (seen.has(dir)) continue;\n\t\tseen.add(dir);\n\t\tconst resolved = getArtifactManager(dir).resolve(id);\n\t\tif (resolved) return resolved;\n\t}\n\tconst available = [...new Set([...pinnedDirs, ...activeArtifactDirs])].flatMap((dir) => getArtifactManager(dir).list());\n\tthrow new Error(`Artifact ${id} not found. Available: ${available.join(\", \") || \"none\"}`);\n}\n\n/** Build an InternalResourceRouter that resolves/reads artifact:// urls. */\nexport function createArtifactRouter(getPinnedDirs: () => readonly string[]): InternalResourceRouter {\n\treturn {\n\t\tresolve: (url) => {\n\t\t\tif (!/^artifact:\\/\\//i.test(url)) return undefined;\n\t\t\ttry { return resolveArtifactUrl(url, getPinnedDirs()); } catch { return undefined; }\n\t\t},\n\t\tread: (url) => {\n\t\t\tif (!/^artifact:\\/\\//i.test(url)) return undefined;\n\t\t\tconst path = resolveArtifactUrl(url, getPinnedDirs());\n\t\t\tif (!path || !existsSync(path)) throw new Error(`Artifact not found: ${url}`);\n\t\t\treturn readFileSync(path, \"utf8\");\n\t\t},\n\t};\n}\n\n/** Resolve and read an artifact directly from a context's artifact dirs. */\nexport function readArtifactUrl(url: string, dirs: readonly string[] = []): string {\n\tconst path = resolveArtifactUrl(url, dirs);\n\tif (!path || !existsSync(path)) throw new Error(`Artifact not found: ${url}`);\n\treturn readFileSync(path, \"utf8\");\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface AllocatedArtifact {
|
|
2
|
+
path: string;
|
|
3
|
+
id: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class ArtifactManager {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(dir: string);
|
|
8
|
+
get dir(): string;
|
|
9
|
+
allocate(toolType: string): AllocatedArtifact;
|
|
10
|
+
save(content: string, toolType: string): string;
|
|
11
|
+
/** Resolve a numeric id to its artifact file path, matching by `${id}.` prefix. */
|
|
12
|
+
resolve(id: string): string | undefined;
|
|
13
|
+
list(): string[];
|
|
14
|
+
}
|
|
15
|
+
/** Returns a shared ArtifactManager for a session artifacts dir (cached per dir). */
|
|
16
|
+
export declare function getArtifactManager(artifactsDir: string): ArtifactManager;
|
|
17
|
+
export declare function resetArtifactManagerCache(): void;
|
|
18
|
+
//# sourceMappingURL=artifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/core/tools/artifacts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED,qBAAa,eAAe;;IAK3B,YAAY,GAAG,EAAE,MAAM,EAEtB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAkBD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAK5C;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM9C;IAED,mFAAmF;IACnF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMtC;IAED,IAAI,IAAI,MAAM,EAAE,CASf;CACD;AAID,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,CAOxE;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD","sourcesContent":["/**\n * Session artifact storage, mirrored from oh-my-pi's\n * `packages/coding-agent/src/session/artifacts.ts` at 15b5c1397fc.\n *\n * Artifacts are persisted as `<numericId>.<toolType>.log` files inside a\n * session artifacts directory. IDs are sequential and scan-initialized from\n * existing files so a resumed session keeps a contiguous id space. Subagents\n * that share a session dir share the same manager/id space.\n */\nimport { existsSync, mkdirSync, readdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface AllocatedArtifact {\n\tpath: string;\n\tid: string;\n}\n\nexport class ArtifactManager {\n\t#nextId = 0;\n\treadonly #dir: string;\n\t#initialized = false;\n\n\tconstructor(dir: string) {\n\t\tthis.#dir = dir;\n\t}\n\n\tget dir(): string {\n\t\treturn this.#dir;\n\t}\n\n\t#init(): void {\n\t\tif (this.#initialized) return;\n\t\tthis.#initialized = true;\n\t\tlet max = -1;\n\t\tif (existsSync(this.#dir)) {\n\t\t\tfor (const name of readdirSync(this.#dir)) {\n\t\t\t\tconst match = name.match(/^(\\d+)\\..*\\.log$/);\n\t\t\t\tif (match) {\n\t\t\t\t\tconst id = Number.parseInt(match[1] ?? \"0\", 10);\n\t\t\t\t\tif (id > max) max = id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.#nextId = max + 1;\n\t}\n\n\tallocate(toolType: string): AllocatedArtifact {\n\t\tthis.#init();\n\t\tconst id = String(this.#nextId++);\n\t\tconst path = join(this.#dir, `${id}.${toolType}.log`);\n\t\treturn { path, id };\n\t}\n\n\tsave(content: string, toolType: string): string {\n\t\tthis.#init();\n\t\tconst { path, id } = this.allocate(toolType);\n\t\tif (!existsSync(this.#dir)) mkdirSync(this.#dir, { recursive: true });\n\t\twriteFileSync(path, content, \"utf8\");\n\t\treturn id;\n\t}\n\n\t/** Resolve a numeric id to its artifact file path, matching by `${id}.` prefix. */\n\tresolve(id: string): string | undefined {\n\t\tthis.#init();\n\t\tif (!existsSync(this.#dir)) return undefined;\n\t\tconst prefix = `${id}.`;\n\t\tfor (const name of readdirSync(this.#dir)) if (name.startsWith(prefix) && name.endsWith(\".log\")) return join(this.#dir, name);\n\t\treturn undefined;\n\t}\n\n\tlist(): string[] {\n\t\tthis.#init();\n\t\tif (!existsSync(this.#dir)) return [];\n\t\tconst ids: number[] = [];\n\t\tfor (const name of readdirSync(this.#dir)) {\n\t\t\tconst match = name.match(/^(\\d+)\\..*\\.log$/);\n\t\t\tif (match) ids.push(Number.parseInt(match[1] ?? \"0\", 10));\n\t\t}\n\t\treturn ids.sort((a, b) => a - b).map(String);\n\t}\n}\n\nconst managers = new Map<string, ArtifactManager>();\n\n/** Returns a shared ArtifactManager for a session artifacts dir (cached per dir). */\nexport function getArtifactManager(artifactsDir: string): ArtifactManager {\n\tlet manager = managers.get(artifactsDir);\n\tif (!manager) {\n\t\tmanager = new ArtifactManager(artifactsDir);\n\t\tmanagers.set(artifactsDir, manager);\n\t}\n\treturn manager;\n}\n\nexport function resetArtifactManagerCache(): void {\n\tmanagers.clear();\n}\n"]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session artifact storage, mirrored from oh-my-pi's
|
|
3
|
+
* `packages/coding-agent/src/session/artifacts.ts` at 15b5c1397fc.
|
|
4
|
+
*
|
|
5
|
+
* Artifacts are persisted as `<numericId>.<toolType>.log` files inside a
|
|
6
|
+
* session artifacts directory. IDs are sequential and scan-initialized from
|
|
7
|
+
* existing files so a resumed session keeps a contiguous id space. Subagents
|
|
8
|
+
* that share a session dir share the same manager/id space.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
export class ArtifactManager {
|
|
13
|
+
#nextId = 0;
|
|
14
|
+
#dir;
|
|
15
|
+
#initialized = false;
|
|
16
|
+
constructor(dir) {
|
|
17
|
+
this.#dir = dir;
|
|
18
|
+
}
|
|
19
|
+
get dir() {
|
|
20
|
+
return this.#dir;
|
|
21
|
+
}
|
|
22
|
+
#init() {
|
|
23
|
+
if (this.#initialized)
|
|
24
|
+
return;
|
|
25
|
+
this.#initialized = true;
|
|
26
|
+
let max = -1;
|
|
27
|
+
if (existsSync(this.#dir)) {
|
|
28
|
+
for (const name of readdirSync(this.#dir)) {
|
|
29
|
+
const match = name.match(/^(\d+)\..*\.log$/);
|
|
30
|
+
if (match) {
|
|
31
|
+
const id = Number.parseInt(match[1] ?? "0", 10);
|
|
32
|
+
if (id > max)
|
|
33
|
+
max = id;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
this.#nextId = max + 1;
|
|
38
|
+
}
|
|
39
|
+
allocate(toolType) {
|
|
40
|
+
this.#init();
|
|
41
|
+
const id = String(this.#nextId++);
|
|
42
|
+
const path = join(this.#dir, `${id}.${toolType}.log`);
|
|
43
|
+
return { path, id };
|
|
44
|
+
}
|
|
45
|
+
save(content, toolType) {
|
|
46
|
+
this.#init();
|
|
47
|
+
const { path, id } = this.allocate(toolType);
|
|
48
|
+
if (!existsSync(this.#dir))
|
|
49
|
+
mkdirSync(this.#dir, { recursive: true });
|
|
50
|
+
writeFileSync(path, content, "utf8");
|
|
51
|
+
return id;
|
|
52
|
+
}
|
|
53
|
+
/** Resolve a numeric id to its artifact file path, matching by `${id}.` prefix. */
|
|
54
|
+
resolve(id) {
|
|
55
|
+
this.#init();
|
|
56
|
+
if (!existsSync(this.#dir))
|
|
57
|
+
return undefined;
|
|
58
|
+
const prefix = `${id}.`;
|
|
59
|
+
for (const name of readdirSync(this.#dir))
|
|
60
|
+
if (name.startsWith(prefix) && name.endsWith(".log"))
|
|
61
|
+
return join(this.#dir, name);
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
list() {
|
|
65
|
+
this.#init();
|
|
66
|
+
if (!existsSync(this.#dir))
|
|
67
|
+
return [];
|
|
68
|
+
const ids = [];
|
|
69
|
+
for (const name of readdirSync(this.#dir)) {
|
|
70
|
+
const match = name.match(/^(\d+)\..*\.log$/);
|
|
71
|
+
if (match)
|
|
72
|
+
ids.push(Number.parseInt(match[1] ?? "0", 10));
|
|
73
|
+
}
|
|
74
|
+
return ids.sort((a, b) => a - b).map(String);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const managers = new Map();
|
|
78
|
+
/** Returns a shared ArtifactManager for a session artifacts dir (cached per dir). */
|
|
79
|
+
export function getArtifactManager(artifactsDir) {
|
|
80
|
+
let manager = managers.get(artifactsDir);
|
|
81
|
+
if (!manager) {
|
|
82
|
+
manager = new ArtifactManager(artifactsDir);
|
|
83
|
+
managers.set(artifactsDir, manager);
|
|
84
|
+
}
|
|
85
|
+
return manager;
|
|
86
|
+
}
|
|
87
|
+
export function resetArtifactManagerCache() {
|
|
88
|
+
managers.clear();
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../../src/core/tools/artifacts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Session artifact storage, mirrored from oh-my-pi's\n * `packages/coding-agent/src/session/artifacts.ts` at 15b5c1397fc.\n *\n * Artifacts are persisted as `<numericId>.<toolType>.log` files inside a\n * session artifacts directory. IDs are sequential and scan-initialized from\n * existing files so a resumed session keeps a contiguous id space. Subagents\n * that share a session dir share the same manager/id space.\n */\nimport { existsSync, mkdirSync, readdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface AllocatedArtifact {\n\tpath: string;\n\tid: string;\n}\n\nexport class ArtifactManager {\n\t#nextId = 0;\n\treadonly #dir: string;\n\t#initialized = false;\n\n\tconstructor(dir: string) {\n\t\tthis.#dir = dir;\n\t}\n\n\tget dir(): string {\n\t\treturn this.#dir;\n\t}\n\n\t#init(): void {\n\t\tif (this.#initialized) return;\n\t\tthis.#initialized = true;\n\t\tlet max = -1;\n\t\tif (existsSync(this.#dir)) {\n\t\t\tfor (const name of readdirSync(this.#dir)) {\n\t\t\t\tconst match = name.match(/^(\\d+)\\..*\\.log$/);\n\t\t\t\tif (match) {\n\t\t\t\t\tconst id = Number.parseInt(match[1] ?? \"0\", 10);\n\t\t\t\t\tif (id > max) max = id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.#nextId = max + 1;\n\t}\n\n\tallocate(toolType: string): AllocatedArtifact {\n\t\tthis.#init();\n\t\tconst id = String(this.#nextId++);\n\t\tconst path = join(this.#dir, `${id}.${toolType}.log`);\n\t\treturn { path, id };\n\t}\n\n\tsave(content: string, toolType: string): string {\n\t\tthis.#init();\n\t\tconst { path, id } = this.allocate(toolType);\n\t\tif (!existsSync(this.#dir)) mkdirSync(this.#dir, { recursive: true });\n\t\twriteFileSync(path, content, \"utf8\");\n\t\treturn id;\n\t}\n\n\t/** Resolve a numeric id to its artifact file path, matching by `${id}.` prefix. */\n\tresolve(id: string): string | undefined {\n\t\tthis.#init();\n\t\tif (!existsSync(this.#dir)) return undefined;\n\t\tconst prefix = `${id}.`;\n\t\tfor (const name of readdirSync(this.#dir)) if (name.startsWith(prefix) && name.endsWith(\".log\")) return join(this.#dir, name);\n\t\treturn undefined;\n\t}\n\n\tlist(): string[] {\n\t\tthis.#init();\n\t\tif (!existsSync(this.#dir)) return [];\n\t\tconst ids: number[] = [];\n\t\tfor (const name of readdirSync(this.#dir)) {\n\t\t\tconst match = name.match(/^(\\d+)\\..*\\.log$/);\n\t\t\tif (match) ids.push(Number.parseInt(match[1] ?? \"0\", 10));\n\t\t}\n\t\treturn ids.sort((a, b) => a - b).map(String);\n\t}\n}\n\nconst managers = new Map<string, ArtifactManager>();\n\n/** Returns a shared ArtifactManager for a session artifacts dir (cached per dir). */\nexport function getArtifactManager(artifactsDir: string): ArtifactManager {\n\tlet manager = managers.get(artifactsDir);\n\tif (!manager) {\n\t\tmanager = new ArtifactManager(artifactsDir);\n\t\tmanagers.set(artifactsDir, manager);\n\t}\n\treturn manager;\n}\n\nexport function resetArtifactManagerCache(): void {\n\tmanagers.clear();\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ManagedBashJob {
|
|
2
|
+
jobId: string;
|
|
3
|
+
command: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
status: "running" | "completed" | "failed";
|
|
6
|
+
output: string;
|
|
7
|
+
fullOutputPath?: string;
|
|
8
|
+
exitCode?: number | null;
|
|
9
|
+
error?: string;
|
|
10
|
+
startedAt: number;
|
|
11
|
+
endedAt?: number;
|
|
12
|
+
timeoutSeconds?: number;
|
|
13
|
+
requestedTimeoutSeconds?: number;
|
|
14
|
+
abortController?: AbortController;
|
|
15
|
+
}
|
|
16
|
+
export declare function formatAsyncJobError(error: unknown): string;
|
|
17
|
+
export declare function createManagedBashJob(command: string, cwd: string, timeoutSeconds?: number, requestedTimeoutSeconds?: number): ManagedBashJob;
|
|
18
|
+
export declare function getManagedBashJob(jobId: string): ManagedBashJob | undefined;
|
|
19
|
+
export declare function abortManagedBashJob(jobId: string): ManagedBashJob | undefined;
|
|
20
|
+
//# sourceMappingURL=bash-async-jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-async-jobs.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-jobs.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAKD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI1D;AAgBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,uBAAuB,CAAC,EAAE,MAAM,GAAG,cAAc,CAO5I;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAM3E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAK7E","sourcesContent":["import { rmSync } from \"node:fs\";\n\nexport interface ManagedBashJob {\n\tjobId: string;\n\tcommand: string;\n\tcwd: string;\n\tstatus: \"running\" | \"completed\" | \"failed\";\n\toutput: string;\n\tfullOutputPath?: string;\n\texitCode?: number | null;\n\terror?: string;\n\tstartedAt: number;\n\tendedAt?: number;\n\ttimeoutSeconds?: number;\n\trequestedTimeoutSeconds?: number;\n\tabortController?: AbortController;\n}\n\nconst MAX_MANAGED_BASH_JOBS = 100;\nconst COMPLETED_JOB_TTL_MS = 30 * 60 * 1000;\nconst managedBashJobs = new Map<string, ManagedBashJob>();\nexport function formatAsyncJobError(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : String(error);\n\tif (message.startsWith(\"timeout:\")) return `Command timed out after ${message.slice(\"timeout:\".length)} seconds`;\n\treturn message;\n}\nfunction deleteJobOutput(job: ManagedBashJob): void {\n\tif (!job.fullOutputPath) return;\n\ttry { rmSync(job.fullOutputPath, { force: true }); } catch { /* best-effort temp cleanup */ }\n}\n\nfunction cleanupManagedBashJobs(now = Date.now()): void {\n\tfor (const [jobId, job] of managedBashJobs) if (job.status !== \"running\" && job.endedAt !== undefined && now - job.endedAt > COMPLETED_JOB_TTL_MS) { deleteJobOutput(job); managedBashJobs.delete(jobId); }\n\twhile (managedBashJobs.size > MAX_MANAGED_BASH_JOBS) {\n\t\tconst oldest = [...managedBashJobs.values()].sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt))[0];\n\t\tif (!oldest) break;\n\t\tif (oldest.status === \"running\") oldest.abortController?.abort(); else deleteJobOutput(oldest);\n\t\tmanagedBashJobs.delete(oldest.jobId);\n\t}\n}\n\nexport function createManagedBashJob(command: string, cwd: string, timeoutSeconds?: number, requestedTimeoutSeconds?: number): ManagedBashJob {\n\tcleanupManagedBashJobs();\n\tconst jobId = `bash-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n\tconst job: ManagedBashJob = { jobId, command, cwd, status: \"running\", output: \"\", startedAt: Date.now(), timeoutSeconds, requestedTimeoutSeconds, abortController: new AbortController() };\n\tmanagedBashJobs.set(jobId, job);\n\tcleanupManagedBashJobs();\n\treturn job;\n}\n\nexport function getManagedBashJob(jobId: string): ManagedBashJob | undefined {\n\tcleanupManagedBashJobs();\n\tconst job = managedBashJobs.get(jobId);\n\tif (!job) return undefined;\n\tconst { abortController: _abortController, ...snapshot } = job;\n\treturn { ...snapshot };\n}\n\nexport function abortManagedBashJob(jobId: string): ManagedBashJob | undefined {\n\tconst job = managedBashJobs.get(jobId);\n\tif (!job) return undefined;\n\tjob.abortController?.abort();\n\treturn job;\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { rmSync } from "node:fs";
|
|
2
|
+
const MAX_MANAGED_BASH_JOBS = 100;
|
|
3
|
+
const COMPLETED_JOB_TTL_MS = 30 * 60 * 1000;
|
|
4
|
+
const managedBashJobs = new Map();
|
|
5
|
+
export function formatAsyncJobError(error) {
|
|
6
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7
|
+
if (message.startsWith("timeout:"))
|
|
8
|
+
return `Command timed out after ${message.slice("timeout:".length)} seconds`;
|
|
9
|
+
return message;
|
|
10
|
+
}
|
|
11
|
+
function deleteJobOutput(job) {
|
|
12
|
+
if (!job.fullOutputPath)
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
rmSync(job.fullOutputPath, { force: true });
|
|
16
|
+
}
|
|
17
|
+
catch { /* best-effort temp cleanup */ }
|
|
18
|
+
}
|
|
19
|
+
function cleanupManagedBashJobs(now = Date.now()) {
|
|
20
|
+
for (const [jobId, job] of managedBashJobs)
|
|
21
|
+
if (job.status !== "running" && job.endedAt !== undefined && now - job.endedAt > COMPLETED_JOB_TTL_MS) {
|
|
22
|
+
deleteJobOutput(job);
|
|
23
|
+
managedBashJobs.delete(jobId);
|
|
24
|
+
}
|
|
25
|
+
while (managedBashJobs.size > MAX_MANAGED_BASH_JOBS) {
|
|
26
|
+
const oldest = [...managedBashJobs.values()].sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt))[0];
|
|
27
|
+
if (!oldest)
|
|
28
|
+
break;
|
|
29
|
+
if (oldest.status === "running")
|
|
30
|
+
oldest.abortController?.abort();
|
|
31
|
+
else
|
|
32
|
+
deleteJobOutput(oldest);
|
|
33
|
+
managedBashJobs.delete(oldest.jobId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function createManagedBashJob(command, cwd, timeoutSeconds, requestedTimeoutSeconds) {
|
|
37
|
+
cleanupManagedBashJobs();
|
|
38
|
+
const jobId = `bash-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
39
|
+
const job = { jobId, command, cwd, status: "running", output: "", startedAt: Date.now(), timeoutSeconds, requestedTimeoutSeconds, abortController: new AbortController() };
|
|
40
|
+
managedBashJobs.set(jobId, job);
|
|
41
|
+
cleanupManagedBashJobs();
|
|
42
|
+
return job;
|
|
43
|
+
}
|
|
44
|
+
export function getManagedBashJob(jobId) {
|
|
45
|
+
cleanupManagedBashJobs();
|
|
46
|
+
const job = managedBashJobs.get(jobId);
|
|
47
|
+
if (!job)
|
|
48
|
+
return undefined;
|
|
49
|
+
const { abortController: _abortController, ...snapshot } = job;
|
|
50
|
+
return { ...snapshot };
|
|
51
|
+
}
|
|
52
|
+
export function abortManagedBashJob(jobId) {
|
|
53
|
+
const job = managedBashJobs.get(jobId);
|
|
54
|
+
if (!job)
|
|
55
|
+
return undefined;
|
|
56
|
+
job.abortController?.abort();
|
|
57
|
+
return job;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=bash-async-jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-async-jobs.js","sourceRoot":"","sources":["../../../src/core/tools/bash-async-jobs.ts"],"names":[],"mappings":"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","sourcesContent":["import { rmSync } from \"node:fs\";\n\nexport interface ManagedBashJob {\n\tjobId: string;\n\tcommand: string;\n\tcwd: string;\n\tstatus: \"running\" | \"completed\" | \"failed\";\n\toutput: string;\n\tfullOutputPath?: string;\n\texitCode?: number | null;\n\terror?: string;\n\tstartedAt: number;\n\tendedAt?: number;\n\ttimeoutSeconds?: number;\n\trequestedTimeoutSeconds?: number;\n\tabortController?: AbortController;\n}\n\nconst MAX_MANAGED_BASH_JOBS = 100;\nconst COMPLETED_JOB_TTL_MS = 30 * 60 * 1000;\nconst managedBashJobs = new Map<string, ManagedBashJob>();\nexport function formatAsyncJobError(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : String(error);\n\tif (message.startsWith(\"timeout:\")) return `Command timed out after ${message.slice(\"timeout:\".length)} seconds`;\n\treturn message;\n}\nfunction deleteJobOutput(job: ManagedBashJob): void {\n\tif (!job.fullOutputPath) return;\n\ttry { rmSync(job.fullOutputPath, { force: true }); } catch { /* best-effort temp cleanup */ }\n}\n\nfunction cleanupManagedBashJobs(now = Date.now()): void {\n\tfor (const [jobId, job] of managedBashJobs) if (job.status !== \"running\" && job.endedAt !== undefined && now - job.endedAt > COMPLETED_JOB_TTL_MS) { deleteJobOutput(job); managedBashJobs.delete(jobId); }\n\twhile (managedBashJobs.size > MAX_MANAGED_BASH_JOBS) {\n\t\tconst oldest = [...managedBashJobs.values()].sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt))[0];\n\t\tif (!oldest) break;\n\t\tif (oldest.status === \"running\") oldest.abortController?.abort(); else deleteJobOutput(oldest);\n\t\tmanagedBashJobs.delete(oldest.jobId);\n\t}\n}\n\nexport function createManagedBashJob(command: string, cwd: string, timeoutSeconds?: number, requestedTimeoutSeconds?: number): ManagedBashJob {\n\tcleanupManagedBashJobs();\n\tconst jobId = `bash-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n\tconst job: ManagedBashJob = { jobId, command, cwd, status: \"running\", output: \"\", startedAt: Date.now(), timeoutSeconds, requestedTimeoutSeconds, abortController: new AbortController() };\n\tmanagedBashJobs.set(jobId, job);\n\tcleanupManagedBashJobs();\n\treturn job;\n}\n\nexport function getManagedBashJob(jobId: string): ManagedBashJob | undefined {\n\tcleanupManagedBashJobs();\n\tconst job = managedBashJobs.get(jobId);\n\tif (!job) return undefined;\n\tconst { abortController: _abortController, ...snapshot } = job;\n\treturn { ...snapshot };\n}\n\nexport function abortManagedBashJob(jobId: string): ManagedBashJob | undefined {\n\tconst job = managedBashJobs.get(jobId);\n\tif (!job) return undefined;\n\tjob.abortController?.abort();\n\treturn job;\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface BashAsyncOutputTarget {
|
|
2
|
+
output: string;
|
|
3
|
+
fullOutputPath?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface BashAsyncOutputAppender {
|
|
6
|
+
append(chunk: Buffer): void;
|
|
7
|
+
close(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare function createAsyncOutputAppender(job: BashAsyncOutputTarget): BashAsyncOutputAppender;
|
|
10
|
+
//# sourceMappingURL=bash-async-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-async-output.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-output.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACvC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAeD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,qBAAqB,GAAG,uBAAuB,CAmD7F","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize } from \"./truncate.ts\";\n\nexport interface BashAsyncOutputTarget {\n\toutput: string;\n\tfullOutputPath?: string;\n}\n\nexport interface BashAsyncOutputAppender {\n\tappend(chunk: Buffer): void;\n\tclose(): Promise<void>;\n}\n\nfunction outputPath(): string {\n\treturn join(tmpdir(), `${APP_NAME}-bash-async-${randomBytes(8).toString(\"hex\")}.log`);\n}\n\nfunction byteLength(text: string): number { return Buffer.byteLength(text, \"utf8\"); }\nfunction sanitizeDecodedOutput(text: string): string { return sanitizeBinaryOutput(stripAnsi(text)).replace(/\\r/g, \"\"); }\nfunction utf8Prefix(text: string, maxBytes: number): string {\n\tif (byteLength(text) <= maxBytes) return text;\n\tlet end = text.length;\n\twhile (end > 0 && byteLength(text.slice(0, end)) > maxBytes) end--;\n\treturn text.slice(0, end);\n}\n\nexport function createAsyncOutputAppender(job: BashAsyncOutputTarget): BashAsyncOutputAppender {\n\tlet outputBytes = 0;\n\tlet truncated = false;\n\tlet fullOutputStream: WriteStream | undefined;\n\tlet bufferedChunks: Buffer[] = [];\n\tconst decoder = new TextDecoder();\n\n\tconst ensureFullOutputStream = (): WriteStream => {\n\t\tif (fullOutputStream) return fullOutputStream;\n\t\tjob.fullOutputPath = outputPath();\n\t\tfullOutputStream = createWriteStream(job.fullOutputPath);\n\t\tfor (const chunk of bufferedChunks) fullOutputStream.write(chunk);\n\t\tbufferedChunks = [];\n\t\treturn fullOutputStream;\n\t};\n\tconst appendDecodedText = (decoded: string): void => {\n\t\tif (truncated || decoded.length === 0) return;\n\t\tconst text = sanitizeDecodedOutput(decoded);\n\t\tif (text.length === 0) return;\n\t\tconst bytes = byteLength(text);\n\t\tif (outputBytes + bytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureFullOutputStream();\n\t\t\tconst remaining = Math.max(0, DEFAULT_MAX_BYTES - outputBytes);\n\t\t\tif (remaining > 0) job.output += utf8Prefix(text, remaining);\n\t\t\tjob.output += `\\n[Output truncated at ${formatSize(DEFAULT_MAX_BYTES)} for async job polling. Full output: ${job.fullOutputPath}]`;\n\t\t\toutputBytes += bytes;\n\t\t\ttruncated = true;\n\t\t\treturn;\n\t\t}\n\t\toutputBytes += bytes;\n\t\tjob.output += text;\n\t};\n\n\treturn {\n\t\tappend(chunk) {\n\t\t\tif (fullOutputStream) fullOutputStream.write(chunk);\n\t\t\telse bufferedChunks.push(chunk);\n\t\t\tappendDecodedText(decoder.decode(chunk, { stream: true }));\n\t\t},\n\t\tasync close() {\n\t\t\tappendDecodedText(decoder.decode());\n\t\t\tif (!fullOutputStream) return;\n\t\t\tconst stream = fullOutputStream;\n\t\t\tfullOutputStream = undefined;\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tstream.once(\"error\", reject);\n\t\t\t\tstream.once(\"finish\", resolve);\n\t\t\t\tstream.end();\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { createWriteStream } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { APP_NAME } from "../../config.js";
|
|
6
|
+
import { stripAnsi } from "../../utils/ansi.js";
|
|
7
|
+
import { sanitizeBinaryOutput } from "../../utils/shell.js";
|
|
8
|
+
import { DEFAULT_MAX_BYTES, formatSize } from "./truncate.js";
|
|
9
|
+
function outputPath() {
|
|
10
|
+
return join(tmpdir(), `${APP_NAME}-bash-async-${randomBytes(8).toString("hex")}.log`);
|
|
11
|
+
}
|
|
12
|
+
function byteLength(text) { return Buffer.byteLength(text, "utf8"); }
|
|
13
|
+
function sanitizeDecodedOutput(text) { return sanitizeBinaryOutput(stripAnsi(text)).replace(/\r/g, ""); }
|
|
14
|
+
function utf8Prefix(text, maxBytes) {
|
|
15
|
+
if (byteLength(text) <= maxBytes)
|
|
16
|
+
return text;
|
|
17
|
+
let end = text.length;
|
|
18
|
+
while (end > 0 && byteLength(text.slice(0, end)) > maxBytes)
|
|
19
|
+
end--;
|
|
20
|
+
return text.slice(0, end);
|
|
21
|
+
}
|
|
22
|
+
export function createAsyncOutputAppender(job) {
|
|
23
|
+
let outputBytes = 0;
|
|
24
|
+
let truncated = false;
|
|
25
|
+
let fullOutputStream;
|
|
26
|
+
let bufferedChunks = [];
|
|
27
|
+
const decoder = new TextDecoder();
|
|
28
|
+
const ensureFullOutputStream = () => {
|
|
29
|
+
if (fullOutputStream)
|
|
30
|
+
return fullOutputStream;
|
|
31
|
+
job.fullOutputPath = outputPath();
|
|
32
|
+
fullOutputStream = createWriteStream(job.fullOutputPath);
|
|
33
|
+
for (const chunk of bufferedChunks)
|
|
34
|
+
fullOutputStream.write(chunk);
|
|
35
|
+
bufferedChunks = [];
|
|
36
|
+
return fullOutputStream;
|
|
37
|
+
};
|
|
38
|
+
const appendDecodedText = (decoded) => {
|
|
39
|
+
if (truncated || decoded.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
const text = sanitizeDecodedOutput(decoded);
|
|
42
|
+
if (text.length === 0)
|
|
43
|
+
return;
|
|
44
|
+
const bytes = byteLength(text);
|
|
45
|
+
if (outputBytes + bytes > DEFAULT_MAX_BYTES) {
|
|
46
|
+
ensureFullOutputStream();
|
|
47
|
+
const remaining = Math.max(0, DEFAULT_MAX_BYTES - outputBytes);
|
|
48
|
+
if (remaining > 0)
|
|
49
|
+
job.output += utf8Prefix(text, remaining);
|
|
50
|
+
job.output += `\n[Output truncated at ${formatSize(DEFAULT_MAX_BYTES)} for async job polling. Full output: ${job.fullOutputPath}]`;
|
|
51
|
+
outputBytes += bytes;
|
|
52
|
+
truncated = true;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
outputBytes += bytes;
|
|
56
|
+
job.output += text;
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
append(chunk) {
|
|
60
|
+
if (fullOutputStream)
|
|
61
|
+
fullOutputStream.write(chunk);
|
|
62
|
+
else
|
|
63
|
+
bufferedChunks.push(chunk);
|
|
64
|
+
appendDecodedText(decoder.decode(chunk, { stream: true }));
|
|
65
|
+
},
|
|
66
|
+
async close() {
|
|
67
|
+
appendDecodedText(decoder.decode());
|
|
68
|
+
if (!fullOutputStream)
|
|
69
|
+
return;
|
|
70
|
+
const stream = fullOutputStream;
|
|
71
|
+
fullOutputStream = undefined;
|
|
72
|
+
await new Promise((resolve, reject) => {
|
|
73
|
+
stream.once("error", reject);
|
|
74
|
+
stream.once("finish", resolve);
|
|
75
|
+
stream.end();
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=bash-async-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-async-output.js","sourceRoot":"","sources":["../../../src/core/tools/bash-async-output.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize } from \"./truncate.ts\";\n\nexport interface BashAsyncOutputTarget {\n\toutput: string;\n\tfullOutputPath?: string;\n}\n\nexport interface BashAsyncOutputAppender {\n\tappend(chunk: Buffer): void;\n\tclose(): Promise<void>;\n}\n\nfunction outputPath(): string {\n\treturn join(tmpdir(), `${APP_NAME}-bash-async-${randomBytes(8).toString(\"hex\")}.log`);\n}\n\nfunction byteLength(text: string): number { return Buffer.byteLength(text, \"utf8\"); }\nfunction sanitizeDecodedOutput(text: string): string { return sanitizeBinaryOutput(stripAnsi(text)).replace(/\\r/g, \"\"); }\nfunction utf8Prefix(text: string, maxBytes: number): string {\n\tif (byteLength(text) <= maxBytes) return text;\n\tlet end = text.length;\n\twhile (end > 0 && byteLength(text.slice(0, end)) > maxBytes) end--;\n\treturn text.slice(0, end);\n}\n\nexport function createAsyncOutputAppender(job: BashAsyncOutputTarget): BashAsyncOutputAppender {\n\tlet outputBytes = 0;\n\tlet truncated = false;\n\tlet fullOutputStream: WriteStream | undefined;\n\tlet bufferedChunks: Buffer[] = [];\n\tconst decoder = new TextDecoder();\n\n\tconst ensureFullOutputStream = (): WriteStream => {\n\t\tif (fullOutputStream) return fullOutputStream;\n\t\tjob.fullOutputPath = outputPath();\n\t\tfullOutputStream = createWriteStream(job.fullOutputPath);\n\t\tfor (const chunk of bufferedChunks) fullOutputStream.write(chunk);\n\t\tbufferedChunks = [];\n\t\treturn fullOutputStream;\n\t};\n\tconst appendDecodedText = (decoded: string): void => {\n\t\tif (truncated || decoded.length === 0) return;\n\t\tconst text = sanitizeDecodedOutput(decoded);\n\t\tif (text.length === 0) return;\n\t\tconst bytes = byteLength(text);\n\t\tif (outputBytes + bytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureFullOutputStream();\n\t\t\tconst remaining = Math.max(0, DEFAULT_MAX_BYTES - outputBytes);\n\t\t\tif (remaining > 0) job.output += utf8Prefix(text, remaining);\n\t\t\tjob.output += `\\n[Output truncated at ${formatSize(DEFAULT_MAX_BYTES)} for async job polling. Full output: ${job.fullOutputPath}]`;\n\t\t\toutputBytes += bytes;\n\t\t\ttruncated = true;\n\t\t\treturn;\n\t\t}\n\t\toutputBytes += bytes;\n\t\tjob.output += text;\n\t};\n\n\treturn {\n\t\tappend(chunk) {\n\t\t\tif (fullOutputStream) fullOutputStream.write(chunk);\n\t\t\telse bufferedChunks.push(chunk);\n\t\t\tappendDecodedText(decoder.decode(chunk, { stream: true }));\n\t\t},\n\t\tasync close() {\n\t\t\tappendDecodedText(decoder.decode());\n\t\t\tif (!fullOutputStream) return;\n\t\t\tconst stream = fullOutputStream;\n\t\t\tfullOutputStream = undefined;\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tstream.once(\"error\", reject);\n\t\t\t\tstream.once(\"finish\", resolve);\n\t\t\t\tstream.end();\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface BashInterceptorRule {
|
|
2
|
+
pattern: string;
|
|
3
|
+
flags?: string;
|
|
4
|
+
tool: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[];
|
|
8
|
+
export declare function checkBashInterception(command: string, availableTools: readonly string[], rules?: readonly BashInterceptorRule[]): string | undefined;
|
|
9
|
+
export declare function checkBashInterceptionCandidates(candidates: Array<string | undefined>, availableTools: readonly string[], rules?: readonly BashInterceptorRule[]): void;
|
|
10
|
+
//# sourceMappingURL=bash-interceptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-interceptor.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-interceptor.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,8BAA8B,EAAE,mBAAmB,EAQ/D,CAAC;AAEF,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,KAAK,GAAE,SAAS,mBAAmB,EAAmC,GACpE,MAAM,GAAG,SAAS,CAWpB;AAED,wBAAgB,+BAA+B,CAC9C,UAAU,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,EACrC,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,KAAK,GAAE,SAAS,mBAAmB,EAAmC,GACpE,IAAI,CASN","sourcesContent":["export interface BashInterceptorRule {\n\tpattern: string;\n\tflags?: string;\n\ttool: string;\n\tmessage: string;\n}\n\nexport const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [\n\t{ pattern: \"^\\\\s*(cat|head|tail|less|more)\\\\s+\", tool: \"read\", message: \"Use the read tool instead of shell commands to inspect file contents.\" },\n\t{ pattern: \"^\\\\s*(grep|rg|ripgrep|ag|ack)\\\\s+\", tool: \"search\", message: \"Use the search tool instead of shell grep commands.\" },\n\t{ pattern: \"^\\\\s*(find|fd|locate)\\\\s+.*(-name|-iname|-type|--type|-glob)\", tool: \"find\", message: \"Use the find tool instead of shell find/fd/locate commands.\" },\n\t{ pattern: \"^\\\\s*sed\\\\s+(-i|--in-place)\", tool: \"edit\", message: \"Use the edit tool instead of in-place sed.\" },\n\t{ pattern: \"^\\\\s*perl\\\\s+.*-[pn]?i\", tool: \"edit\", message: \"Use the edit tool instead of in-place perl.\" },\n\t{ pattern: \"^\\\\s*awk\\\\s+.*-i\\\\s+inplace\", tool: \"edit\", message: \"Use the edit tool instead of in-place awk.\" },\n\t{ pattern: \"^\\\\s*(echo|printf|cat\\\\s*<<)\\\\s+(?:[^\\\"'>]|\\\"[^\\\"]*\\\"|'[^']*')*(?<!\\\\|)>{1,2}\\\\|?\\\\s*[$\\\\w./~\\\"'-]\", tool: \"write\", message: \"Use the write tool instead of shell redirection to write files.\" },\n];\n\nexport function checkBashInterception(\n\tcommand: string,\n\tavailableTools: readonly string[],\n\trules: readonly BashInterceptorRule[] = DEFAULT_BASH_INTERCEPTOR_RULES,\n): string | undefined {\n\tfor (const rule of rules) {\n\t\tif (!availableTools.includes(rule.tool)) continue;\n\t\tlet matcher: RegExp;\n\t\ttry { matcher = new RegExp(rule.pattern, rule.flags); }\n\t\tcatch (error) { throw new Error(`Invalid bash interceptor rule for ${rule.tool}: ${error instanceof Error ? error.message : String(error)}`); }\n\t\tif (matcher.test(command.trim())) {\n\t\t\treturn `Blocked: ${rule.message}\\n\\nOriginal command: ${command}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport function checkBashInterceptionCandidates(\n\tcandidates: Array<string | undefined>,\n\tavailableTools: readonly string[],\n\trules: readonly BashInterceptorRule[] = DEFAULT_BASH_INTERCEPTOR_RULES,\n): void {\n\tconst seen = new Set<string>();\n\tfor (const candidate of candidates) {\n\t\tconst command = candidate?.trim();\n\t\tif (!command || seen.has(command)) continue;\n\t\tseen.add(command);\n\t\tconst blocked = checkBashInterception(command, availableTools, rules);\n\t\tif (blocked) throw new Error(blocked);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const DEFAULT_BASH_INTERCEPTOR_RULES = [
|
|
2
|
+
{ pattern: "^\\s*(cat|head|tail|less|more)\\s+", tool: "read", message: "Use the read tool instead of shell commands to inspect file contents." },
|
|
3
|
+
{ pattern: "^\\s*(grep|rg|ripgrep|ag|ack)\\s+", tool: "search", message: "Use the search tool instead of shell grep commands." },
|
|
4
|
+
{ pattern: "^\\s*(find|fd|locate)\\s+.*(-name|-iname|-type|--type|-glob)", tool: "find", message: "Use the find tool instead of shell find/fd/locate commands." },
|
|
5
|
+
{ pattern: "^\\s*sed\\s+(-i|--in-place)", tool: "edit", message: "Use the edit tool instead of in-place sed." },
|
|
6
|
+
{ pattern: "^\\s*perl\\s+.*-[pn]?i", tool: "edit", message: "Use the edit tool instead of in-place perl." },
|
|
7
|
+
{ pattern: "^\\s*awk\\s+.*-i\\s+inplace", tool: "edit", message: "Use the edit tool instead of in-place awk." },
|
|
8
|
+
{ pattern: "^\\s*(echo|printf|cat\\s*<<)\\s+(?:[^\"'>]|\"[^\"]*\"|'[^']*')*(?<!\\|)>{1,2}\\|?\\s*[$\\w./~\"'-]", tool: "write", message: "Use the write tool instead of shell redirection to write files." },
|
|
9
|
+
];
|
|
10
|
+
export function checkBashInterception(command, availableTools, rules = DEFAULT_BASH_INTERCEPTOR_RULES) {
|
|
11
|
+
for (const rule of rules) {
|
|
12
|
+
if (!availableTools.includes(rule.tool))
|
|
13
|
+
continue;
|
|
14
|
+
let matcher;
|
|
15
|
+
try {
|
|
16
|
+
matcher = new RegExp(rule.pattern, rule.flags);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw new Error(`Invalid bash interceptor rule for ${rule.tool}: ${error instanceof Error ? error.message : String(error)}`);
|
|
20
|
+
}
|
|
21
|
+
if (matcher.test(command.trim())) {
|
|
22
|
+
return `Blocked: ${rule.message}\n\nOriginal command: ${command}`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export function checkBashInterceptionCandidates(candidates, availableTools, rules = DEFAULT_BASH_INTERCEPTOR_RULES) {
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
const command = candidate?.trim();
|
|
31
|
+
if (!command || seen.has(command))
|
|
32
|
+
continue;
|
|
33
|
+
seen.add(command);
|
|
34
|
+
const blocked = checkBashInterception(command, availableTools, rules);
|
|
35
|
+
if (blocked)
|
|
36
|
+
throw new Error(blocked);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=bash-interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-interceptor.js","sourceRoot":"","sources":["../../../src/core/tools/bash-interceptor.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,8BAA8B,GAA0B;IACpE,EAAE,OAAO,EAAE,oCAAoC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uEAAuE,EAAE;IACjJ,EAAE,OAAO,EAAE,mCAAmC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,qDAAqD,EAAE;IAChI,EAAE,OAAO,EAAE,8DAA8D,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,6DAA6D,EAAE;IACjK,EAAE,OAAO,EAAE,6BAA6B,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,4CAA4C,EAAE;IAC/G,EAAE,OAAO,EAAE,wBAAwB,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,6CAA6C,EAAE;IAC3G,EAAE,OAAO,EAAE,6BAA6B,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,4CAA4C,EAAE;IAC/G,EAAE,OAAO,EAAE,oGAAoG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,iEAAiE,EAAE;CAC5M,CAAC;AAEF,MAAM,UAAU,qBAAqB,CACpC,OAAe,EACf,cAAiC,EACjC,KAAK,GAAmC,8BAA8B;IAEtE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClD,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YAAC,OAAO,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAAC,CAAC;QACvD,OAAO,KAAK,EAAE,CAAC;YAAC,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,CAAC,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAAC,CAAC;QAC/I,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAClC,OAAO,YAAY,IAAI,CAAC,OAAO,yBAAyB,OAAO,EAAE,CAAC;QACnE,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC9C,UAAqC,EACrC,cAAiC,EACjC,KAAK,GAAmC,8BAA8B;IAEtE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QAC5C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClB,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;QACtE,IAAI,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;AACF,CAAC","sourcesContent":["export interface BashInterceptorRule {\n\tpattern: string;\n\tflags?: string;\n\ttool: string;\n\tmessage: string;\n}\n\nexport const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [\n\t{ pattern: \"^\\\\s*(cat|head|tail|less|more)\\\\s+\", tool: \"read\", message: \"Use the read tool instead of shell commands to inspect file contents.\" },\n\t{ pattern: \"^\\\\s*(grep|rg|ripgrep|ag|ack)\\\\s+\", tool: \"search\", message: \"Use the search tool instead of shell grep commands.\" },\n\t{ pattern: \"^\\\\s*(find|fd|locate)\\\\s+.*(-name|-iname|-type|--type|-glob)\", tool: \"find\", message: \"Use the find tool instead of shell find/fd/locate commands.\" },\n\t{ pattern: \"^\\\\s*sed\\\\s+(-i|--in-place)\", tool: \"edit\", message: \"Use the edit tool instead of in-place sed.\" },\n\t{ pattern: \"^\\\\s*perl\\\\s+.*-[pn]?i\", tool: \"edit\", message: \"Use the edit tool instead of in-place perl.\" },\n\t{ pattern: \"^\\\\s*awk\\\\s+.*-i\\\\s+inplace\", tool: \"edit\", message: \"Use the edit tool instead of in-place awk.\" },\n\t{ pattern: \"^\\\\s*(echo|printf|cat\\\\s*<<)\\\\s+(?:[^\\\"'>]|\\\"[^\\\"]*\\\"|'[^']*')*(?<!\\\\|)>{1,2}\\\\|?\\\\s*[$\\\\w./~\\\"'-]\", tool: \"write\", message: \"Use the write tool instead of shell redirection to write files.\" },\n];\n\nexport function checkBashInterception(\n\tcommand: string,\n\tavailableTools: readonly string[],\n\trules: readonly BashInterceptorRule[] = DEFAULT_BASH_INTERCEPTOR_RULES,\n): string | undefined {\n\tfor (const rule of rules) {\n\t\tif (!availableTools.includes(rule.tool)) continue;\n\t\tlet matcher: RegExp;\n\t\ttry { matcher = new RegExp(rule.pattern, rule.flags); }\n\t\tcatch (error) { throw new Error(`Invalid bash interceptor rule for ${rule.tool}: ${error instanceof Error ? error.message : String(error)}`); }\n\t\tif (matcher.test(command.trim())) {\n\t\t\treturn `Blocked: ${rule.message}\\n\\nOriginal command: ${command}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport function checkBashInterceptionCandidates(\n\tcandidates: Array<string | undefined>,\n\tavailableTools: readonly string[],\n\trules: readonly BashInterceptorRule[] = DEFAULT_BASH_INTERCEPTOR_RULES,\n): void {\n\tconst seen = new Set<string>();\n\tfor (const candidate of candidates) {\n\t\tconst command = candidate?.trim();\n\t\tif (!command || seen.has(command)) continue;\n\t\tseen.add(command);\n\t\tconst blocked = checkBashInterception(command, availableTools, rules);\n\t\tif (blocked) throw new Error(blocked);\n\t}\n}\n"]}
|