@wrongstack/core 0.297.0 → 0.298.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/dist/chronicle/index.js +126 -22
- package/dist/chronicle/project-server-client.d.ts +12 -0
- package/dist/chronicle/project-server-protocol.d.ts +22 -0
- package/dist/chronicle/project-server.js +238 -157
- package/dist/coordination/agents/index.js +136 -1
- package/dist/coordination/agents/role-skills.d.ts +3 -2
- package/dist/coordination/brain-trace.d.ts +2 -1
- package/dist/coordination/director/director-task-registry.d.ts +12 -0
- package/dist/coordination/fleet-supervisor.d.ts +21 -0
- package/dist/coordination/index.d.ts +23 -21
- package/dist/coordination/index.js +16493 -16068
- package/dist/coordination/mail-tools.d.ts +3 -3
- package/dist/coordination/mailbox-credential-store.d.ts +22 -2
- package/dist/coordination/mailbox-credential-throttle.d.ts +63 -0
- package/dist/coordination/mailbox-project-server-client.d.ts +16 -0
- package/dist/coordination/mailbox-project-server-protocol.d.ts +43 -5
- package/dist/coordination/mailbox-project-server.js +508 -253
- package/dist/coordination/provider-status-tracker.d.ts +2 -0
- package/dist/coordination/remote-mailbox-credential-store.d.ts +5 -5
- package/dist/coordination/remote-mailbox.d.ts +8 -4
- package/dist/coordination/sqlite-mailbox-credentials.d.ts +11 -0
- package/dist/core/fallback-profile-manager.d.ts +14 -0
- package/dist/core/index.js +451 -137
- package/dist/core/instruction-bundle.d.ts +12 -0
- package/dist/core/system-prompt-builder.d.ts +14 -0
- package/dist/core/system-prompt-skill-bodies.d.ts +1 -1
- package/dist/core/system-prompt-skill-text.d.ts +1 -2
- package/dist/defaults/index.js +2191 -1208
- package/dist/design/index.js +47 -8
- package/dist/execution/design-materialize.d.ts +22 -0
- package/dist/execution/index.js +495 -248
- package/dist/execution/skill-loader.d.ts +10 -1
- package/dist/execution/tool-error-taxonomy.d.ts +31 -0
- package/dist/execution/tool-executor.d.ts +1 -0
- package/dist/goal/index.js +3 -1
- package/dist/hooks/index.js +5 -0
- package/dist/hq/auth-audit.d.ts +6 -0
- package/dist/hq/auth-store.d.ts +59 -3
- package/dist/hq/index.js +401 -101
- package/dist/hq/kanban-store.d.ts +3 -0
- package/dist/hq/protocol/core.d.ts +3 -15
- package/dist/hq/protocol/envelope.d.ts +29 -0
- package/dist/hq/protocol/governance.d.ts +34 -0
- package/dist/hq/protocol/project.d.ts +3 -0
- package/dist/hq/protocol/resume.d.ts +1 -1
- package/dist/hq/protocol.d.ts +9 -8
- package/dist/hq/protocol.js +675 -0
- package/dist/index.js +9012 -6916
- package/dist/infrastructure/index.js +199 -102
- package/dist/kernel/events/session-events.d.ts +3 -0
- package/dist/kernel/events/tool-events.d.ts +3 -1
- package/dist/models/index.js +1 -1
- package/dist/observability/index.js +27 -0
- package/dist/observability/prometheus.d.ts +11 -0
- package/dist/plugin/index.d.ts +5 -1
- package/dist/plugin/index.js +988 -558
- package/dist/plugins/auto-review-plugin.d.ts +1 -11
- package/dist/plugins/review-store-maintenance.d.ts +26 -0
- package/dist/plugins/review-types.d.ts +6 -0
- package/dist/security/auto-approve-policy.d.ts +37 -0
- package/dist/security/capabilities.d.ts +45 -1
- package/dist/security/error-sanitize.d.ts +49 -0
- package/dist/security/file-permissions.d.ts +41 -0
- package/dist/security/index.d.ts +9 -6
- package/dist/security/index.js +3008 -2029
- package/dist/security/permission-helpers.d.ts +93 -0
- package/dist/security/permission-policy.d.ts +37 -35
- package/dist/security/secret-vault.d.ts +17 -0
- package/dist/security/totp.d.ts +86 -0
- package/dist/skills/frontmatter.d.ts +12 -1
- package/dist/skills/github-fetcher.d.ts +48 -0
- package/dist/skills/index.js +137 -26
- package/dist/skills/limits.d.ts +19 -0
- package/dist/skills/skill-installer.d.ts +13 -0
- package/dist/storage/index.js +929 -733
- package/dist/storage/provider-config-watcher.d.ts +1 -0
- package/dist/storage/session-helpers.d.ts +1 -0
- package/dist/storage/session-store.d.ts +9 -2
- package/dist/storage/session-workspace-checkpoints.d.ts +5 -0
- package/dist/tools/fallback-manage-tools.d.ts +6 -0
- package/dist/tools/index.js +287 -131
- package/dist/tools/mcp-use.d.ts +1 -1
- package/dist/types/blocks.d.ts +7 -0
- package/dist/types/config/autonomy.d.ts +15 -7
- package/dist/types/config/mcp-features.d.ts +9 -1
- package/dist/types/config/root.d.ts +10 -4
- package/dist/types/context-window.d.ts +15 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/secret-vault.d.ts +8 -0
- package/dist/types/session.d.ts +2 -0
- package/dist/types/skill.d.ts +56 -0
- package/dist/types/tool-executor.d.ts +2 -0
- package/dist/utils/env-typed.d.ts +64 -0
- package/dist/utils/glob-match.d.ts +21 -1
- package/dist/utils/heap-watchdog.js +1 -1
- package/dist/utils/incoming-images.d.ts +19 -0
- package/dist/utils/index.d.ts +16 -12
- package/dist/utils/index.js +3128 -2800
- package/dist/utils/path-segment.d.ts +29 -0
- package/dist/utils/sage-output-block.js +48 -0
- package/dist/utils/win32-cmd.d.ts +25 -0
- package/dist/worktree/index.js +5 -0
- package/instructions/llm/chimera-review.md +4 -6
- package/package.json +13 -4
- package/skills/auto-review/SKILL.md +11 -76
- package/skills/chimera/SKILL.md +31 -49
- package/skills/data-governance/SKILL.md +92 -0
- package/skills/wrongstack-kanban/SKILL.md +125 -64
- package/dist/agent-status-helpers.d.ts.map +0 -1
- package/dist/agent-status-tracker.d.ts.map +0 -1
- package/dist/boot.d.ts.map +0 -1
- package/dist/chronicle/context.d.ts.map +0 -1
- package/dist/chronicle/decision-adapter.d.ts.map +0 -1
- package/dist/chronicle/domain-adapter.d.ts.map +0 -1
- package/dist/chronicle/event-hash.d.ts.map +0 -1
- package/dist/chronicle/file-observer.d.ts.map +0 -1
- package/dist/chronicle/health-monitor.d.ts.map +0 -1
- package/dist/chronicle/identity.d.ts.map +0 -1
- package/dist/chronicle/index.d.ts.map +0 -1
- package/dist/chronicle/index.js.map +0 -7
- package/dist/chronicle/journal.d.ts.map +0 -1
- package/dist/chronicle/legacy-journal-import.d.ts.map +0 -1
- package/dist/chronicle/metrics-store.d.ts.map +0 -1
- package/dist/chronicle/partition-filename.d.ts.map +0 -1
- package/dist/chronicle/partition-range-cache.d.ts.map +0 -1
- package/dist/chronicle/process-adapter.d.ts.map +0 -1
- package/dist/chronicle/project-access.d.ts.map +0 -1
- package/dist/chronicle/project-server-client.d.ts.map +0 -1
- package/dist/chronicle/project-server-endpoint.d.ts.map +0 -1
- package/dist/chronicle/project-server-protocol.d.ts.map +0 -1
- package/dist/chronicle/project-server.d.ts.map +0 -1
- package/dist/chronicle/project-server.js.map +0 -7
- package/dist/chronicle/prompt-manifest.d.ts.map +0 -1
- package/dist/chronicle/provider-adapter.d.ts.map +0 -1
- package/dist/chronicle/query.d.ts.map +0 -1
- package/dist/chronicle/review-adapter.d.ts.map +0 -1
- package/dist/chronicle/rollup-adapter.d.ts.map +0 -1
- package/dist/chronicle/sink.d.ts.map +0 -1
- package/dist/chronicle/sqlite-journal.d.ts.map +0 -1
- package/dist/chronicle/sqlite-query.d.ts.map +0 -1
- package/dist/chronicle/stream-adapter.d.ts.map +0 -1
- package/dist/chronicle/tool-adapter.d.ts.map +0 -1
- package/dist/chronicle/types.d.ts.map +0 -1
- package/dist/coordination/adaptive-concurrency.d.ts.map +0 -1
- package/dist/coordination/agent-bridge.d.ts.map +0 -1
- package/dist/coordination/agent-monitor.d.ts.map +0 -1
- package/dist/coordination/agent-subagent-runner.d.ts.map +0 -1
- package/dist/coordination/agents/agent-prompts.d.ts.map +0 -1
- package/dist/coordination/agents/index.d.ts.map +0 -1
- package/dist/coordination/agents/index.js.map +0 -7
- package/dist/coordination/agents/phase1-discovery.d.ts.map +0 -1
- package/dist/coordination/agents/phase2-planning.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-build.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-techstack.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-wave1-platform.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-wave2-meta.d.ts.map +0 -1
- package/dist/coordination/agents/phase4-verify.d.ts.map +0 -1
- package/dist/coordination/agents/phase5-review.d.ts.map +0 -1
- package/dist/coordination/agents/phase6-domain.d.ts.map +0 -1
- package/dist/coordination/agents/phase7-knowledge.d.ts.map +0 -1
- package/dist/coordination/agents/phase8-delivery.d.ts.map +0 -1
- package/dist/coordination/agents/phase8-wave3-products.d.ts.map +0 -1
- package/dist/coordination/agents/phase9-meta.d.ts.map +0 -1
- package/dist/coordination/agents/phase9-wave4-platform-meta.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-config-validation.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-consolidation.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-files.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-identity-types.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-identity.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-knowledge-manifests.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-entries.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-normalize.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-policy.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-structured.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-paths.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-profile.d.ts.map +0 -1
- package/dist/coordination/agents/role-skills.d.ts.map +0 -1
- package/dist/coordination/agents/types.d.ts.map +0 -1
- package/dist/coordination/agents.d.ts.map +0 -1
- package/dist/coordination/auto-extend.d.ts.map +0 -1
- package/dist/coordination/autonomous-brain.d.ts.map +0 -1
- package/dist/coordination/autonomous-coordinator.d.ts.map +0 -1
- package/dist/coordination/brain-cache.d.ts.map +0 -1
- package/dist/coordination/brain-heuristics.d.ts.map +0 -1
- package/dist/coordination/brain-ledger.d.ts.map +0 -1
- package/dist/coordination/brain-monitor.d.ts.map +0 -1
- package/dist/coordination/brain-rules.d.ts.map +0 -1
- package/dist/coordination/brain-telemetry.d.ts.map +0 -1
- package/dist/coordination/brain-trace.d.ts.map +0 -1
- package/dist/coordination/brain.d.ts.map +0 -1
- package/dist/coordination/change-manager.d.ts.map +0 -1
- package/dist/coordination/checkpoint-wiring.d.ts.map +0 -1
- package/dist/coordination/collab-bus.d.ts.map +0 -1
- package/dist/coordination/collab-debug-types.d.ts.map +0 -1
- package/dist/coordination/collab-debug.d.ts.map +0 -1
- package/dist/coordination/collab-director-host.d.ts.map +0 -1
- package/dist/coordination/commit-safety.d.ts.map +0 -1
- package/dist/coordination/consensus-protocol.d.ts.map +0 -1
- package/dist/coordination/coordinator/error-classifier.d.ts.map +0 -1
- package/dist/coordination/delegate-tool.d.ts.map +0 -1
- package/dist/coordination/dep-watcher-bridge.d.ts.map +0 -1
- package/dist/coordination/dep-watcher.d.ts.map +0 -1
- package/dist/coordination/director/director-btw-notes.d.ts.map +0 -1
- package/dist/coordination/director/director-budget-policy.d.ts.map +0 -1
- package/dist/coordination/director/director-collab.d.ts.map +0 -1
- package/dist/coordination/director/director-errors.d.ts.map +0 -1
- package/dist/coordination/director/director-task-registry.d.ts.map +0 -1
- package/dist/coordination/director/director-toolset.d.ts.map +0 -1
- package/dist/coordination/director-basic-tools.d.ts.map +0 -1
- package/dist/coordination/director-collab-tools.d.ts.map +0 -1
- package/dist/coordination/director-host-contracts.d.ts.map +0 -1
- package/dist/coordination/director-input-helpers.d.ts.map +0 -1
- package/dist/coordination/director-kanban-queue-helpers.d.ts.map +0 -1
- package/dist/coordination/director-options.d.ts.map +0 -1
- package/dist/coordination/director-prompts.d.ts.map +0 -1
- package/dist/coordination/director-quality-gate-tool.d.ts.map +0 -1
- package/dist/coordination/director-session.d.ts.map +0 -1
- package/dist/coordination/director-spawn-model.d.ts.map +0 -1
- package/dist/coordination/director-tools.d.ts.map +0 -1
- package/dist/coordination/director.d.ts.map +0 -1
- package/dist/coordination/dispatcher.d.ts.map +0 -1
- package/dist/coordination/file-author-tracker.d.ts.map +0 -1
- package/dist/coordination/fleet-bus.d.ts.map +0 -1
- package/dist/coordination/fleet-event-validation.d.ts.map +0 -1
- package/dist/coordination/fleet-manager.d.ts.map +0 -1
- package/dist/coordination/fleet-spawn.d.ts.map +0 -1
- package/dist/coordination/fleet-status-tool.d.ts.map +0 -1
- package/dist/coordination/fleet-supervisor.d.ts.map +0 -1
- package/dist/coordination/fleet.d.ts.map +0 -1
- package/dist/coordination/global-mailbox-completion.d.ts.map +0 -1
- package/dist/coordination/global-mailbox-paths.d.ts.map +0 -1
- package/dist/coordination/icoordinator.d.ts.map +0 -1
- package/dist/coordination/ifleet-manager.d.ts.map +0 -1
- package/dist/coordination/in-memory-transport.d.ts.map +0 -1
- package/dist/coordination/index.d.ts.map +0 -1
- package/dist/coordination/index.js.map +0 -7
- package/dist/coordination/knowledge-graph.d.ts.map +0 -1
- package/dist/coordination/large-answer-store.d.ts.map +0 -1
- package/dist/coordination/mail-tools.d.ts.map +0 -1
- package/dist/coordination/mailbox-actions.d.ts.map +0 -1
- package/dist/coordination/mailbox-auth-types.d.ts.map +0 -1
- package/dist/coordination/mailbox-codecs.d.ts.map +0 -1
- package/dist/coordination/mailbox-constants.d.ts.map +0 -1
- package/dist/coordination/mailbox-credential-store.d.ts.map +0 -1
- package/dist/coordination/mailbox-events.d.ts.map +0 -1
- package/dist/coordination/mailbox-health.d.ts.map +0 -1
- package/dist/coordination/mailbox-hooks.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-auth.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-rate-limit.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-router.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-validation.d.ts.map +0 -1
- package/dist/coordination/mailbox-message-codec.d.ts.map +0 -1
- package/dist/coordination/mailbox-parse-state.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server-client.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server-endpoint.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server-protocol.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server.js.map +0 -7
- package/dist/coordination/mailbox-receipt-folding.d.ts.map +0 -1
- package/dist/coordination/mailbox-registry-codec.d.ts.map +0 -1
- package/dist/coordination/mailbox-retention-state.d.ts.map +0 -1
- package/dist/coordination/mailbox-status-mappers.d.ts.map +0 -1
- package/dist/coordination/mailbox-tool.d.ts.map +0 -1
- package/dist/coordination/mailbox-type-properties.d.ts.map +0 -1
- package/dist/coordination/mailbox-types.d.ts.map +0 -1
- package/dist/coordination/model-matrix.d.ts.map +0 -1
- package/dist/coordination/multi-agent-coordinator.d.ts.map +0 -1
- package/dist/coordination/multi-agent-timeout.d.ts.map +0 -1
- package/dist/coordination/null-fleet-bus.d.ts.map +0 -1
- package/dist/coordination/package-author-tracker.d.ts.map +0 -1
- package/dist/coordination/package-outdated-watcher.d.ts.map +0 -1
- package/dist/coordination/provider-status-tracker.d.ts.map +0 -1
- package/dist/coordination/remote-mailbox-credential-store.d.ts.map +0 -1
- package/dist/coordination/remote-mailbox.d.ts.map +0 -1
- package/dist/coordination/single-instance-mailbox.d.ts.map +0 -1
- package/dist/coordination/spawn-budget.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-compaction.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-credentials.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-rows.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-schema.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox.d.ts.map +0 -1
- package/dist/coordination/subagent-budget.d.ts.map +0 -1
- package/dist/coordination/subagent-nicknames.d.ts.map +0 -1
- package/dist/coordination/subagent-result-tool.d.ts.map +0 -1
- package/dist/coordination/task-auctioneer.d.ts.map +0 -1
- package/dist/coordination/task-dag.d.ts.map +0 -1
- package/dist/coordination/techstack-mailbox-consumer.d.ts.map +0 -1
- package/dist/coordination/transport.d.ts.map +0 -1
- package/dist/coordination/worktree-task-runner.d.ts.map +0 -1
- package/dist/core/agent-internals.d.ts.map +0 -1
- package/dist/core/agent-loop.d.ts.map +0 -1
- package/dist/core/agent-response.d.ts.map +0 -1
- package/dist/core/agent-tools.d.ts.map +0 -1
- package/dist/core/agent-types.d.ts.map +0 -1
- package/dist/core/agent.d.ts.map +0 -1
- package/dist/core/btw.d.ts.map +0 -1
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/continue-intent.d.ts.map +0 -1
- package/dist/core/continue-to-next-iteration.d.ts.map +0 -1
- package/dist/core/conversation-state.d.ts.map +0 -1
- package/dist/core/fallback-model.d.ts.map +0 -1
- package/dist/core/fallback-profile-manager.d.ts.map +0 -1
- package/dist/core/fleet-pulse.d.ts.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -7
- package/dist/core/input-builder.d.ts.map +0 -1
- package/dist/core/instruction-bundle.d.ts.map +0 -1
- package/dist/core/iteration-limit.d.ts.map +0 -1
- package/dist/core/mailbox-loop.d.ts.map +0 -1
- package/dist/core/model-availability-calendar.d.ts.map +0 -1
- package/dist/core/model-ref.d.ts.map +0 -1
- package/dist/core/model-ref.js.map +0 -7
- package/dist/core/modes/brief.d.ts.map +0 -1
- package/dist/core/modes/default.d.ts.map +0 -1
- package/dist/core/modes/teach.d.ts.map +0 -1
- package/dist/core/provider-runner.d.ts.map +0 -1
- package/dist/core/queued-messages.d.ts.map +0 -1
- package/dist/core/request-provider-binding.d.ts.map +0 -1
- package/dist/core/run-env.d.ts.map +0 -1
- package/dist/core/streaming-response-builder.d.ts.map +0 -1
- package/dist/core/system-prompt-blocks.d.ts.map +0 -1
- package/dist/core/system-prompt-builder.d.ts.map +0 -1
- package/dist/core/system-prompt-environment-probes.d.ts.map +0 -1
- package/dist/core/system-prompt-environment.d.ts.map +0 -1
- package/dist/core/system-prompt-memory-skills.d.ts.map +0 -1
- package/dist/core/system-prompt-plan.d.ts.map +0 -1
- package/dist/core/system-prompt-shell.d.ts.map +0 -1
- package/dist/core/system-prompt-skill-bodies.d.ts.map +0 -1
- package/dist/core/system-prompt-skill-text.d.ts.map +0 -1
- package/dist/defaults/index.d.ts.map +0 -1
- package/dist/defaults/index.js.map +0 -7
- package/dist/design/index.d.ts.map +0 -1
- package/dist/design/index.js.map +0 -7
- package/dist/execution/auto-compaction-middleware.d.ts.map +0 -1
- package/dist/execution/autonomous-runner.d.ts.map +0 -1
- package/dist/execution/autonomy-brain.d.ts.map +0 -1
- package/dist/execution/autonomy-prompt-contributor.d.ts.map +0 -1
- package/dist/execution/brain-chain.d.ts.map +0 -1
- package/dist/execution/brain-circuit.d.ts.map +0 -1
- package/dist/execution/brain-evaluation.d.ts.map +0 -1
- package/dist/execution/brain-runtime-constants.d.ts.map +0 -1
- package/dist/execution/brain-runtime.d.ts.map +0 -1
- package/dist/execution/compaction-core.d.ts.map +0 -1
- package/dist/execution/compaction-scoring.d.ts.map +0 -1
- package/dist/execution/compaction-summary-cache.d.ts.map +0 -1
- package/dist/execution/compactor.d.ts.map +0 -1
- package/dist/execution/council-brain.d.ts.map +0 -1
- package/dist/execution/council-orchestrator.d.ts.map +0 -1
- package/dist/execution/council-personas.d.ts.map +0 -1
- package/dist/execution/council-profiles.d.ts.map +0 -1
- package/dist/execution/council-prompts.d.ts.map +0 -1
- package/dist/execution/council-resolution.d.ts.map +0 -1
- package/dist/execution/design-color.d.ts.map +0 -1
- package/dist/execution/design-detect.d.ts.map +0 -1
- package/dist/execution/design-kit-loader.d.ts.map +0 -1
- package/dist/execution/design-materialize.d.ts.map +0 -1
- package/dist/execution/design-project-store.d.ts.map +0 -1
- package/dist/execution/design-tune.d.ts.map +0 -1
- package/dist/execution/design-verify.d.ts.map +0 -1
- package/dist/execution/enhance-recovery.d.ts.map +0 -1
- package/dist/execution/error-handler.d.ts.map +0 -1
- package/dist/execution/eternal-autonomy-types.d.ts.map +0 -1
- package/dist/execution/eternal-autonomy.d.ts.map +0 -1
- package/dist/execution/goal-preamble.d.ts.map +0 -1
- package/dist/execution/index.d.ts.map +0 -1
- package/dist/execution/index.js.map +0 -7
- package/dist/execution/intelligent-compactor.d.ts.map +0 -1
- package/dist/execution/model-runtime.d.ts.map +0 -1
- package/dist/execution/one-shot-llm.d.ts.map +0 -1
- package/dist/execution/parallel-eternal-engine.d.ts.map +0 -1
- package/dist/execution/prompt-enhancer.d.ts.map +0 -1
- package/dist/execution/prompt-enhancer.js.map +0 -7
- package/dist/execution/prompt-loader.d.ts.map +0 -1
- package/dist/execution/provider-runner-impl.d.ts.map +0 -1
- package/dist/execution/regex-patterns.d.ts.map +0 -1
- package/dist/execution/retry-policy.d.ts.map +0 -1
- package/dist/execution/selective-compactor.d.ts.map +0 -1
- package/dist/execution/skill-loader.d.ts.map +0 -1
- package/dist/execution/strategy-compactor.d.ts.map +0 -1
- package/dist/execution/subagent-compaction.d.ts.map +0 -1
- package/dist/execution/tool-executor-logging.d.ts.map +0 -1
- package/dist/execution/tool-executor-results.d.ts.map +0 -1
- package/dist/execution/tool-executor-stream.d.ts.map +0 -1
- package/dist/execution/tool-executor-support.d.ts.map +0 -1
- package/dist/execution/tool-executor.d.ts.map +0 -1
- package/dist/extension/extension-points.d.ts.map +0 -1
- package/dist/extension/index.d.ts.map +0 -1
- package/dist/extension/index.js.map +0 -7
- package/dist/extension/registry.d.ts.map +0 -1
- package/dist/fleet-notifier.d.ts.map +0 -1
- package/dist/goal/checkpoint.d.ts.map +0 -1
- package/dist/goal/goal-assessor.d.ts.map +0 -1
- package/dist/goal/goal-planner.d.ts.map +0 -1
- package/dist/goal/goal-runner.d.ts.map +0 -1
- package/dist/goal/index.d.ts.map +0 -1
- package/dist/goal/index.js.map +0 -7
- package/dist/goal/phase-board-mutations.d.ts.map +0 -1
- package/dist/goal/phase-graph-builder.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-integration.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-queries.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-runtime.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-types.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator.d.ts.map +0 -1
- package/dist/goal/phase-store.d.ts.map +0 -1
- package/dist/goal/types.d.ts.map +0 -1
- package/dist/hooks/http-executor.d.ts.map +0 -1
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js.map +0 -7
- package/dist/hooks/registry.d.ts.map +0 -1
- package/dist/hooks/runner.d.ts.map +0 -1
- package/dist/hooks/shell-executor.d.ts.map +0 -1
- package/dist/hooks/shell-hooks-equal.d.ts.map +0 -1
- package/dist/hq/alerts.d.ts.map +0 -1
- package/dist/hq/auth-audit.d.ts.map +0 -1
- package/dist/hq/auth-store.d.ts.map +0 -1
- package/dist/hq/bootstrap-store.d.ts.map +0 -1
- package/dist/hq/brain-bridge.d.ts.map +0 -1
- package/dist/hq/bridge-context.d.ts.map +0 -1
- package/dist/hq/commands.d.ts.map +0 -1
- package/dist/hq/cost-bridge.d.ts.map +0 -1
- package/dist/hq/exposure.d.ts.map +0 -1
- package/dist/hq/factory.d.ts.map +0 -1
- package/dist/hq/fleet-bridge.d.ts.map +0 -1
- package/dist/hq/index.d.ts.map +0 -1
- package/dist/hq/index.js.map +0 -7
- package/dist/hq/kanban-store.d.ts.map +0 -1
- package/dist/hq/mailbox-mapper.d.ts.map +0 -1
- package/dist/hq/persistence/event-log.d.ts.map +0 -1
- package/dist/hq/persistence/jsonl-io.d.ts.map +0 -1
- package/dist/hq/persistence/simple-log.d.ts.map +0 -1
- package/dist/hq/persistence/snapshot-store.d.ts.map +0 -1
- package/dist/hq/persistence/timeseries-store.d.ts.map +0 -1
- package/dist/hq/persistence.d.ts.map +0 -1
- package/dist/hq/protocol/brain.d.ts.map +0 -1
- package/dist/hq/protocol/browser.d.ts.map +0 -1
- package/dist/hq/protocol/client.d.ts.map +0 -1
- package/dist/hq/protocol/core.d.ts.map +0 -1
- package/dist/hq/protocol/fleet.d.ts.map +0 -1
- package/dist/hq/protocol/kanban.d.ts.map +0 -1
- package/dist/hq/protocol/mailbox.d.ts.map +0 -1
- package/dist/hq/protocol/mcp.d.ts.map +0 -1
- package/dist/hq/protocol/peer.d.ts.map +0 -1
- package/dist/hq/protocol/project.d.ts.map +0 -1
- package/dist/hq/protocol/resume.d.ts.map +0 -1
- package/dist/hq/protocol/session.d.ts.map +0 -1
- package/dist/hq/protocol/tool.d.ts.map +0 -1
- package/dist/hq/protocol.d.ts.map +0 -1
- package/dist/hq/publisher.d.ts.map +0 -1
- package/dist/hq/redaction.d.ts.map +0 -1
- package/dist/hq/session-bridge.d.ts.map +0 -1
- package/dist/hq/tool-bridge.d.ts.map +0 -1
- package/dist/hq/transcript-mapper.d.ts.map +0 -1
- package/dist/hq/worktree-bridge.d.ts.map +0 -1
- package/dist/hq/write-queues.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -7
- package/dist/infrastructure/context-manager.d.ts.map +0 -1
- package/dist/infrastructure/index.d.ts.map +0 -1
- package/dist/infrastructure/index.js.map +0 -7
- package/dist/infrastructure/logger.d.ts.map +0 -1
- package/dist/infrastructure/mcp-servers.d.ts.map +0 -1
- package/dist/infrastructure/path-resolver.d.ts.map +0 -1
- package/dist/infrastructure/provider-cache-ledger.d.ts.map +0 -1
- package/dist/infrastructure/token-counter.d.ts.map +0 -1
- package/dist/kernel/container.d.ts.map +0 -1
- package/dist/kernel/events/agent-events.d.ts.map +0 -1
- package/dist/kernel/events/brain-events.d.ts.map +0 -1
- package/dist/kernel/events/file-events.d.ts.map +0 -1
- package/dist/kernel/events/fleet-events.d.ts.map +0 -1
- package/dist/kernel/events/memory-events.d.ts.map +0 -1
- package/dist/kernel/events/network-events.d.ts.map +0 -1
- package/dist/kernel/events/process-events.d.ts.map +0 -1
- package/dist/kernel/events/provider-events.d.ts.map +0 -1
- package/dist/kernel/events/sdd-events.d.ts.map +0 -1
- package/dist/kernel/events/session-events.d.ts.map +0 -1
- package/dist/kernel/events/tool-events.d.ts.map +0 -1
- package/dist/kernel/events/worktree-events.d.ts.map +0 -1
- package/dist/kernel/events.d.ts.map +0 -1
- package/dist/kernel/index.d.ts.map +0 -1
- package/dist/kernel/index.js.map +0 -7
- package/dist/kernel/pipeline.d.ts.map +0 -1
- package/dist/kernel/run-controller.d.ts.map +0 -1
- package/dist/kernel/tokens.d.ts.map +0 -1
- package/dist/mailbox-attach.d.ts.map +0 -1
- package/dist/middleware/collab-pause.d.ts.map +0 -1
- package/dist/models/alibaba-token-plan-catalog.d.ts.map +0 -1
- package/dist/models/codex-catalog.d.ts.map +0 -1
- package/dist/models/index.d.ts.map +0 -1
- package/dist/models/index.js.map +0 -7
- package/dist/models/llm-selector.d.ts.map +0 -1
- package/dist/models/mode-store.d.ts.map +0 -1
- package/dist/models/model-intelligence.d.ts.map +0 -1
- package/dist/models/model-router.d.ts.map +0 -1
- package/dist/models/models-registry.d.ts.map +0 -1
- package/dist/models/provider-model-resolve.d.ts.map +0 -1
- package/dist/notifications/index.d.ts.map +0 -1
- package/dist/notifications/index.js.map +0 -7
- package/dist/notifications/notifier-impl.d.ts.map +0 -1
- package/dist/notifications/notifier.d.ts.map +0 -1
- package/dist/notifications/types.d.ts.map +0 -1
- package/dist/observability/event-bridge.d.ts.map +0 -1
- package/dist/observability/health.d.ts.map +0 -1
- package/dist/observability/index.d.ts.map +0 -1
- package/dist/observability/index.js.map +0 -7
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/network-telemetry.d.ts.map +0 -1
- package/dist/observability/otel-tracer.d.ts.map +0 -1
- package/dist/observability/otlp-metrics.d.ts.map +0 -1
- package/dist/observability/otlp-traces.d.ts.map +0 -1
- package/dist/observability/process-telemetry.d.ts.map +0 -1
- package/dist/observability/prometheus.d.ts.map +0 -1
- package/dist/observability/redact-command.d.ts.map +0 -1
- package/dist/observability/tracer.d.ts.map +0 -1
- package/dist/plugin/api.d.ts.map +0 -1
- package/dist/plugin/config.d.ts.map +0 -1
- package/dist/plugin/index.d.ts.map +0 -1
- package/dist/plugin/index.js.map +0 -7
- package/dist/plugin/loader.d.ts.map +0 -1
- package/dist/plugins/auto-review-plugin.d.ts.map +0 -1
- package/dist/plugins/chimera-plugin.d.ts.map +0 -1
- package/dist/plugins/prompts-plugin.d.ts.map +0 -1
- package/dist/plugins/review-claim-registry.d.ts.map +0 -1
- package/dist/plugins/review-context-builder.d.ts.map +0 -1
- package/dist/plugins/review-finding-commands.d.ts.map +0 -1
- package/dist/plugins/review-finding-integration.d.ts.map +0 -1
- package/dist/plugins/review-finding-parser.d.ts.map +0 -1
- package/dist/plugins/review-finding-store.d.ts.map +0 -1
- package/dist/plugins/review-finding-types.d.ts.map +0 -1
- package/dist/plugins/review-report-integration.d.ts.map +0 -1
- package/dist/plugins/review-report-store.d.ts.map +0 -1
- package/dist/plugins/review-report-types.d.ts.map +0 -1
- package/dist/plugins/review-types.d.ts.map +0 -1
- package/dist/plugins/skills-plugin.d.ts.map +0 -1
- package/dist/plugins/sync-plugin.d.ts.map +0 -1
- package/dist/prompts/index.d.ts.map +0 -1
- package/dist/prompts/prompt-installer.d.ts.map +0 -1
- package/dist/prompts/prompt-manifest-store.d.ts.map +0 -1
- package/dist/registry/index.d.ts.map +0 -1
- package/dist/registry/index.js.map +0 -7
- package/dist/registry/provider-registry.d.ts.map +0 -1
- package/dist/registry/slash-command-registry.d.ts.map +0 -1
- package/dist/registry/tool-registry.d.ts.map +0 -1
- package/dist/replay/hash.d.ts.map +0 -1
- package/dist/replay/index.d.ts.map +0 -1
- package/dist/replay/index.js.map +0 -7
- package/dist/replay/replay-provider-runner.d.ts.map +0 -1
- package/dist/security/capabilities.d.ts.map +0 -1
- package/dist/security/config-secrets.d.ts.map +0 -1
- package/dist/security/directory-permission-policy.d.ts.map +0 -1
- package/dist/security/directory-policy-schema.d.ts.map +0 -1
- package/dist/security/index.d.ts.map +0 -1
- package/dist/security/index.js.map +0 -7
- package/dist/security/kanban-boundary.d.ts.map +0 -1
- package/dist/security/permission-policy-schema.d.ts.map +0 -1
- package/dist/security/permission-policy.d.ts.map +0 -1
- package/dist/security/readonly-permission-policy.d.ts.map +0 -1
- package/dist/security/secret-scrubber.d.ts.map +0 -1
- package/dist/security/secret-vault.d.ts.map +0 -1
- package/dist/security/trust-boundary.d.ts.map +0 -1
- package/dist/security/yolo-risk.d.ts.map +0 -1
- package/dist/session-registry-atomic-file.d.ts.map +0 -1
- package/dist/session-registry-types.d.ts.map +0 -1
- package/dist/session-registry.d.ts.map +0 -1
- package/dist/skills/foreign-sources.d.ts.map +0 -1
- package/dist/skills/frontmatter.d.ts.map +0 -1
- package/dist/skills/github-fetcher.d.ts.map +0 -1
- package/dist/skills/index.d.ts.map +0 -1
- package/dist/skills/index.js.map +0 -7
- package/dist/skills/limits.d.ts.map +0 -1
- package/dist/skills/manifest-store.d.ts.map +0 -1
- package/dist/skills/registry/github-direct-adapter.d.ts.map +0 -1
- package/dist/skills/registry/registry-adapter.d.ts.map +0 -1
- package/dist/skills/registry/skills-sh-adapter.d.ts.map +0 -1
- package/dist/skills/skill-generator.d.ts.map +0 -1
- package/dist/skills/skill-installer.d.ts.map +0 -1
- package/dist/storage/annotations-store.d.ts.map +0 -1
- package/dist/storage/attachment-store.d.ts.map +0 -1
- package/dist/storage/cloud-sync.d.ts.map +0 -1
- package/dist/storage/completed-work-checkpoint.d.ts.map +0 -1
- package/dist/storage/config-loader/bootstrap.d.ts.map +0 -1
- package/dist/storage/config-loader/default-repair.d.ts.map +0 -1
- package/dist/storage/config-loader/defaults.d.ts.map +0 -1
- package/dist/storage/config-loader/diagnostics.d.ts.map +0 -1
- package/dist/storage/config-loader/env-overrides.d.ts.map +0 -1
- package/dist/storage/config-loader/in-project-policy.d.ts.map +0 -1
- package/dist/storage/config-loader/inline-provider-models.d.ts.map +0 -1
- package/dist/storage/config-loader/legacy-sage-migration.d.ts.map +0 -1
- package/dist/storage/config-loader/path-identity.d.ts.map +0 -1
- package/dist/storage/config-loader/types.d.ts.map +0 -1
- package/dist/storage/config-loader/validation.d.ts.map +0 -1
- package/dist/storage/config-loader.d.ts.map +0 -1
- package/dist/storage/config-migration.d.ts.map +0 -1
- package/dist/storage/config-store.d.ts.map +0 -1
- package/dist/storage/director-state.d.ts.map +0 -1
- package/dist/storage/file-session-writer.d.ts.map +0 -1
- package/dist/storage/goal-coordination.d.ts.map +0 -1
- package/dist/storage/goal-kanban.d.ts.map +0 -1
- package/dist/storage/goal-store.d.ts.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -7
- package/dist/storage/input-history-store.d.ts.map +0 -1
- package/dist/storage/memory-backend.d.ts.map +0 -1
- package/dist/storage/memory-consolidator.d.ts.map +0 -1
- package/dist/storage/memory-graph-backend.d.ts.map +0 -1
- package/dist/storage/plan-store.d.ts.map +0 -1
- package/dist/storage/plan-templates.d.ts.map +0 -1
- package/dist/storage/prompt-store.d.ts.map +0 -1
- package/dist/storage/prompt-usage-store.d.ts.map +0 -1
- package/dist/storage/provider-config-watcher.d.ts.map +0 -1
- package/dist/storage/queue-store.d.ts.map +0 -1
- package/dist/storage/recovery-lock.d.ts.map +0 -1
- package/dist/storage/replay-log-store.d.ts.map +0 -1
- package/dist/storage/session-analyzer.d.ts.map +0 -1
- package/dist/storage/session-checkpoint-cas.d.ts.map +0 -1
- package/dist/storage/session-event-bridge.d.ts.map +0 -1
- package/dist/storage/session-helpers.d.ts.map +0 -1
- package/dist/storage/session-id-resolver.d.ts.map +0 -1
- package/dist/storage/session-id.d.ts.map +0 -1
- package/dist/storage/session-read-scrubber.d.ts.map +0 -1
- package/dist/storage/session-reader.d.ts.map +0 -1
- package/dist/storage/session-recovery.d.ts.map +0 -1
- package/dist/storage/session-resume-validation.d.ts.map +0 -1
- package/dist/storage/session-rewind-apply.d.ts.map +0 -1
- package/dist/storage/session-rewinder.d.ts.map +0 -1
- package/dist/storage/session-store/delete-session-artifacts.d.ts.map +0 -1
- package/dist/storage/session-store/delete-session-guards.d.ts.map +0 -1
- package/dist/storage/session-store/directory-scan.d.ts.map +0 -1
- package/dist/storage/session-store/directory-session-files.d.ts.map +0 -1
- package/dist/storage/session-store/events.d.ts.map +0 -1
- package/dist/storage/session-store/fork-session.d.ts.map +0 -1
- package/dist/storage/session-store/index-reader.d.ts.map +0 -1
- package/dist/storage/session-store/load-cache.d.ts.map +0 -1
- package/dist/storage/session-store/load-session-data.d.ts.map +0 -1
- package/dist/storage/session-store/paths.d.ts.map +0 -1
- package/dist/storage/session-store/prune-helpers.d.ts.map +0 -1
- package/dist/storage/session-store/replay.d.ts.map +0 -1
- package/dist/storage/session-store/search-events.d.ts.map +0 -1
- package/dist/storage/session-store/shard-manifest.d.ts.map +0 -1
- package/dist/storage/session-store/summary-builder.d.ts.map +0 -1
- package/dist/storage/session-store/summary-header.d.ts.map +0 -1
- package/dist/storage/session-store/types.d.ts.map +0 -1
- package/dist/storage/session-store.d.ts.map +0 -1
- package/dist/storage/session-summary.d.ts.map +0 -1
- package/dist/storage/session-tool-call-ends.d.ts.map +0 -1
- package/dist/storage/session-writer-scrubber.d.ts.map +0 -1
- package/dist/storage/session-writer-truncate.d.ts.map +0 -1
- package/dist/storage/storage-concurrency.d.ts.map +0 -1
- package/dist/storage/task-store.d.ts.map +0 -1
- package/dist/storage/todos-checkpoint.d.ts.map +0 -1
- package/dist/storage/tool-audit-log.d.ts.map +0 -1
- package/dist/tasking/index.d.ts.map +0 -1
- package/dist/tasking/index.js.map +0 -7
- package/dist/tasking/task-store.d.ts.map +0 -1
- package/dist/tasking/task-tracker.d.ts.map +0 -1
- package/dist/tools/council-tool.d.ts.map +0 -1
- package/dist/tools/fallback-chain-manage-tool.d.ts.map +0 -1
- package/dist/tools/fallback-favorite-manage-tool.d.ts.map +0 -1
- package/dist/tools/fallback-manage-helpers.d.ts.map +0 -1
- package/dist/tools/fallback-manage-tool-options.d.ts.map +0 -1
- package/dist/tools/fallback-manage-tools.d.ts.map +0 -1
- package/dist/tools/fallback-model-ref-parse.d.ts.map +0 -1
- package/dist/tools/fallback-provider-key-store.d.ts.map +0 -1
- package/dist/tools/fallback-system-config-view-tool.d.ts.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -7
- package/dist/tools/mcp-control.d.ts.map +0 -1
- package/dist/tools/mcp-use.d.ts.map +0 -1
- package/dist/tools/one-shot-llm-tool.d.ts.map +0 -1
- package/dist/tools/plugin-manager.d.ts.map +0 -1
- package/dist/types/agent-bridge.d.ts.map +0 -1
- package/dist/types/attachment.d.ts.map +0 -1
- package/dist/types/autonomy.d.ts.map +0 -1
- package/dist/types/blocks.d.ts.map +0 -1
- package/dist/types/compactor.d.ts.map +0 -1
- package/dist/types/config/autonomy.d.ts.map +0 -1
- package/dist/types/config/context.d.ts.map +0 -1
- package/dist/types/config/fleet-chat.d.ts.map +0 -1
- package/dist/types/config/mcp-features.d.ts.map +0 -1
- package/dist/types/config/providers.d.ts.map +0 -1
- package/dist/types/config/root.d.ts.map +0 -1
- package/dist/types/config/runtime.d.ts.map +0 -1
- package/dist/types/config/skills-fleet-brain.d.ts.map +0 -1
- package/dist/types/config/tools.d.ts.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/context-evidence.d.ts.map +0 -1
- package/dist/types/context-window.d.ts.map +0 -1
- package/dist/types/council.d.ts.map +0 -1
- package/dist/types/default-config.d.ts.map +0 -1
- package/dist/types/design-kit.d.ts.map +0 -1
- package/dist/types/error-handler.d.ts.map +0 -1
- package/dist/types/errors.d.ts.map +0 -1
- package/dist/types/file-event-record.d.ts.map +0 -1
- package/dist/types/hooks.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -7
- package/dist/types/input-reader.d.ts.map +0 -1
- package/dist/types/logger.d.ts.map +0 -1
- package/dist/types/memory.d.ts.map +0 -1
- package/dist/types/messages.d.ts.map +0 -1
- package/dist/types/mode-prompts.d.ts.map +0 -1
- package/dist/types/mode.d.ts.map +0 -1
- package/dist/types/models-registry.d.ts.map +0 -1
- package/dist/types/multi-agent.d.ts.map +0 -1
- package/dist/types/observability.d.ts.map +0 -1
- package/dist/types/one-shot-llm.d.ts.map +0 -1
- package/dist/types/path-resolver.d.ts.map +0 -1
- package/dist/types/permission.d.ts.map +0 -1
- package/dist/types/plugin.d.ts.map +0 -1
- package/dist/types/prompt-registry.d.ts.map +0 -1
- package/dist/types/prompt.d.ts.map +0 -1
- package/dist/types/provider-runner.d.ts.map +0 -1
- package/dist/types/provider.d.ts.map +0 -1
- package/dist/types/quota-regex.d.ts.map +0 -1
- package/dist/types/renderer.d.ts.map +0 -1
- package/dist/types/retry-policy.d.ts.map +0 -1
- package/dist/types/secret-scrubber.d.ts.map +0 -1
- package/dist/types/secret-vault.d.ts.map +0 -1
- package/dist/types/selector.d.ts.map +0 -1
- package/dist/types/session-markers.d.ts.map +0 -1
- package/dist/types/session-markers.js.map +0 -7
- package/dist/types/session-reader.d.ts.map +0 -1
- package/dist/types/session-rewinder.d.ts.map +0 -1
- package/dist/types/session.d.ts.map +0 -1
- package/dist/types/side-effect.d.ts.map +0 -1
- package/dist/types/skill.d.ts.map +0 -1
- package/dist/types/slash-command.d.ts.map +0 -1
- package/dist/types/spec.d.ts.map +0 -1
- package/dist/types/system-prompt-contributor.d.ts.map +0 -1
- package/dist/types/system-prompt.d.ts.map +0 -1
- package/dist/types/task-graph.d.ts.map +0 -1
- package/dist/types/token-counter.d.ts.map +0 -1
- package/dist/types/tool-executor.d.ts.map +0 -1
- package/dist/types/tool-markers.d.ts.map +0 -1
- package/dist/types/tool.d.ts.map +0 -1
- package/dist/types/utility-types.d.ts.map +0 -1
- package/dist/utils/assert-never.d.ts.map +0 -1
- package/dist/utils/atomic-write.d.ts.map +0 -1
- package/dist/utils/cache-key.d.ts.map +0 -1
- package/dist/utils/child-env.d.ts.map +0 -1
- package/dist/utils/color.d.ts.map +0 -1
- package/dist/utils/compaction-preview.d.ts.map +0 -1
- package/dist/utils/config-backup.d.ts.map +0 -1
- package/dist/utils/config-json.d.ts.map +0 -1
- package/dist/utils/connectivity.d.ts.map +0 -1
- package/dist/utils/context-breakdown.d.ts.map +0 -1
- package/dist/utils/context-evidence.d.ts.map +0 -1
- package/dist/utils/continuous-memory-profiler.d.ts.map +0 -1
- package/dist/utils/crash-shield.d.ts.map +0 -1
- package/dist/utils/deep-merge.d.ts.map +0 -1
- package/dist/utils/diff.d.ts.map +0 -1
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -7
- package/dist/utils/expect-defined.d.ts.map +0 -1
- package/dist/utils/expect-defined.js.map +0 -7
- package/dist/utils/glob-expand.d.ts.map +0 -1
- package/dist/utils/glob-match.d.ts.map +0 -1
- package/dist/utils/heap-watchdog-types.d.ts.map +0 -1
- package/dist/utils/heap-watchdog.d.ts.map +0 -1
- package/dist/utils/heap-watchdog.js.map +0 -7
- package/dist/utils/incoming-images.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -7
- package/dist/utils/instruction-file.d.ts.map +0 -1
- package/dist/utils/ip-guard.d.ts.map +0 -1
- package/dist/utils/json-repair.d.ts.map +0 -1
- package/dist/utils/json-schema-validate.d.ts.map +0 -1
- package/dist/utils/lru-cache.d.ts.map +0 -1
- package/dist/utils/memory-flight-recorder.d.ts.map +0 -1
- package/dist/utils/merge-custom-models.d.ts.map +0 -1
- package/dist/utils/merge-models-payload.d.ts.map +0 -1
- package/dist/utils/message-invariants.d.ts.map +0 -1
- package/dist/utils/newline-normalize.d.ts.map +0 -1
- package/dist/utils/perf-profile.d.ts.map +0 -1
- package/dist/utils/pid.d.ts.map +0 -1
- package/dist/utils/project-identity.d.ts.map +0 -1
- package/dist/utils/project-watch.d.ts.map +0 -1
- package/dist/utils/regex-guard.d.ts.map +0 -1
- package/dist/utils/safe-json.d.ts.map +0 -1
- package/dist/utils/sage-output-block.d.ts.map +0 -1
- package/dist/utils/session-scoped-path.d.ts.map +0 -1
- package/dist/utils/sleep.d.ts.map +0 -1
- package/dist/utils/slug.d.ts.map +0 -1
- package/dist/utils/socket-path.d.ts.map +0 -1
- package/dist/utils/sqlite-warning.d.ts.map +0 -1
- package/dist/utils/string.d.ts.map +0 -1
- package/dist/utils/task-format.d.ts.map +0 -1
- package/dist/utils/term.d.ts.map +0 -1
- package/dist/utils/todos-format.d.ts.map +0 -1
- package/dist/utils/token-estimate.d.ts.map +0 -1
- package/dist/utils/tool-description-mode.d.ts.map +0 -1
- package/dist/utils/tool-name.d.ts.map +0 -1
- package/dist/utils/tool-output-serializer.d.ts.map +0 -1
- package/dist/utils/tool-result-render-mode.d.ts.map +0 -1
- package/dist/utils/tool-subject.d.ts.map +0 -1
- package/dist/utils/tool-wire-compact.d.ts.map +0 -1
- package/dist/utils/tree-kill.d.ts.map +0 -1
- package/dist/utils/tree-kill.js.map +0 -7
- package/dist/utils/ulid.d.ts.map +0 -1
- package/dist/utils/walk-ignore.d.ts.map +0 -1
- package/dist/utils/wstack-paths.d.ts.map +0 -1
- package/dist/worktree/index.d.ts.map +0 -1
- package/dist/worktree/index.js.map +0 -7
- package/dist/worktree/worktree-git.d.ts.map +0 -1
- package/dist/worktree/worktree-managed-ops.d.ts.map +0 -1
- package/dist/worktree/worktree-manager.d.ts.map +0 -1
- package/dist/worktree/worktree-types.d.ts.map +0 -1
package/dist/defaults/index.js
CHANGED
|
@@ -295,7 +295,7 @@ var InMemoryAgentBridge = class {
|
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
297
|
this.inflightGuards.add(correlationId);
|
|
298
|
-
return new Promise((
|
|
298
|
+
return new Promise((resolve17, reject) => {
|
|
299
299
|
const timer = setTimeout(() => {
|
|
300
300
|
this.inflightGuards.delete(correlationId);
|
|
301
301
|
this.pendingRequests.delete(correlationId);
|
|
@@ -314,7 +314,7 @@ var InMemoryAgentBridge = class {
|
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
316
|
this.pendingRequests.set(correlationId, {
|
|
317
|
-
resolve:
|
|
317
|
+
resolve: resolve17,
|
|
318
318
|
reject,
|
|
319
319
|
timer
|
|
320
320
|
});
|
|
@@ -663,13 +663,13 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
663
663
|
if (!bus?.hasListenerFor("budget.threshold_reached")) {
|
|
664
664
|
return Promise.resolve("stop");
|
|
665
665
|
}
|
|
666
|
-
return new Promise((
|
|
666
|
+
return new Promise((resolve17) => {
|
|
667
667
|
let resolved = false;
|
|
668
668
|
const respond = (d) => {
|
|
669
669
|
if (resolved) return;
|
|
670
670
|
resolved = true;
|
|
671
671
|
clearTimeout(fallback);
|
|
672
|
-
|
|
672
|
+
resolve17(d);
|
|
673
673
|
};
|
|
674
674
|
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
675
675
|
const sessionId = this.currentSessionId();
|
|
@@ -808,7 +808,26 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
808
808
|
var ToolCapabilities = {
|
|
809
809
|
/** Can execute arbitrary commands in the user's shell (the `bash` tool). */
|
|
810
810
|
SHELL_ARBITRARY: "shell.arbitrary",
|
|
811
|
-
/**
|
|
811
|
+
/**
|
|
812
|
+
* Can execute the `exec` tool's allowlisted commands.
|
|
813
|
+
*
|
|
814
|
+
* "Restricted" describes the command NAME check, not the resulting power.
|
|
815
|
+
* Treat this as equivalent to {@link SHELL_ARBITRARY} when deciding what to
|
|
816
|
+
* grant: the allowlist includes general-purpose interpreters (`node`,
|
|
817
|
+
* `python`, `perl`, `ruby`) and, on Windows, the platform shells (`cmd`,
|
|
818
|
+
* `powershell`, `pwsh`) — deliberately, since without them `exec` cannot run
|
|
819
|
+
* `dir`/`type`/any cmdlet. `node -e …` or `cmd /c …` therefore reaches
|
|
820
|
+
* arbitrary execution, and per-argument denylisting cannot close that while
|
|
821
|
+
* the shells remain (blocking `node -e` still leaves `cmd /c node -e`).
|
|
822
|
+
*
|
|
823
|
+
* What actually bounds this is downstream, not the name check: the permission
|
|
824
|
+
* policy reconstructs the full command line and runs it through the
|
|
825
|
+
* destructive classifier, so destructive calls still require confirmation.
|
|
826
|
+
*
|
|
827
|
+
* Granting `shell.restricted` while withholding `shell.arbitrary` therefore
|
|
828
|
+
* buys no meaningful reduction in blast radius. Documented rather than
|
|
829
|
+
* silently implied, because the two read as a narrow/wide pair (WS-083).
|
|
830
|
+
*/
|
|
812
831
|
SHELL_RESTRICTED: "shell.restricted",
|
|
813
832
|
/** Can run a restricted project formatter/linter-style command. */
|
|
814
833
|
SHELL_EXEC: "shell.exec",
|
|
@@ -5284,7 +5303,7 @@ function createDelegateTool(opts) {
|
|
|
5284
5303
|
};
|
|
5285
5304
|
}
|
|
5286
5305
|
async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abortSignal) {
|
|
5287
|
-
return new Promise((
|
|
5306
|
+
return new Promise((resolve17) => {
|
|
5288
5307
|
let settled = false;
|
|
5289
5308
|
let timer;
|
|
5290
5309
|
let offAbort = () => {
|
|
@@ -5297,7 +5316,7 @@ async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abo
|
|
|
5297
5316
|
offIter();
|
|
5298
5317
|
offProgress();
|
|
5299
5318
|
offAbort();
|
|
5300
|
-
|
|
5319
|
+
resolve17(value);
|
|
5301
5320
|
};
|
|
5302
5321
|
const arm = () => {
|
|
5303
5322
|
if (timer) clearTimeout(timer);
|
|
@@ -7151,10 +7170,10 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
|
|
|
7151
7170
|
pending: taskIds.filter((id) => !done.has(id))
|
|
7152
7171
|
});
|
|
7153
7172
|
}
|
|
7154
|
-
return new Promise((
|
|
7173
|
+
return new Promise((resolve17) => {
|
|
7155
7174
|
const entry = {
|
|
7156
7175
|
ids: new Set(taskIds),
|
|
7157
|
-
resolve: (result) =>
|
|
7176
|
+
resolve: (result) => resolve17({
|
|
7158
7177
|
completed: [result],
|
|
7159
7178
|
pending: taskIds.filter((id) => id !== result.taskId)
|
|
7160
7179
|
})
|
|
@@ -7162,7 +7181,7 @@ var DirectorTaskRegistry = class _DirectorTaskRegistry {
|
|
|
7162
7181
|
if (opts?.timeoutMs !== void 0) {
|
|
7163
7182
|
entry.timer = setTimeout(() => {
|
|
7164
7183
|
this.anyWaiters.delete(entry);
|
|
7165
|
-
|
|
7184
|
+
resolve17({ completed: [], pending: [...taskIds], timedOut: true });
|
|
7166
7185
|
}, opts.timeoutMs);
|
|
7167
7186
|
}
|
|
7168
7187
|
this.anyWaiters.add(entry);
|
|
@@ -7236,6 +7255,25 @@ ${JSON.stringify(result.result, null, 2)}
|
|
|
7236
7255
|
this.descriptions.delete(taskId);
|
|
7237
7256
|
}
|
|
7238
7257
|
}
|
|
7258
|
+
/**
|
|
7259
|
+
* Remove every task owned by `subagentId` from the descriptions/owners
|
|
7260
|
+
* indexes and return the removed task ids. Director.remove() calls this so
|
|
7261
|
+
* per-task state is reclaimed on the default FleetManager path too, where
|
|
7262
|
+
* the Director's manifestEntries map is never populated (fleet-spawn.ts:289
|
|
7263
|
+
* fills it only when !host.fleetManager) and the manifest-gated removeTasks
|
|
7264
|
+
* call would otherwise be skipped — leaking one description (a full task
|
|
7265
|
+
* brief, KB-scale) plus one owner entry per assigned task for the Director's
|
|
7266
|
+
* lifetime. The owners index is populated on every path (assign/retarget),
|
|
7267
|
+
* so this works with or without a FleetManager. Idempotent with removeTasks.
|
|
7268
|
+
*/
|
|
7269
|
+
removeTasksOwnedBy(subagentId) {
|
|
7270
|
+
const removed = [];
|
|
7271
|
+
for (const [taskId, owner] of this.owners) {
|
|
7272
|
+
if (owner === subagentId) removed.push(taskId);
|
|
7273
|
+
}
|
|
7274
|
+
this.removeTasks(removed);
|
|
7275
|
+
return removed;
|
|
7276
|
+
}
|
|
7239
7277
|
resolveWaitersOnShutdown() {
|
|
7240
7278
|
for (const entry of [...this.anyWaiters]) {
|
|
7241
7279
|
if (entry.timer) clearTimeout(entry.timer);
|
|
@@ -7261,11 +7299,11 @@ ${JSON.stringify(result.result, null, 2)}
|
|
|
7261
7299
|
this.makeStoppedResult(taskId, "director", `Unknown task id "${taskId}" \u2014 never assigned`)
|
|
7262
7300
|
);
|
|
7263
7301
|
}
|
|
7264
|
-
let
|
|
7302
|
+
let resolve17;
|
|
7265
7303
|
const promise = new Promise((done) => {
|
|
7266
|
-
|
|
7304
|
+
resolve17 = done;
|
|
7267
7305
|
});
|
|
7268
|
-
this.taskWaiters.set(taskId, { promise, resolve:
|
|
7306
|
+
this.taskWaiters.set(taskId, { promise, resolve: resolve17 });
|
|
7269
7307
|
return promise;
|
|
7270
7308
|
}
|
|
7271
7309
|
recordAssignment(task) {
|
|
@@ -7431,12 +7469,13 @@ function rosterSummaryFromConfigs(roster) {
|
|
|
7431
7469
|
// src/coordination/director-tools.ts
|
|
7432
7470
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
7433
7471
|
import {
|
|
7434
|
-
|
|
7435
|
-
|
|
7472
|
+
completeKanbanDispatch,
|
|
7473
|
+
failKanbanDispatch,
|
|
7436
7474
|
getBoard,
|
|
7437
7475
|
heartbeatTaskAssignment,
|
|
7438
7476
|
listReadyTasks,
|
|
7439
|
-
|
|
7477
|
+
reserveKanbanDispatch,
|
|
7478
|
+
startKanbanDispatch,
|
|
7440
7479
|
updateTaskAssignment
|
|
7441
7480
|
} from "@wrongstack/kanban";
|
|
7442
7481
|
|
|
@@ -8577,9 +8616,6 @@ function makeFleetTool(director) {
|
|
|
8577
8616
|
}
|
|
8578
8617
|
|
|
8579
8618
|
// src/coordination/director-tools.ts
|
|
8580
|
-
function nowIso() {
|
|
8581
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
8582
|
-
}
|
|
8583
8619
|
function makeSpawnTool(director, roster) {
|
|
8584
8620
|
const dispatchCatalog = () => {
|
|
8585
8621
|
if (!roster) return void 0;
|
|
@@ -8809,13 +8845,6 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8809
8845
|
i.heartbeatIntervalMs ?? Math.floor(leaseTtlMs / 2)
|
|
8810
8846
|
);
|
|
8811
8847
|
const effectiveLeaseTtlMs = Math.max(leaseTtlMs, heartbeatIntervalMs * 2);
|
|
8812
|
-
const claimedAt = nowIso();
|
|
8813
|
-
const leaseSeeding = {
|
|
8814
|
-
leaseId: randomUUID8(),
|
|
8815
|
-
claimedAt,
|
|
8816
|
-
heartbeatAt: claimedAt,
|
|
8817
|
-
leaseExpiresAt: new Date(Date.now() + effectiveLeaseTtlMs).toISOString()
|
|
8818
|
-
};
|
|
8819
8848
|
const candidateTaskIds = i.taskId !== void 0 ? [i.taskId] : i.query ? (await listReadyTasks(projectRoot, {
|
|
8820
8849
|
...i.boardId !== void 0 ? { boardId: i.boardId } : {}
|
|
8821
8850
|
})).filter((candidate) => matchesKanbanQueueQuery(candidate.task, i.query ?? "")).slice(0, maxTasks).map((candidate) => candidate.task.id) : void 0;
|
|
@@ -8827,24 +8856,28 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8827
8856
|
const candidateTaskId = candidateTaskIds?.[index];
|
|
8828
8857
|
if (candidateTaskIds && !candidateTaskId) break;
|
|
8829
8858
|
if (candidateTaskId && budgetRejectedTaskIds.has(candidateTaskId)) continue;
|
|
8830
|
-
const
|
|
8859
|
+
const reserved = await reserveKanbanDispatch(projectRoot, {
|
|
8831
8860
|
...i.boardId !== void 0 ? { boardId: i.boardId } : {},
|
|
8832
8861
|
...candidateTaskId !== void 0 ? { taskId: candidateTaskId } : {},
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8862
|
+
routing: {
|
|
8863
|
+
...i.agentId !== void 0 ? { agentId: i.agentId } : {},
|
|
8864
|
+
...i.name !== void 0 ? { name: i.name } : {},
|
|
8865
|
+
...i.role !== void 0 ? { role: i.role } : {},
|
|
8866
|
+
...i.provider !== void 0 ? { provider: i.provider } : {},
|
|
8867
|
+
...i.model !== void 0 ? { model: i.model } : {},
|
|
8868
|
+
...i.fallbackModels !== void 0 ? { fallbackModels: i.fallbackModels } : {},
|
|
8869
|
+
...i.tools !== void 0 ? { tools: i.tools } : {},
|
|
8870
|
+
...i.allowedCapabilities !== void 0 ? { allowedCapabilities: i.allowedCapabilities } : {}
|
|
8871
|
+
},
|
|
8872
|
+
leaseTtlMs: effectiveLeaseTtlMs,
|
|
8873
|
+
heartbeatIntervalMs
|
|
8843
8874
|
});
|
|
8844
|
-
if (!
|
|
8875
|
+
if (!reserved) {
|
|
8845
8876
|
if (candidateTaskIds) continue;
|
|
8846
8877
|
break;
|
|
8847
8878
|
}
|
|
8879
|
+
const ourLeaseId = reserved.lease.leaseId;
|
|
8880
|
+
const claim = reserved;
|
|
8848
8881
|
let subagentId;
|
|
8849
8882
|
let runTaskId;
|
|
8850
8883
|
const costCeiling = claim.task.assignment?.costCeilingUsd;
|
|
@@ -8862,7 +8895,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8862
8895
|
status: "failed",
|
|
8863
8896
|
error: budgetError
|
|
8864
8897
|
},
|
|
8865
|
-
{ expectedLeaseId:
|
|
8898
|
+
{ expectedLeaseId: ourLeaseId }
|
|
8866
8899
|
);
|
|
8867
8900
|
errors.push({
|
|
8868
8901
|
taskId: claim.task.id,
|
|
@@ -8888,8 +8921,8 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8888
8921
|
// 10s lease, letting the lease expire before its first renewal.
|
|
8889
8922
|
heartbeatIntervalMs,
|
|
8890
8923
|
leaseTtlMs: effectiveLeaseTtlMs,
|
|
8891
|
-
leaseId:
|
|
8892
|
-
leaseExpiresAt:
|
|
8924
|
+
leaseId: ourLeaseId,
|
|
8925
|
+
leaseExpiresAt: reserved.lease.leaseExpiresAt
|
|
8893
8926
|
}),
|
|
8894
8927
|
...i.maxToolCalls !== void 0 ? { maxToolCalls: i.maxToolCalls } : {},
|
|
8895
8928
|
...i.timeoutMs !== void 0 ? { timeoutMs: i.timeoutMs } : {},
|
|
@@ -8900,33 +8933,19 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8900
8933
|
taskId: claim.task.id,
|
|
8901
8934
|
origin: claim.task.origin,
|
|
8902
8935
|
projectRoot,
|
|
8903
|
-
leaseId:
|
|
8936
|
+
leaseId: ourLeaseId
|
|
8904
8937
|
}
|
|
8905
8938
|
}
|
|
8906
8939
|
};
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
claim.
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
// Renew the lease from the actual dispatch moment, not from claim
|
|
8917
|
-
// time. The claim-time lease starts before spawn + queueing, so
|
|
8918
|
-
// `timeoutMs + 60_000` can expire while the worker is still
|
|
8919
|
-
// queued or running, letting recover_stale reclaim and duplicate
|
|
8920
|
-
// the assignment (see auto-extend.ts: timeout extensions can push
|
|
8921
|
-
// execution well past the original timeoutMs). Both heartbeatAt
|
|
8922
|
-
// and leaseExpiresAt are stamped from now so the worker has a
|
|
8923
|
-
// full lease window from the moment it was actually dispatched.
|
|
8924
|
-
heartbeatAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8925
|
-
leaseExpiresAt: new Date(Date.now() + effectiveLeaseTtlMs).toISOString()
|
|
8926
|
-
},
|
|
8927
|
-
{ expectedLeaseId: leaseSeeding.leaseId }
|
|
8928
|
-
);
|
|
8929
|
-
if (dispatched === null) {
|
|
8940
|
+
const started = await startKanbanDispatch(projectRoot, {
|
|
8941
|
+
boardId: claim.board.id,
|
|
8942
|
+
taskId: claim.task.id,
|
|
8943
|
+
leaseId: ourLeaseId,
|
|
8944
|
+
actor: i.agentId ?? "director-agent",
|
|
8945
|
+
subagentId,
|
|
8946
|
+
runTaskId: dispatchTaskId
|
|
8947
|
+
});
|
|
8948
|
+
if (started === null) {
|
|
8930
8949
|
try {
|
|
8931
8950
|
await director.terminate(subagentId);
|
|
8932
8951
|
} catch (cleanupErr) {
|
|
@@ -8942,27 +8961,6 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8942
8961
|
});
|
|
8943
8962
|
continue;
|
|
8944
8963
|
}
|
|
8945
|
-
if (dispatched?.lifecycle?.mode === "managed") {
|
|
8946
|
-
const mTask = dispatched.tasks.find(
|
|
8947
|
-
(candidate) => candidate.id === claim.task.id
|
|
8948
|
-
);
|
|
8949
|
-
if (mTask?.lifecycle?.currentStage === "todo") {
|
|
8950
|
-
try {
|
|
8951
|
-
const tr = await transitionTask(
|
|
8952
|
-
projectRoot,
|
|
8953
|
-
claim.board.id,
|
|
8954
|
-
claim.task.id,
|
|
8955
|
-
{
|
|
8956
|
-
to: "running",
|
|
8957
|
-
actor: i.agentId ?? "director-agent",
|
|
8958
|
-
comment: "Dispatched by kanban_queue."
|
|
8959
|
-
}
|
|
8960
|
-
);
|
|
8961
|
-
if (tr) dispatched = tr.board;
|
|
8962
|
-
} catch {
|
|
8963
|
-
}
|
|
8964
|
-
}
|
|
8965
|
-
}
|
|
8966
8964
|
await director.assign(taskSpec);
|
|
8967
8965
|
runTaskId = dispatchTaskId;
|
|
8968
8966
|
dispatches.push({
|
|
@@ -8971,6 +8969,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8971
8969
|
title: claim.task.title,
|
|
8972
8970
|
subagentId,
|
|
8973
8971
|
runTaskId,
|
|
8972
|
+
leaseId: ourLeaseId,
|
|
8974
8973
|
...config.provider !== void 0 ? { provider: config.provider } : {},
|
|
8975
8974
|
...config.model !== void 0 ? { model: config.model } : {}
|
|
8976
8975
|
});
|
|
@@ -8983,24 +8982,17 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
8983
8982
|
message = `${message}; cleanup failed for spawned subagent ${subagentId}: ${toErrorMessage(cleanupErr)}`;
|
|
8984
8983
|
}
|
|
8985
8984
|
}
|
|
8986
|
-
await
|
|
8987
|
-
|
|
8988
|
-
claim.
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
...subagentId !== void 0 ? { subagentId } : {},
|
|
8994
|
-
...runTaskId !== void 0 ? { runTaskId } : {},
|
|
8995
|
-
error: message
|
|
8996
|
-
},
|
|
8997
|
-
{ expectedLeaseId: leaseSeeding.leaseId }
|
|
8998
|
-
);
|
|
8985
|
+
await failKanbanDispatch(projectRoot, {
|
|
8986
|
+
boardId: claim.board.id,
|
|
8987
|
+
taskId: claim.task.id,
|
|
8988
|
+
leaseId: ourLeaseId,
|
|
8989
|
+
actor: "kanban-queue",
|
|
8990
|
+
error: message
|
|
8991
|
+
});
|
|
8999
8992
|
errors.push({ taskId: claim.task.id, error: message });
|
|
9000
8993
|
}
|
|
9001
8994
|
}
|
|
9002
8995
|
if (!i.awaitCompletion && dispatches.length > 0 && typeof director.fleet?.subscribe === "function") {
|
|
9003
|
-
const ourLeaseId = leaseSeeding.leaseId;
|
|
9004
8996
|
const disposers = /* @__PURE__ */ new Map();
|
|
9005
8997
|
const renewalStartedAt = Date.now();
|
|
9006
8998
|
const maxRenewalWindowMs = 24 * 60 * 6e4;
|
|
@@ -9023,7 +9015,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
9023
9015
|
taskId: dispatch.taskId,
|
|
9024
9016
|
subagentId: dispatch.subagentId,
|
|
9025
9017
|
runTaskId: dispatch.runTaskId,
|
|
9026
|
-
ourLeaseId,
|
|
9018
|
+
ourLeaseId: dispatch.leaseId,
|
|
9027
9019
|
refreshedExpiry,
|
|
9028
9020
|
director,
|
|
9029
9021
|
disposers
|
|
@@ -9044,7 +9036,6 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
9044
9036
|
}
|
|
9045
9037
|
let results;
|
|
9046
9038
|
if (i.awaitCompletion && dispatches.length > 0) {
|
|
9047
|
-
const ourLeaseId = leaseSeeding.leaseId;
|
|
9048
9039
|
const runTaskIds = new Set(dispatches.map((dispatch) => dispatch.runTaskId));
|
|
9049
9040
|
let heartbeatRunning = false;
|
|
9050
9041
|
let heartbeatInFlight = Promise.resolve();
|
|
@@ -9056,7 +9047,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
9056
9047
|
const board = await getBoard(projectRoot, dispatch.boardId);
|
|
9057
9048
|
const liveTask = board?.tasks.find((t) => t.id === dispatch.taskId);
|
|
9058
9049
|
const liveLeaseId = liveTask?.assignment?.leaseId;
|
|
9059
|
-
if (liveLeaseId !== void 0 && liveLeaseId !==
|
|
9050
|
+
if (liveLeaseId !== void 0 && liveLeaseId !== dispatch.leaseId) {
|
|
9060
9051
|
await director.terminate(dispatch.subagentId).catch(() => {
|
|
9061
9052
|
});
|
|
9062
9053
|
runTaskIds.delete(dispatch.runTaskId);
|
|
@@ -9067,7 +9058,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
9067
9058
|
try {
|
|
9068
9059
|
await heartbeatTaskAssignment(projectRoot, dispatch.boardId, dispatch.taskId, {
|
|
9069
9060
|
leaseExpiresAt: refreshedExpiry,
|
|
9070
|
-
expectedLeaseId:
|
|
9061
|
+
expectedLeaseId: dispatch.leaseId
|
|
9071
9062
|
});
|
|
9072
9063
|
} catch {
|
|
9073
9064
|
}
|
|
@@ -9090,29 +9081,36 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
9090
9081
|
const dispatch = dispatches.find((candidate) => candidate.runTaskId === result.taskId);
|
|
9091
9082
|
if (!dispatch) continue;
|
|
9092
9083
|
runTaskIds.delete(dispatch.runTaskId);
|
|
9093
|
-
|
|
9094
|
-
projectRoot,
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
await finalizeTaskCompletion(projectRoot, dispatch.boardId, dispatch.taskId, {
|
|
9108
|
-
eventContext: { actor: "kanban-queue" }
|
|
9084
|
+
if (result.status === "success") {
|
|
9085
|
+
const completed = await completeKanbanDispatch(projectRoot, {
|
|
9086
|
+
boardId: dispatch.boardId,
|
|
9087
|
+
taskId: dispatch.taskId,
|
|
9088
|
+
leaseId: dispatch.leaseId,
|
|
9089
|
+
actor: "kanban-queue",
|
|
9090
|
+
result: resultToText(result)
|
|
9091
|
+
});
|
|
9092
|
+
if (!completed) {
|
|
9093
|
+
resultFailures.push({
|
|
9094
|
+
taskId: dispatch.taskId,
|
|
9095
|
+
runTaskId: result.taskId,
|
|
9096
|
+
status: "failed",
|
|
9097
|
+
error: "Lease lost during completion write (reassigned by recovery)."
|
|
9109
9098
|
});
|
|
9110
|
-
}
|
|
9099
|
+
}
|
|
9100
|
+
} else {
|
|
9101
|
+
const failed = await failKanbanDispatch(projectRoot, {
|
|
9102
|
+
boardId: dispatch.boardId,
|
|
9103
|
+
taskId: dispatch.taskId,
|
|
9104
|
+
leaseId: dispatch.leaseId,
|
|
9105
|
+
actor: "kanban-queue",
|
|
9106
|
+
error: resultErrorText(result)
|
|
9107
|
+
});
|
|
9108
|
+
if (!failed) {
|
|
9111
9109
|
resultFailures.push({
|
|
9112
9110
|
taskId: dispatch.taskId,
|
|
9113
9111
|
runTaskId: result.taskId,
|
|
9114
9112
|
status: "failed",
|
|
9115
|
-
error:
|
|
9113
|
+
error: "Lease lost during failure write (reassigned by recovery)."
|
|
9116
9114
|
});
|
|
9117
9115
|
}
|
|
9118
9116
|
}
|
|
@@ -11475,12 +11473,12 @@ async function executeSubagentWithTimeout({
|
|
|
11475
11473
|
}
|
|
11476
11474
|
return new Promise((resolveDecision) => {
|
|
11477
11475
|
let settled = false;
|
|
11478
|
-
const
|
|
11476
|
+
const resolve17 = (d) => {
|
|
11479
11477
|
if (settled) return;
|
|
11480
11478
|
settled = true;
|
|
11481
11479
|
resolveDecision(d);
|
|
11482
11480
|
};
|
|
11483
|
-
const fallback = setTimeout(() =>
|
|
11481
|
+
const fallback = setTimeout(() => resolve17("stop"), DECISION_TIMEOUT_MS);
|
|
11484
11482
|
const sessionId = currentSessionId();
|
|
11485
11483
|
budget._events?.emit("budget.threshold_reached", {
|
|
11486
11484
|
...sessionId ? { sessionId } : {},
|
|
@@ -11490,11 +11488,11 @@ async function executeSubagentWithTimeout({
|
|
|
11490
11488
|
timeoutMs: DECISION_TIMEOUT_MS,
|
|
11491
11489
|
extend: (extra) => {
|
|
11492
11490
|
clearTimeout(fallback);
|
|
11493
|
-
queueMicrotask(() =>
|
|
11491
|
+
queueMicrotask(() => resolve17({ extend: extra }));
|
|
11494
11492
|
},
|
|
11495
11493
|
deny: () => {
|
|
11496
11494
|
clearTimeout(fallback);
|
|
11497
|
-
|
|
11495
|
+
resolve17("stop");
|
|
11498
11496
|
}
|
|
11499
11497
|
});
|
|
11500
11498
|
});
|
|
@@ -11881,7 +11879,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
11881
11879
|
taskIds.map((id) => {
|
|
11882
11880
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
11883
11881
|
if (cached) return cached;
|
|
11884
|
-
return new Promise((
|
|
11882
|
+
return new Promise((resolve17, reject) => {
|
|
11885
11883
|
const timeout = setTimeout(() => {
|
|
11886
11884
|
this.off("task.completed", handler);
|
|
11887
11885
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -11890,7 +11888,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
11890
11888
|
if (result.taskId === id) {
|
|
11891
11889
|
clearTimeout(timeout);
|
|
11892
11890
|
this.off("task.completed", handler);
|
|
11893
|
-
|
|
11891
|
+
resolve17(result);
|
|
11894
11892
|
}
|
|
11895
11893
|
};
|
|
11896
11894
|
this.on("task.completed", handler);
|
|
@@ -11915,13 +11913,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
11915
11913
|
const done = new Set(completed.map((r) => r.taskId));
|
|
11916
11914
|
return { completed, pending: taskIds.filter((id) => !done.has(id)) };
|
|
11917
11915
|
}
|
|
11918
|
-
return new Promise((
|
|
11916
|
+
return new Promise((resolve17) => {
|
|
11919
11917
|
let timer;
|
|
11920
11918
|
const handler = ({ result }) => {
|
|
11921
11919
|
if (!ids.has(result.taskId)) return;
|
|
11922
11920
|
if (timer) clearTimeout(timer);
|
|
11923
11921
|
this.off("task.completed", handler);
|
|
11924
|
-
|
|
11922
|
+
resolve17({
|
|
11925
11923
|
completed: [result],
|
|
11926
11924
|
pending: taskIds.filter((id) => id !== result.taskId)
|
|
11927
11925
|
});
|
|
@@ -11929,7 +11927,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
11929
11927
|
if (opts?.timeoutMs !== void 0) {
|
|
11930
11928
|
timer = setTimeout(() => {
|
|
11931
11929
|
this.off("task.completed", handler);
|
|
11932
|
-
|
|
11930
|
+
resolve17({ completed: [], pending: [...taskIds], timedOut: true });
|
|
11933
11931
|
}, opts.timeoutMs);
|
|
11934
11932
|
}
|
|
11935
11933
|
this.on("task.completed", handler);
|
|
@@ -12497,67 +12495,118 @@ import * as path21 from "node:path";
|
|
|
12497
12495
|
import * as fsp17 from "node:fs/promises";
|
|
12498
12496
|
import * as path20 from "node:path";
|
|
12499
12497
|
|
|
12498
|
+
// src/security/file-permissions.ts
|
|
12499
|
+
import { chmod } from "node:fs/promises";
|
|
12500
|
+
var SECRET_FILE_MODE = 384;
|
|
12501
|
+
async function restrictFilePermissions(filePath, opts) {
|
|
12502
|
+
const label = opts?.label ?? "file-permissions";
|
|
12503
|
+
const warn = opts?.warn ?? ((msg) => console.warn(msg));
|
|
12504
|
+
if (process.platform === "win32") {
|
|
12505
|
+
try {
|
|
12506
|
+
const { execFile: execFile2 } = await import("node:child_process");
|
|
12507
|
+
const { promisify: promisify2 } = await import("node:util");
|
|
12508
|
+
const execFileAsync = promisify2(execFile2);
|
|
12509
|
+
const user = windowsAccountName();
|
|
12510
|
+
if (!user) {
|
|
12511
|
+
warn(
|
|
12512
|
+
`[${label}] Could not determine the current Windows user for ${filePath}; skipping icacls hardening.`
|
|
12513
|
+
);
|
|
12514
|
+
return;
|
|
12515
|
+
}
|
|
12516
|
+
await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${user}:(F)`], {
|
|
12517
|
+
windowsHide: true
|
|
12518
|
+
});
|
|
12519
|
+
} catch {
|
|
12520
|
+
warn(
|
|
12521
|
+
`[${label}] Could not restrict permissions on ${filePath} \u2014 it may be readable by other users on this system.`
|
|
12522
|
+
);
|
|
12523
|
+
}
|
|
12524
|
+
} else {
|
|
12525
|
+
try {
|
|
12526
|
+
await chmod(filePath, SECRET_FILE_MODE);
|
|
12527
|
+
} catch {
|
|
12528
|
+
}
|
|
12529
|
+
}
|
|
12530
|
+
}
|
|
12531
|
+
function windowsAccountName() {
|
|
12532
|
+
const username = process.env.USERNAME || process.env.USER;
|
|
12533
|
+
if (!username || username.includes("\0")) return void 0;
|
|
12534
|
+
const domain = process.env.USERDOMAIN;
|
|
12535
|
+
if (domain && !domain.includes("\0")) return `${domain}\\${username}`;
|
|
12536
|
+
return username;
|
|
12537
|
+
}
|
|
12538
|
+
|
|
12500
12539
|
// src/security/secret-scrubber.ts
|
|
12501
12540
|
var PATTERNS = [
|
|
12502
12541
|
// Anchored at the start where possible so partial matches inside larger
|
|
12503
12542
|
// strings don't trigger false positives.
|
|
12504
12543
|
{
|
|
12505
12544
|
type: "anthropic_key",
|
|
12506
|
-
regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g
|
|
12545
|
+
regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g,
|
|
12546
|
+
anchor: "sk-ant-"
|
|
12507
12547
|
},
|
|
12508
|
-
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g },
|
|
12509
|
-
{ type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g },
|
|
12510
|
-
{ type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g },
|
|
12511
|
-
{ type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g },
|
|
12512
|
-
{ type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g },
|
|
12513
|
-
{ type: "slack_token", regex: /(?<![A-Za-z0-9-])xox[abpos]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g },
|
|
12548
|
+
{ type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g, anchor: "sk-" },
|
|
12549
|
+
{ type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g, anchor: "ghp_" },
|
|
12550
|
+
{ type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g, anchor: "github_pat_" },
|
|
12551
|
+
{ type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g, anchor: "AKIA" },
|
|
12552
|
+
{ type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g, anchor: "AIza" },
|
|
12553
|
+
{ type: "slack_token", regex: /(?<![A-Za-z0-9-])xox[abpos]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g, anchor: "xox" },
|
|
12514
12554
|
{
|
|
12515
12555
|
type: "stripe_key",
|
|
12516
|
-
regex: /(?<![A-Za-z0-9])sk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g
|
|
12556
|
+
regex: /(?<![A-Za-z0-9])sk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g,
|
|
12557
|
+
anchor: "sk_"
|
|
12517
12558
|
},
|
|
12518
12559
|
{
|
|
12519
12560
|
type: "twilio_sid",
|
|
12520
|
-
regex: /(?<![A-Za-z0-9])AC[a-f0-9]{32}(?![A-Za-z0-9])/g
|
|
12561
|
+
regex: /(?<![A-Za-z0-9])AC[a-f0-9]{32}(?![A-Za-z0-9])/g,
|
|
12562
|
+
anchor: "AC"
|
|
12521
12563
|
},
|
|
12522
12564
|
{
|
|
12523
12565
|
type: "telegram_bot_token",
|
|
12524
12566
|
// Telegram tokens are of the form bot<digits>:<alphanum> in URL paths
|
|
12525
|
-
regex: /\/bot\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
12567
|
+
regex: /\/bot\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
12568
|
+
anchor: "/bot"
|
|
12526
12569
|
},
|
|
12527
12570
|
{
|
|
12528
12571
|
type: "jwt",
|
|
12529
12572
|
// Anchored: look for literal "eyJ" which is unambiguous for JWT header
|
|
12530
|
-
regex: /(?<![A-Za-z0-9/+=])eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?![A-Za-z0-9/+=])/g
|
|
12573
|
+
regex: /(?<![A-Za-z0-9/+=])eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?![A-Za-z0-9/+=])/g,
|
|
12574
|
+
anchor: "eyJ"
|
|
12531
12575
|
},
|
|
12532
12576
|
{
|
|
12533
12577
|
type: "private_key",
|
|
12534
12578
|
// Anchored: start must be BEGIN, end must be END with no extra dashes after END
|
|
12535
|
-
regex: /(?:^|\n)-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----[\s\S]*?-----END[^-]*-----(?:\n|$)/g
|
|
12579
|
+
regex: /(?:^|\n)-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----[\s\S]*?-----END[^-]*-----(?:\n|$)/g,
|
|
12580
|
+
anchor: "-----BEGIN"
|
|
12536
12581
|
},
|
|
12537
|
-
{ type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s"'`]+/g },
|
|
12538
|
-
{ type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g },
|
|
12539
|
-
{ type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g },
|
|
12540
|
-
{ type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g },
|
|
12582
|
+
{ type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s"'`]+/g, anchor: "mongodb" },
|
|
12583
|
+
{ type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g, anchor: "postgres" },
|
|
12584
|
+
{ type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g, anchor: "mysql://" },
|
|
12585
|
+
{ type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g, anchor: "redis://" },
|
|
12541
12586
|
// AI/ML provider keys — modern LLM services with well-known prefixes
|
|
12542
12587
|
{
|
|
12543
12588
|
type: "huggingface_token",
|
|
12544
12589
|
// HuggingFace tokens: hf_ followed by 34 alphanumeric chars
|
|
12545
|
-
regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g
|
|
12590
|
+
regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g,
|
|
12591
|
+
anchor: "hf_"
|
|
12546
12592
|
},
|
|
12547
12593
|
{
|
|
12548
12594
|
type: "replicate_token",
|
|
12549
12595
|
// Replicate tokens: r8_ followed by 40+ alphanumeric chars
|
|
12550
|
-
regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
|
|
12596
|
+
regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
12597
|
+
anchor: "r8_"
|
|
12551
12598
|
},
|
|
12552
12599
|
{
|
|
12553
12600
|
type: "perplexity_key",
|
|
12554
12601
|
// Perplexity API keys: pplx- followed by 40+ alphanumeric chars
|
|
12555
|
-
regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
|
|
12602
|
+
regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
12603
|
+
anchor: "pplx-"
|
|
12556
12604
|
},
|
|
12557
12605
|
{
|
|
12558
12606
|
type: "groq_key",
|
|
12559
12607
|
// Groq API keys: gsk_ followed by 40+ alphanumeric chars
|
|
12560
|
-
regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
|
|
12608
|
+
regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
12609
|
+
anchor: "gsk_"
|
|
12561
12610
|
},
|
|
12562
12611
|
{
|
|
12563
12612
|
type: "bearer_token",
|
|
@@ -12570,7 +12619,8 @@ var PATTERNS = [
|
|
|
12570
12619
|
// tokens sharing a single delimiter (`Bearer a… Bearer b…`) must both be
|
|
12571
12620
|
// redacted. A consuming trailing delimiter would eat the separator the
|
|
12572
12621
|
// next match needs for its leading anchor, leaking the second token.
|
|
12573
|
-
regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g
|
|
12622
|
+
regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g,
|
|
12623
|
+
anchor: "Bearer"
|
|
12574
12624
|
},
|
|
12575
12625
|
{
|
|
12576
12626
|
type: "high_entropy_env",
|
|
@@ -12586,7 +12636,89 @@ var PATTERNS = [
|
|
|
12586
12636
|
// replacement so the separator between adjacent secrets is preserved
|
|
12587
12637
|
// rather than collapsed. Capture groups are therefore: 1=leading
|
|
12588
12638
|
// delimiter, 2=key name, 3=value.
|
|
12589
|
-
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g
|
|
12639
|
+
regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
|
|
12640
|
+
anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD"]
|
|
12641
|
+
},
|
|
12642
|
+
// ── Ported from packages/plugins credential-patterns.ts (WS-034) ─────────
|
|
12643
|
+
// The plugin runtime carried 37 patterns while this scrubber — the one that
|
|
12644
|
+
// guards session JSONL, chronicle, HQ broadcast, WebUI events and the auth
|
|
12645
|
+
// audit — carried 22. The plugin side already had a parity test; it just did
|
|
12646
|
+
// not cover core. Most consequential: WrongStack mints `gho_` tokens itself
|
|
12647
|
+
// in the Copilot OAuth flow, and `gh[ousr]_` was absent here.
|
|
12648
|
+
{
|
|
12649
|
+
type: "github_oauth_token",
|
|
12650
|
+
regex: /(?<![A-Za-z0-9])gh[ousr]_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g,
|
|
12651
|
+
anchor: ["gho_", "ghu_", "ghs_", "ghr_"]
|
|
12652
|
+
},
|
|
12653
|
+
{
|
|
12654
|
+
type: "gitlab_pat",
|
|
12655
|
+
regex: /(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
12656
|
+
anchor: "glpat-"
|
|
12657
|
+
},
|
|
12658
|
+
{
|
|
12659
|
+
type: "gitlab_runner_token",
|
|
12660
|
+
regex: /(?<![A-Za-z0-9])glrt-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
12661
|
+
anchor: "glrt-"
|
|
12662
|
+
},
|
|
12663
|
+
{
|
|
12664
|
+
type: "npm_token",
|
|
12665
|
+
regex: /(?<![A-Za-z0-9])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9])/g,
|
|
12666
|
+
anchor: "npm_"
|
|
12667
|
+
},
|
|
12668
|
+
{
|
|
12669
|
+
type: "slack_app_token",
|
|
12670
|
+
regex: /(?<![A-Za-z0-9-])xapp-\d-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
|
|
12671
|
+
anchor: "xapp-"
|
|
12672
|
+
},
|
|
12673
|
+
{
|
|
12674
|
+
type: "slack_webhook",
|
|
12675
|
+
regex: /https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_-]+\/B[A-Za-z0-9_-]+\/[A-Za-z0-9]{16,}/g,
|
|
12676
|
+
anchor: "hooks.slack.com"
|
|
12677
|
+
},
|
|
12678
|
+
{
|
|
12679
|
+
type: "sendgrid_key",
|
|
12680
|
+
regex: /(?<![A-Za-z0-9])SG\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
12681
|
+
anchor: "SG."
|
|
12682
|
+
},
|
|
12683
|
+
{
|
|
12684
|
+
type: "digitalocean_token",
|
|
12685
|
+
regex: /(?<![A-Za-z0-9])dop_v1_[a-f0-9]{64}(?![A-Za-z0-9])/g,
|
|
12686
|
+
anchor: "dop_v1_"
|
|
12687
|
+
},
|
|
12688
|
+
{
|
|
12689
|
+
type: "doppler_token",
|
|
12690
|
+
regex: /(?<![A-Za-z0-9])dp\.(?:pt|st|sa|scim|audit)\.[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
12691
|
+
anchor: "dp."
|
|
12692
|
+
},
|
|
12693
|
+
{
|
|
12694
|
+
type: "shopify_token",
|
|
12695
|
+
regex: /(?<![A-Za-z0-9])shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}(?![A-Za-z0-9])/g,
|
|
12696
|
+
anchor: "shp"
|
|
12697
|
+
},
|
|
12698
|
+
{
|
|
12699
|
+
type: "docker_pat",
|
|
12700
|
+
regex: /(?<![A-Za-z0-9])dckr_pat_[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
12701
|
+
anchor: "dckr_pat_"
|
|
12702
|
+
},
|
|
12703
|
+
{
|
|
12704
|
+
type: "linear_key",
|
|
12705
|
+
regex: /(?<![A-Za-z0-9])lin_api_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
|
|
12706
|
+
anchor: "lin_api_"
|
|
12707
|
+
},
|
|
12708
|
+
{
|
|
12709
|
+
type: "atlassian_token",
|
|
12710
|
+
regex: /(?<![A-Za-z0-9])ATATT3[A-Za-z0-9_\-=]{40,}(?![A-Za-z0-9_\-=])/g,
|
|
12711
|
+
anchor: "ATATT3"
|
|
12712
|
+
},
|
|
12713
|
+
{
|
|
12714
|
+
type: "square_token",
|
|
12715
|
+
regex: /(?<![A-Za-z0-9])(?:sq0(?:atp|csp)-|EAAA)[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
12716
|
+
anchor: ["sq0atp-", "sq0csp-", "EAAA"]
|
|
12717
|
+
},
|
|
12718
|
+
{
|
|
12719
|
+
type: "google_oauth_client_secret",
|
|
12720
|
+
regex: /(?<![A-Za-z0-9_-])GOCSPX-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
|
|
12721
|
+
anchor: "GOCSPX-"
|
|
12590
12722
|
}
|
|
12591
12723
|
];
|
|
12592
12724
|
var SIMPLE_PATTERNS = PATTERNS.filter((p) => p.type !== "high_entropy_env");
|
|
@@ -12595,32 +12727,32 @@ var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").reg
|
|
|
12595
12727
|
var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
|
|
12596
12728
|
var SCRUB_CHUNK_BYTES = 64 * 1024;
|
|
12597
12729
|
var SCRUB_OVERLAP_BYTES = 1024;
|
|
12730
|
+
var PATTERN_ANCHORS = [
|
|
12731
|
+
...new Set(
|
|
12732
|
+
PATTERNS.flatMap(
|
|
12733
|
+
(pattern) => typeof pattern.anchor === "string" ? [pattern.anchor] : [...pattern.anchor]
|
|
12734
|
+
)
|
|
12735
|
+
)
|
|
12736
|
+
];
|
|
12737
|
+
var JSON_KEY_ANCHORS = [
|
|
12738
|
+
'"apiKey"',
|
|
12739
|
+
'"api_key"',
|
|
12740
|
+
'"token"',
|
|
12741
|
+
'"secret"',
|
|
12742
|
+
'"password"',
|
|
12743
|
+
'"authorization"',
|
|
12744
|
+
'"bearer"',
|
|
12745
|
+
'"private_key"',
|
|
12746
|
+
'"access_token"',
|
|
12747
|
+
'"refresh_token"',
|
|
12748
|
+
'"client_secret"'
|
|
12749
|
+
];
|
|
12750
|
+
var ALL_ANCHORS = [...PATTERN_ANCHORS, ...JSON_KEY_ANCHORS];
|
|
12598
12751
|
function hasCredentialAnchors(text) {
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
text.includes("github_pat_") || // GitHub PAT v2
|
|
12604
|
-
text.includes("eyJ") || // JWT
|
|
12605
|
-
text.includes("AKIA") || // AWS access key
|
|
12606
|
-
text.includes("AIza") || // GCP service key
|
|
12607
|
-
text.includes("xox") || // Slack token (xoxa/xoxb/xoxp/xoxo/xoxs)
|
|
12608
|
-
text.includes("Bearer ") || // Bearer token (space suffix reduces false positives)
|
|
12609
|
-
text.includes("/bot") || // Telegram bot token (URL path pattern)
|
|
12610
|
-
text.includes("hf_") || // HuggingFace token
|
|
12611
|
-
text.includes("r8_") || // Replicate token
|
|
12612
|
-
text.includes("pplx-") || // Perplexity API key
|
|
12613
|
-
text.includes("gsk_") || // Groq API key
|
|
12614
|
-
text.includes("_KEY=") || // High-entropy env vars: API_KEY=, SECRET_KEY=, ...
|
|
12615
|
-
text.includes("_TOKEN=") || // ACCESS_TOKEN=, AUTH_TOKEN=, ...
|
|
12616
|
-
text.includes("_SECRET=") || // API_SECRET=, CLIENT_SECRET=, ...
|
|
12617
|
-
text.includes("_PASSWORD=") || // DB_PASSWORD=, ROOT_PASSWORD=, ...
|
|
12618
|
-
text.includes("mongodb://") || text.includes("mongodb+srv://") || text.includes("postgres://") || text.includes("postgresql://") || text.includes("mysql://") || text.includes("redis://") || // JSON-style credential keys: tool outputs often serialise objects as
|
|
12619
|
-
// raw JSON strings rather than parsed objects. The value may not start
|
|
12620
|
-
// with a recognisable prefix (sk-, ghp_, etc.), so we anchor on common
|
|
12621
|
-
// key names. The `"` prefix avoids matching prose that happens to mention
|
|
12622
|
-
// these words — JSON serialisation always quotes string keys.
|
|
12623
|
-
text.includes('"apiKey"') || text.includes('"api_key"') || text.includes('"token"') || text.includes('"secret"') || text.includes('"password"') || text.includes('"authorization"') || text.includes('"bearer"') || text.includes('"private_key"') || text.includes('"access_token"') || text.includes('"refresh_token"') || text.includes('"client_secret"');
|
|
12752
|
+
for (const anchor of ALL_ANCHORS) {
|
|
12753
|
+
if (text.includes(anchor)) return true;
|
|
12754
|
+
}
|
|
12755
|
+
return false;
|
|
12624
12756
|
}
|
|
12625
12757
|
var DefaultSecretScrubber = class {
|
|
12626
12758
|
scrub(text) {
|
|
@@ -12736,6 +12868,11 @@ var SECRET_NAME_PARTS = [
|
|
|
12736
12868
|
"SECRET",
|
|
12737
12869
|
"PASSWORD",
|
|
12738
12870
|
"PASSWD",
|
|
12871
|
+
// WRONGSTACK_VAULT_PASSPHRASE is the vault KEK. Without this entry it matched
|
|
12872
|
+
// no rule here and fell through to the `WRONGSTACK_` prefix allow-rule, so the
|
|
12873
|
+
// key that protects the credential vault was forwarded to every child process
|
|
12874
|
+
// — including the agent's own exec/bash tools and MCP stdio servers (WS-033).
|
|
12875
|
+
"PASSPHRASE",
|
|
12739
12876
|
"AUTH",
|
|
12740
12877
|
"CRED",
|
|
12741
12878
|
"BEARER",
|
|
@@ -12922,6 +13059,65 @@ async function backupConfigFile(filePath, paths) {
|
|
|
12922
13059
|
}
|
|
12923
13060
|
}
|
|
12924
13061
|
|
|
13062
|
+
// src/utils/deep-merge.ts
|
|
13063
|
+
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
13064
|
+
"__proto__",
|
|
13065
|
+
"constructor",
|
|
13066
|
+
"prototype",
|
|
13067
|
+
"__defineGetter__",
|
|
13068
|
+
"__defineSetter__",
|
|
13069
|
+
"__lookupGetter__",
|
|
13070
|
+
"__lookupSetter__"
|
|
13071
|
+
]);
|
|
13072
|
+
function isPrimitiveArray(a) {
|
|
13073
|
+
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
13074
|
+
}
|
|
13075
|
+
function deepMerge(base, patch, options = {}) {
|
|
13076
|
+
const {
|
|
13077
|
+
conflictResolution = "prefer-patch",
|
|
13078
|
+
arrayMode = "replace",
|
|
13079
|
+
protectProto = true,
|
|
13080
|
+
onNonPrimitiveArrayReplace
|
|
13081
|
+
} = options;
|
|
13082
|
+
if (typeof base !== "object" || base === null) {
|
|
13083
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13084
|
+
}
|
|
13085
|
+
if (typeof patch !== "object" || patch === null) {
|
|
13086
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13087
|
+
}
|
|
13088
|
+
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
13089
|
+
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
13090
|
+
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
13091
|
+
}
|
|
13092
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13093
|
+
}
|
|
13094
|
+
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
13095
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13096
|
+
}
|
|
13097
|
+
const baseObj = base;
|
|
13098
|
+
const patchObj = patch;
|
|
13099
|
+
const out = { ...baseObj };
|
|
13100
|
+
for (const [k, v] of Object.entries(patchObj)) {
|
|
13101
|
+
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
13102
|
+
const existing = out[k];
|
|
13103
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
13104
|
+
out[k] = deepMerge(existing, v, options);
|
|
13105
|
+
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
13106
|
+
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
13107
|
+
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
13108
|
+
}
|
|
13109
|
+
out[k] = deepMerge(existing, v, options);
|
|
13110
|
+
} else if (v !== void 0) {
|
|
13111
|
+
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
13112
|
+
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
13113
|
+
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
13114
|
+
}
|
|
13115
|
+
out[k] = v;
|
|
13116
|
+
}
|
|
13117
|
+
}
|
|
13118
|
+
return out;
|
|
13119
|
+
}
|
|
13120
|
+
|
|
12925
13121
|
// src/utils/message-invariants.ts
|
|
12926
13122
|
function repairToolUseAdjacency(messages) {
|
|
12927
13123
|
const removedToolUses = [];
|
|
@@ -13072,6 +13268,14 @@ var SKILL_LIMITS = {
|
|
|
13072
13268
|
* Consumer: `downloadGitHubTarball`.
|
|
13073
13269
|
*/
|
|
13074
13270
|
MAX_TARBALL_SIZE: 50 * 1024 * 1024,
|
|
13271
|
+
/**
|
|
13272
|
+
* Maximum size of a GitHub skill tarball after gzip decompression. The
|
|
13273
|
+
* compressed download limit alone does not protect against highly
|
|
13274
|
+
* compressible archives expanding until the process runs out of memory.
|
|
13275
|
+
*
|
|
13276
|
+
* Consumer: `downloadGitHubTarball`.
|
|
13277
|
+
*/
|
|
13278
|
+
MAX_UNCOMPRESSED_TARBALL_SIZE: 64 * 1024 * 1024,
|
|
13075
13279
|
/**
|
|
13076
13280
|
* Default total char budget for skill bodies injected in eager mode when
|
|
13077
13281
|
* `config.skills.eagerMaxChars` is unset. ~6k tokens. Skills are injected
|
|
@@ -13083,6 +13287,17 @@ var SKILL_LIMITS = {
|
|
|
13083
13287
|
* `skillEagerMaxChars`).
|
|
13084
13288
|
*/
|
|
13085
13289
|
EAGER_DEFAULT_MAX_CHARS: 24e3,
|
|
13290
|
+
/**
|
|
13291
|
+
* Default total char budget for compact (token-saving) skill bodies injected
|
|
13292
|
+
* by `buildCompactSkillBodiesText`. Mirrors the eager ceiling so compact mode
|
|
13293
|
+
* can never inject more total skill text than eager mode. Before this bound
|
|
13294
|
+
* existed, compact mode injected EVERY skill's compact body with no cap, so
|
|
13295
|
+
* the section grew linearly with the number of installed skills. Skills past
|
|
13296
|
+
* the budget overflow to a name list the agent loads via the `skill` tool.
|
|
13297
|
+
*
|
|
13298
|
+
* Consumer: `buildCompactSkillBodiesText` (default for its `budget` param).
|
|
13299
|
+
*/
|
|
13300
|
+
COMPACT_DEFAULT_MAX_CHARS: 24e3,
|
|
13086
13301
|
/**
|
|
13087
13302
|
* Auto-compact body limits (the token-saving fallback used when a skill has
|
|
13088
13303
|
* no hand-crafted `SKILL.save.md`). The Overview and Rules sections are
|
|
@@ -13111,74 +13326,104 @@ var SKILL_LIMITS = {
|
|
|
13111
13326
|
SKILL_BODY_LINE_LIMIT: 500
|
|
13112
13327
|
};
|
|
13113
13328
|
|
|
13114
|
-
// src/
|
|
13115
|
-
var
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
"
|
|
13119
|
-
"
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
"__lookupSetter__"
|
|
13123
|
-
]);
|
|
13124
|
-
function isPrimitiveArray(a) {
|
|
13125
|
-
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
13329
|
+
// src/skills/frontmatter.ts
|
|
13330
|
+
var SCALAR_KEYS = /* @__PURE__ */ new Set(["name", "description", "trigger", "version", "license", "compatibility"]);
|
|
13331
|
+
function parseSkillFrontmatter(raw) {
|
|
13332
|
+
const text = normalizeLineEndings(raw);
|
|
13333
|
+
if (!text.startsWith("---")) return {};
|
|
13334
|
+
const end = text.indexOf("\n---", 4);
|
|
13335
|
+
if (end === -1) return {};
|
|
13336
|
+
return parseFrontmatterBlock(text.slice(4, end));
|
|
13126
13337
|
}
|
|
13127
|
-
function
|
|
13128
|
-
const
|
|
13129
|
-
|
|
13130
|
-
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
|
|
13135
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13136
|
-
}
|
|
13137
|
-
if (typeof patch !== "object" || patch === null) {
|
|
13138
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13139
|
-
}
|
|
13140
|
-
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
13141
|
-
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
13142
|
-
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
13143
|
-
}
|
|
13144
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13145
|
-
}
|
|
13146
|
-
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
13147
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
13148
|
-
}
|
|
13149
|
-
const baseObj = base;
|
|
13150
|
-
const patchObj = patch;
|
|
13151
|
-
const out = { ...baseObj };
|
|
13152
|
-
for (const [k, v] of Object.entries(patchObj)) {
|
|
13153
|
-
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
13154
|
-
const existing = out[k];
|
|
13155
|
-
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
13156
|
-
out[k] = deepMerge(existing, v, options);
|
|
13157
|
-
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
13158
|
-
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
13159
|
-
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
13160
|
-
}
|
|
13161
|
-
out[k] = deepMerge(existing, v, options);
|
|
13162
|
-
} else if (v !== void 0) {
|
|
13163
|
-
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
13164
|
-
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
13165
|
-
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
13166
|
-
}
|
|
13167
|
-
out[k] = v;
|
|
13168
|
-
}
|
|
13169
|
-
}
|
|
13170
|
-
return out;
|
|
13338
|
+
function stripFrontmatter(raw) {
|
|
13339
|
+
const text = normalizeLineEndings(raw);
|
|
13340
|
+
if (!text.startsWith("---")) return text;
|
|
13341
|
+
const end = text.indexOf("\n---", 4);
|
|
13342
|
+
if (end === -1) return text;
|
|
13343
|
+
let body = text.slice(end + 4);
|
|
13344
|
+
if (body.startsWith("\n")) body = body.slice(1);
|
|
13345
|
+
return body;
|
|
13171
13346
|
}
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
function escapeRegex(s) {
|
|
13175
|
-
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
13347
|
+
function normalizeLineEndings(s) {
|
|
13348
|
+
return s.replace(/\r\n?/g, "\n");
|
|
13176
13349
|
}
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13350
|
+
function parseFrontmatterBlock(block) {
|
|
13351
|
+
const out = {};
|
|
13352
|
+
const lines = block.split("\n");
|
|
13353
|
+
let i = 0;
|
|
13354
|
+
while (i < lines.length) {
|
|
13355
|
+
const line = lines[i] ?? "";
|
|
13356
|
+
const m = /^([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)$/.exec(line);
|
|
13357
|
+
if (!m) {
|
|
13358
|
+
i++;
|
|
13359
|
+
continue;
|
|
13360
|
+
}
|
|
13361
|
+
const key = m[1] ?? "";
|
|
13362
|
+
const rest = (m[2] ?? "").trim();
|
|
13363
|
+
if (key === "metadata") {
|
|
13364
|
+
const map = {};
|
|
13365
|
+
i++;
|
|
13366
|
+
while (i < lines.length) {
|
|
13367
|
+
const sub = lines[i] ?? "";
|
|
13368
|
+
const sm = /^\s+([a-zA-Z0-9_.-]+):\s*(.*)$/.exec(sub);
|
|
13369
|
+
if (!sm) break;
|
|
13370
|
+
map[sm[1] ?? ""] = unquote((sm[2] ?? "").trim());
|
|
13371
|
+
i++;
|
|
13372
|
+
}
|
|
13373
|
+
if (Object.keys(map).length > 0) out.metadata = map;
|
|
13374
|
+
continue;
|
|
13375
|
+
}
|
|
13376
|
+
if (rest === "|" || rest === ">") {
|
|
13377
|
+
const collected = [];
|
|
13378
|
+
i++;
|
|
13379
|
+
while (i < lines.length) {
|
|
13380
|
+
const sub = lines[i] ?? "";
|
|
13381
|
+
if (sub === "" || sub.startsWith(" ") || sub.startsWith(" ")) {
|
|
13382
|
+
collected.push(sub.replace(/^\s+/, ""));
|
|
13383
|
+
i++;
|
|
13384
|
+
} else break;
|
|
13385
|
+
}
|
|
13386
|
+
out[normalizeKey(key)] = collected.join("\n").trim();
|
|
13387
|
+
continue;
|
|
13388
|
+
}
|
|
13389
|
+
if (key === "allowed-tools" || key === "allowedTools") {
|
|
13390
|
+
out.allowedTools = rest.split(/[\s,]+/).filter(Boolean);
|
|
13391
|
+
i++;
|
|
13392
|
+
continue;
|
|
13393
|
+
}
|
|
13394
|
+
if (SCALAR_KEYS.has(key)) {
|
|
13395
|
+
out[key] = unquote(rest);
|
|
13396
|
+
i++;
|
|
13397
|
+
continue;
|
|
13398
|
+
}
|
|
13399
|
+
i++;
|
|
13400
|
+
}
|
|
13401
|
+
return out;
|
|
13402
|
+
}
|
|
13403
|
+
function normalizeKey(key) {
|
|
13404
|
+
return key === "allowed-tools" ? "allowedTools" : key;
|
|
13405
|
+
}
|
|
13406
|
+
function unquote(s) {
|
|
13407
|
+
if (s.length >= 2 && (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'"))) {
|
|
13408
|
+
return s.slice(1, -1);
|
|
13409
|
+
}
|
|
13410
|
+
return s;
|
|
13411
|
+
}
|
|
13412
|
+
function isValidSkillNameFormat(name) {
|
|
13413
|
+
return name.length >= 1 && name.length <= 64 && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
|
13414
|
+
}
|
|
13415
|
+
|
|
13416
|
+
// src/utils/glob-match.ts
|
|
13417
|
+
function escapeRegex(s) {
|
|
13418
|
+
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
13419
|
+
}
|
|
13420
|
+
var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
|
|
13421
|
+
var CACHE_MAX_SIZE = 2e3;
|
|
13422
|
+
var NEVER_MATCH = /[^\s\S]/;
|
|
13423
|
+
var COMMAND_WILDCARD_STOP = ";&|\\n\\r`$<>";
|
|
13424
|
+
function getCachedGlob(pattern, commandSubject = false) {
|
|
13425
|
+
const cacheKey = commandSubject ? `cmd\0${pattern}` : pattern;
|
|
13426
|
+
const cached = COMPILED_GLOB_CACHE.get(cacheKey);
|
|
13182
13427
|
if (cached) return cached;
|
|
13183
13428
|
if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
|
|
13184
13429
|
const keys = [...COMPILED_GLOB_CACHE.keys()];
|
|
@@ -13188,33 +13433,35 @@ function getCachedGlob(pattern) {
|
|
|
13188
13433
|
}
|
|
13189
13434
|
let re;
|
|
13190
13435
|
try {
|
|
13191
|
-
re = compileGlob(pattern);
|
|
13436
|
+
re = compileGlob(pattern, commandSubject);
|
|
13192
13437
|
} catch {
|
|
13193
13438
|
re = NEVER_MATCH;
|
|
13194
13439
|
}
|
|
13195
|
-
COMPILED_GLOB_CACHE.set(
|
|
13440
|
+
COMPILED_GLOB_CACHE.set(cacheKey, re);
|
|
13196
13441
|
return re;
|
|
13197
13442
|
}
|
|
13198
13443
|
var MAX_GLOB_PATTERN_LEN = 1024;
|
|
13199
|
-
function compileGlob(pattern) {
|
|
13444
|
+
function compileGlob(pattern, commandSubject = false) {
|
|
13200
13445
|
if (pattern.length > MAX_GLOB_PATTERN_LEN) {
|
|
13201
13446
|
throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
|
|
13202
13447
|
}
|
|
13448
|
+
const star = commandSubject ? `[^/${COMMAND_WILDCARD_STOP}]*` : "[^/]*";
|
|
13449
|
+
const question = commandSubject ? `[^/${COMMAND_WILDCARD_STOP}]` : "[^/]";
|
|
13203
13450
|
let i = 0;
|
|
13204
13451
|
let re = "^";
|
|
13205
13452
|
while (i < pattern.length) {
|
|
13206
13453
|
const c = pattern[i];
|
|
13207
13454
|
if (c === "*") {
|
|
13208
13455
|
if (pattern[i + 1] === "*") {
|
|
13209
|
-
re += ".*";
|
|
13456
|
+
re += commandSubject ? `[^${COMMAND_WILDCARD_STOP}]*` : ".*";
|
|
13210
13457
|
i += 2;
|
|
13211
13458
|
if (pattern[i] === "/") i++;
|
|
13212
13459
|
} else {
|
|
13213
|
-
re +=
|
|
13460
|
+
re += star;
|
|
13214
13461
|
i++;
|
|
13215
13462
|
}
|
|
13216
13463
|
} else if (c === "?") {
|
|
13217
|
-
re +=
|
|
13464
|
+
re += question;
|
|
13218
13465
|
i++;
|
|
13219
13466
|
} else if (c === "[") {
|
|
13220
13467
|
let cls = "[";
|
|
@@ -13251,6 +13498,12 @@ function matchGlob(pattern, input) {
|
|
|
13251
13498
|
function matchAny(patterns, input) {
|
|
13252
13499
|
return patterns.some((p) => matchGlob(p, input));
|
|
13253
13500
|
}
|
|
13501
|
+
function matchCommandGlob(pattern, input) {
|
|
13502
|
+
return getCachedGlob(pattern, true).test(input);
|
|
13503
|
+
}
|
|
13504
|
+
function matchAnyCommand(patterns, input) {
|
|
13505
|
+
return patterns.some((p) => matchCommandGlob(p, input));
|
|
13506
|
+
}
|
|
13254
13507
|
|
|
13255
13508
|
// src/utils/json-repair.ts
|
|
13256
13509
|
function completePartialObject(s) {
|
|
@@ -13351,10 +13604,10 @@ function validateAgainstSchema(value, schema) {
|
|
|
13351
13604
|
return { ok: errors.length === 0, errors };
|
|
13352
13605
|
}
|
|
13353
13606
|
var MAX_SCHEMA_DEPTH = 64;
|
|
13354
|
-
function walk(value, schema,
|
|
13607
|
+
function walk(value, schema, path40, errors, depth) {
|
|
13355
13608
|
if (depth > MAX_SCHEMA_DEPTH) {
|
|
13356
13609
|
errors.push({
|
|
13357
|
-
path:
|
|
13610
|
+
path: path40 || "<root>",
|
|
13358
13611
|
message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
|
|
13359
13612
|
});
|
|
13360
13613
|
return;
|
|
@@ -13362,7 +13615,7 @@ function walk(value, schema, path39, errors, depth) {
|
|
|
13362
13615
|
if (schema.enum !== void 0) {
|
|
13363
13616
|
if (!enumIncludes(schema.enum, value)) {
|
|
13364
13617
|
errors.push({
|
|
13365
|
-
path:
|
|
13618
|
+
path: path40 || "<root>",
|
|
13366
13619
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
13367
13620
|
});
|
|
13368
13621
|
return;
|
|
@@ -13371,7 +13624,7 @@ function walk(value, schema, path39, errors, depth) {
|
|
|
13371
13624
|
if (typeof schema.type === "string") {
|
|
13372
13625
|
if (!checkType(value, schema.type)) {
|
|
13373
13626
|
errors.push({
|
|
13374
|
-
path:
|
|
13627
|
+
path: path40 || "<root>",
|
|
13375
13628
|
message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`
|
|
13376
13629
|
});
|
|
13377
13630
|
return;
|
|
@@ -13383,7 +13636,7 @@ function walk(value, schema, path39, errors, depth) {
|
|
|
13383
13636
|
if (!(req in obj)) {
|
|
13384
13637
|
const expected = schema.properties?.[req]?.type;
|
|
13385
13638
|
errors.push({
|
|
13386
|
-
path: joinPath(
|
|
13639
|
+
path: joinPath(path40, req),
|
|
13387
13640
|
message: `required property missing${typeof expected === "string" ? ` (expected ${expected})` : ""}`
|
|
13388
13641
|
});
|
|
13389
13642
|
}
|
|
@@ -13391,14 +13644,14 @@ function walk(value, schema, path39, errors, depth) {
|
|
|
13391
13644
|
if (schema.properties) {
|
|
13392
13645
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
13393
13646
|
if (key in obj) {
|
|
13394
|
-
walk(obj[key], subSchema, joinPath(
|
|
13647
|
+
walk(obj[key], subSchema, joinPath(path40, key), errors, depth + 1);
|
|
13395
13648
|
}
|
|
13396
13649
|
}
|
|
13397
13650
|
}
|
|
13398
13651
|
}
|
|
13399
13652
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
13400
13653
|
for (let i = 0; i < value.length; i++) {
|
|
13401
|
-
walk(value[i], schema.items, `${
|
|
13654
|
+
walk(value[i], schema.items, `${path40}[${i}]`, errors, depth + 1);
|
|
13402
13655
|
}
|
|
13403
13656
|
}
|
|
13404
13657
|
}
|
|
@@ -13694,7 +13947,7 @@ function invalid(sessionId) {
|
|
|
13694
13947
|
|
|
13695
13948
|
// src/utils/sleep.ts
|
|
13696
13949
|
function sleep(ms) {
|
|
13697
|
-
return new Promise((
|
|
13950
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
13698
13951
|
}
|
|
13699
13952
|
|
|
13700
13953
|
// src/utils/slug.ts
|
|
@@ -13759,12 +14012,38 @@ function createToolOutputSerializer(opts = {}) {
|
|
|
13759
14012
|
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
13760
14013
|
}
|
|
13761
14014
|
const half = Math.floor(available / 2);
|
|
13762
|
-
const first = text
|
|
13763
|
-
const second =
|
|
14015
|
+
const first = utf8Prefix(text, half);
|
|
14016
|
+
const second = utf8Suffix(text, available - Buffer.byteLength(first, "utf8"));
|
|
13764
14017
|
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
13765
14018
|
}
|
|
13766
14019
|
return { serialize, enforceCap, capBytes };
|
|
13767
14020
|
}
|
|
14021
|
+
function utf8Prefix(text, maxBytes) {
|
|
14022
|
+
if (maxBytes <= 0) return "";
|
|
14023
|
+
let low = 0;
|
|
14024
|
+
let high = text.length;
|
|
14025
|
+
while (low < high) {
|
|
14026
|
+
const mid = Math.ceil((low + high) / 2);
|
|
14027
|
+
if (Buffer.byteLength(text.slice(0, mid), "utf8") <= maxBytes) low = mid;
|
|
14028
|
+
else high = mid - 1;
|
|
14029
|
+
}
|
|
14030
|
+
let end = low;
|
|
14031
|
+
if (end > 0 && /[\uD800-\uDBFF]/.test(text[end - 1])) end--;
|
|
14032
|
+
return text.slice(0, end);
|
|
14033
|
+
}
|
|
14034
|
+
function utf8Suffix(text, maxBytes) {
|
|
14035
|
+
if (maxBytes <= 0) return "";
|
|
14036
|
+
let low = 0;
|
|
14037
|
+
let high = text.length;
|
|
14038
|
+
while (low < high) {
|
|
14039
|
+
const chars = Math.ceil((low + high) / 2);
|
|
14040
|
+
if (Buffer.byteLength(text.slice(text.length - chars), "utf8") <= maxBytes) low = chars;
|
|
14041
|
+
else high = chars - 1;
|
|
14042
|
+
}
|
|
14043
|
+
let start = text.length - low;
|
|
14044
|
+
if (start < text.length && /[\uDC00-\uDFFF]/.test(text[start])) start++;
|
|
14045
|
+
return text.slice(start);
|
|
14046
|
+
}
|
|
13768
14047
|
function renderToolObject(toolName, obj, input) {
|
|
13769
14048
|
if (toolName === "read" && typeof obj["text"] === "string") {
|
|
13770
14049
|
return joinSections([
|
|
@@ -14389,13 +14668,43 @@ function normalizePathSubject(value) {
|
|
|
14389
14668
|
function isPathSubjectKey(subjectKey) {
|
|
14390
14669
|
return subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
|
|
14391
14670
|
}
|
|
14671
|
+
function renderCommandLine(command, args) {
|
|
14672
|
+
if (!Array.isArray(args) || args.length === 0) return command;
|
|
14673
|
+
const rendered = args.map((arg) => {
|
|
14674
|
+
const str = String(arg);
|
|
14675
|
+
return /\s/.test(str) ? `"${str.replace(/"/g, '\\"')}"` : str;
|
|
14676
|
+
});
|
|
14677
|
+
return [command, ...rendered].join(" ");
|
|
14678
|
+
}
|
|
14392
14679
|
function subjectForToolInput(toolName, input, subjectKey) {
|
|
14393
14680
|
if (!input || typeof input !== "object") return void 0;
|
|
14394
14681
|
const obj = input;
|
|
14395
14682
|
if (subjectKey) {
|
|
14396
14683
|
const value = obj[subjectKey];
|
|
14397
14684
|
if (typeof value === "string") {
|
|
14398
|
-
|
|
14685
|
+
if (isPathSubjectKey(subjectKey)) {
|
|
14686
|
+
const normalized = normalizePathSubject(value);
|
|
14687
|
+
if (subjectKey === "files" && obj["check"] === true) return `${normalized}:check`;
|
|
14688
|
+
if (subjectKey === "files" && obj["dry_run"] === true) return `${normalized}:dry-run`;
|
|
14689
|
+
return normalized;
|
|
14690
|
+
}
|
|
14691
|
+
if (subjectKey === "command") {
|
|
14692
|
+
const rendered = renderCommandLine(value, obj["args"]);
|
|
14693
|
+
if (value === "commit" && obj["dry_run"] === true) {
|
|
14694
|
+
return `${escapeGlobSubject(rendered)}:dry-run`;
|
|
14695
|
+
}
|
|
14696
|
+
return escapeGlobSubject(rendered);
|
|
14697
|
+
}
|
|
14698
|
+
if (subjectKey === "directory" && obj["dry_run"] === true) {
|
|
14699
|
+
return `${escapeGlobSubject(value)}:dry-run`;
|
|
14700
|
+
}
|
|
14701
|
+
if (subjectKey === "packages" && obj["dry_run"] === true) {
|
|
14702
|
+
return `${escapeGlobSubject(value)}:dry-run`;
|
|
14703
|
+
}
|
|
14704
|
+
if (subjectKey === "name" && obj["dry_run"] === true) {
|
|
14705
|
+
return `${escapeGlobSubject(value)}:dry-run`;
|
|
14706
|
+
}
|
|
14707
|
+
return escapeGlobSubject(value);
|
|
14399
14708
|
}
|
|
14400
14709
|
}
|
|
14401
14710
|
if (toolName === "bash" && typeof obj.command === "string") {
|
|
@@ -14419,6 +14728,23 @@ import * as fsp7 from "node:fs/promises";
|
|
|
14419
14728
|
import * as path12 from "node:path";
|
|
14420
14729
|
import { createInterface } from "node:readline";
|
|
14421
14730
|
|
|
14731
|
+
// src/storage/session-workspace-checkpoints.ts
|
|
14732
|
+
function requiredCheckpointCas(checkpointCas, operation) {
|
|
14733
|
+
if (!checkpointCas) {
|
|
14734
|
+
throw new Error(`Workspace checkpoint ${operation} requires a projectRoot-aware session store`);
|
|
14735
|
+
}
|
|
14736
|
+
return checkpointCas;
|
|
14737
|
+
}
|
|
14738
|
+
function captureCheckpoint(checkpointCas, sessionId, promptIndex) {
|
|
14739
|
+
return requiredCheckpointCas(checkpointCas, "capture").capture(sessionId, promptIndex);
|
|
14740
|
+
}
|
|
14741
|
+
function materializeCheckpoint(checkpointCas, checkpoint, targetRoot) {
|
|
14742
|
+
return requiredCheckpointCas(checkpointCas, "materialization").materialize(
|
|
14743
|
+
checkpoint,
|
|
14744
|
+
targetRoot
|
|
14745
|
+
);
|
|
14746
|
+
}
|
|
14747
|
+
|
|
14422
14748
|
// src/storage/session-helpers.ts
|
|
14423
14749
|
function consumeControlString(value, start) {
|
|
14424
14750
|
let index = start;
|
|
@@ -15394,7 +15720,8 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
15394
15720
|
promptIndex,
|
|
15395
15721
|
promptPreview,
|
|
15396
15722
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15397
|
-
fileCount
|
|
15723
|
+
fileCount,
|
|
15724
|
+
...workspaceCheckpoint ? { workspaceCheckpoint } : {}
|
|
15398
15725
|
});
|
|
15399
15726
|
}
|
|
15400
15727
|
async writeFileSnapshot(promptIndex, files) {
|
|
@@ -15587,8 +15914,8 @@ function sha256(content) {
|
|
|
15587
15914
|
return createHash2("sha256").update(content).digest("hex");
|
|
15588
15915
|
}
|
|
15589
15916
|
function isInside(root, target) {
|
|
15590
|
-
const
|
|
15591
|
-
return
|
|
15917
|
+
const relative8 = path13.relative(root, target);
|
|
15918
|
+
return relative8 === "" || !relative8.startsWith("..") && !path13.isAbsolute(relative8);
|
|
15592
15919
|
}
|
|
15593
15920
|
function normalizeRelative(input) {
|
|
15594
15921
|
if (!input || path13.isAbsolute(input)) return null;
|
|
@@ -15600,8 +15927,8 @@ function normalizeRelative(input) {
|
|
|
15600
15927
|
function parseNulPaths(output) {
|
|
15601
15928
|
return output.split("\0").map(normalizeRelative).filter((value) => value !== null);
|
|
15602
15929
|
}
|
|
15603
|
-
function isWrongStackWorktreePath(
|
|
15604
|
-
return
|
|
15930
|
+
function isWrongStackWorktreePath(relative8) {
|
|
15931
|
+
return relative8 === ".wrongstack/worktrees" || relative8.startsWith(".wrongstack/worktrees/");
|
|
15605
15932
|
}
|
|
15606
15933
|
var SessionCheckpointCas = class {
|
|
15607
15934
|
rootDir;
|
|
@@ -15627,16 +15954,16 @@ var SessionCheckpointCas = class {
|
|
|
15627
15954
|
}
|
|
15628
15955
|
const relativePaths = [
|
|
15629
15956
|
.../* @__PURE__ */ new Set([...parseNulPaths(tracked.stdout), ...parseNulPaths(untracked.stdout)])
|
|
15630
|
-
].filter((
|
|
15957
|
+
].filter((relative8) => !isWrongStackWorktreePath(relative8)).sort();
|
|
15631
15958
|
const unresolved = [];
|
|
15632
15959
|
let scheduledBytes = 0;
|
|
15633
15960
|
const captured = await mapWithConcurrency(
|
|
15634
15961
|
relativePaths,
|
|
15635
15962
|
CAPTURE_CONCURRENCY,
|
|
15636
|
-
async (
|
|
15637
|
-
const absolute = path13.resolve(this.projectRoot, ...
|
|
15963
|
+
async (relative8) => {
|
|
15964
|
+
const absolute = path13.resolve(this.projectRoot, ...relative8.split("/"));
|
|
15638
15965
|
if (!isInside(this.projectRoot, absolute)) {
|
|
15639
|
-
unresolved.push({ path:
|
|
15966
|
+
unresolved.push({ path: relative8, reason: "path escapes project root" });
|
|
15640
15967
|
return null;
|
|
15641
15968
|
}
|
|
15642
15969
|
try {
|
|
@@ -15646,27 +15973,27 @@ var SessionCheckpointCas = class {
|
|
|
15646
15973
|
const resolvedLink = path13.resolve(path13.dirname(absolute), linkTarget);
|
|
15647
15974
|
if (path13.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
|
|
15648
15975
|
unresolved.push({
|
|
15649
|
-
path:
|
|
15976
|
+
path: relative8,
|
|
15650
15977
|
reason: "symlink target escapes project root"
|
|
15651
15978
|
});
|
|
15652
15979
|
return null;
|
|
15653
15980
|
}
|
|
15654
|
-
return { path:
|
|
15981
|
+
return { path: relative8, state: "symlink", linkTarget };
|
|
15655
15982
|
}
|
|
15656
15983
|
if (!stat15.isFile()) {
|
|
15657
|
-
unresolved.push({ path:
|
|
15984
|
+
unresolved.push({ path: relative8, reason: "changed path is not a regular file" });
|
|
15658
15985
|
return null;
|
|
15659
15986
|
}
|
|
15660
15987
|
if (stat15.size > MAX_BLOB_BYTES) {
|
|
15661
15988
|
unresolved.push({
|
|
15662
|
-
path:
|
|
15989
|
+
path: relative8,
|
|
15663
15990
|
reason: `file exceeds ${MAX_BLOB_BYTES}-byte checkpoint blob limit`
|
|
15664
15991
|
});
|
|
15665
15992
|
return null;
|
|
15666
15993
|
}
|
|
15667
15994
|
if (scheduledBytes + stat15.size > MAX_CHECKPOINT_BYTES) {
|
|
15668
15995
|
unresolved.push({
|
|
15669
|
-
path:
|
|
15996
|
+
path: relative8,
|
|
15670
15997
|
reason: `checkpoint exceeds ${MAX_CHECKPOINT_BYTES}-byte aggregate blob limit`
|
|
15671
15998
|
});
|
|
15672
15999
|
return null;
|
|
@@ -15675,12 +16002,12 @@ var SessionCheckpointCas = class {
|
|
|
15675
16002
|
const content = await fsp8.readFile(absolute);
|
|
15676
16003
|
const blobHash = sha256(content);
|
|
15677
16004
|
await this.putBlob(blobHash, content);
|
|
15678
|
-
return { path:
|
|
16005
|
+
return { path: relative8, state: "file", blobHash, mode: stat15.mode & 511 };
|
|
15679
16006
|
} catch (err) {
|
|
15680
16007
|
if (err.code === "ENOENT") {
|
|
15681
|
-
return { path:
|
|
16008
|
+
return { path: relative8, state: "absent" };
|
|
15682
16009
|
}
|
|
15683
|
-
unresolved.push({ path:
|
|
16010
|
+
unresolved.push({ path: relative8, reason: toErrorMessage(err) });
|
|
15684
16011
|
return null;
|
|
15685
16012
|
}
|
|
15686
16013
|
}
|
|
@@ -15870,8 +16197,8 @@ var SessionCheckpointCas = class {
|
|
|
15870
16197
|
}
|
|
15871
16198
|
return parsed;
|
|
15872
16199
|
}
|
|
15873
|
-
async safeOutputPath(target, realTarget,
|
|
15874
|
-
const normalized = normalizeRelative(
|
|
16200
|
+
async safeOutputPath(target, realTarget, relative8) {
|
|
16201
|
+
const normalized = normalizeRelative(relative8);
|
|
15875
16202
|
if (!normalized) throw new Error("invalid relative path");
|
|
15876
16203
|
const output = path13.resolve(target, ...normalized.split("/"));
|
|
15877
16204
|
if (!isInside(target, output)) throw new Error("path escapes checkpoint target");
|
|
@@ -15891,7 +16218,7 @@ var SessionCheckpointCas = class {
|
|
|
15891
16218
|
}
|
|
15892
16219
|
};
|
|
15893
16220
|
function defaultRunGit(args, cwd) {
|
|
15894
|
-
return new Promise((
|
|
16221
|
+
return new Promise((resolve17) => {
|
|
15895
16222
|
const stdoutChunks = [];
|
|
15896
16223
|
const stderrChunks = [];
|
|
15897
16224
|
let stdoutBytes = 0;
|
|
@@ -15934,8 +16261,8 @@ function defaultRunGit(args, cwd) {
|
|
|
15934
16261
|
stdoutTruncated,
|
|
15935
16262
|
stderrTruncated
|
|
15936
16263
|
});
|
|
15937
|
-
child.on("error", (err) =>
|
|
15938
|
-
child.on("close", (code) =>
|
|
16264
|
+
child.on("error", (err) => resolve17(result(1, err.message)));
|
|
16265
|
+
child.on("close", (code) => resolve17(result(code ?? 1)));
|
|
15939
16266
|
});
|
|
15940
16267
|
}
|
|
15941
16268
|
|
|
@@ -16012,8 +16339,8 @@ var MAX_REVALIDATE_BYTES = 5 * 1024 * 1024;
|
|
|
16012
16339
|
var VALIDATION_CONCURRENCY = 8;
|
|
16013
16340
|
var NOTICE_PATH_LIMIT = 20;
|
|
16014
16341
|
function isInside2(root, target) {
|
|
16015
|
-
const
|
|
16016
|
-
return
|
|
16342
|
+
const relative8 = path15.relative(root, target);
|
|
16343
|
+
return relative8 === "" || !relative8.startsWith("..") && !path15.isAbsolute(relative8);
|
|
16017
16344
|
}
|
|
16018
16345
|
function errno(err) {
|
|
16019
16346
|
return err && typeof err === "object" && "code" in err ? String(err.code) : void 0;
|
|
@@ -16100,8 +16427,8 @@ function formatResumeValidationNotice(validation, projectRoot) {
|
|
|
16100
16427
|
if (validation.staleFiles.length === 0) return null;
|
|
16101
16428
|
const root = path15.resolve(projectRoot);
|
|
16102
16429
|
const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {
|
|
16103
|
-
const
|
|
16104
|
-
const display = isInside2(root, entry.path) ?
|
|
16430
|
+
const relative8 = path15.relative(root, entry.path);
|
|
16431
|
+
const display = isInside2(root, entry.path) ? relative8 || "." : entry.path;
|
|
16105
16432
|
return `- ${JSON.stringify(display)} [${entry.status}]`;
|
|
16106
16433
|
});
|
|
16107
16434
|
const omitted = validation.staleFiles.length - shown.length;
|
|
@@ -16123,57 +16450,82 @@ function formatInterruptedToolNotice(pendingToolUseCount) {
|
|
|
16123
16450
|
].join("\n");
|
|
16124
16451
|
}
|
|
16125
16452
|
|
|
16126
|
-
// src/storage/session-store/
|
|
16453
|
+
// src/storage/session-store/delete-session-artifacts.ts
|
|
16127
16454
|
import * as fsp10 from "node:fs/promises";
|
|
16128
|
-
|
|
16129
|
-
|
|
16130
|
-
|
|
16131
|
-
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16455
|
+
import * as path17 from "node:path";
|
|
16456
|
+
|
|
16457
|
+
// src/storage/session-store/paths.ts
|
|
16458
|
+
import * as path16 from "node:path";
|
|
16459
|
+
function sessionPath(storeDir, id, ext) {
|
|
16460
|
+
return sessionScopedPath(storeDir, id, ext);
|
|
16461
|
+
}
|
|
16462
|
+
function shardManifestPath(storeDir, shardKey) {
|
|
16463
|
+
return shardKey ? path16.join(storeDir, shardKey, "_manifest.json") : path16.join(storeDir, "_manifest.json");
|
|
16464
|
+
}
|
|
16465
|
+
function shardKeyForSessionId(id) {
|
|
16466
|
+
const dirName = path16.dirname(id);
|
|
16467
|
+
return dirName === "." ? "" : dirName;
|
|
16468
|
+
}
|
|
16469
|
+
async function ensureShardDir(storeDir, id) {
|
|
16470
|
+
const dirPath = path16.dirname(sessionScopedPath(storeDir, id, ""));
|
|
16471
|
+
await ensureDir(dirPath);
|
|
16472
|
+
return dirPath;
|
|
16473
|
+
}
|
|
16474
|
+
|
|
16475
|
+
// src/storage/session-store/delete-session-artifacts.ts
|
|
16476
|
+
async function deleteSessionArtifacts({
|
|
16477
|
+
rootDir,
|
|
16478
|
+
id,
|
|
16479
|
+
jsonlPath
|
|
16480
|
+
}) {
|
|
16481
|
+
const shardDir = path17.dirname(jsonlPath);
|
|
16482
|
+
const base = path17.basename(id);
|
|
16483
|
+
const sessDir = path17.join(shardDir, base);
|
|
16484
|
+
const deletions = [
|
|
16485
|
+
fsp10.unlink(jsonlPath),
|
|
16486
|
+
fsp10.unlink(sessionPath(rootDir, id, ".summary.json")),
|
|
16487
|
+
fsp10.unlink(sessionPath(rootDir, id, ".plan.json")),
|
|
16488
|
+
fsp10.unlink(sessionPath(rootDir, id, ".tasks.json")),
|
|
16489
|
+
fsp10.unlink(sessionPath(rootDir, id, ".todos.json"))
|
|
16490
|
+
];
|
|
16491
|
+
const results = await Promise.allSettled(deletions);
|
|
16492
|
+
for (const result of results) {
|
|
16493
|
+
if (result.status === "rejected") {
|
|
16494
|
+
warnDeleteFailure(id, result.reason);
|
|
16142
16495
|
}
|
|
16143
16496
|
}
|
|
16497
|
+
await fsp10.rm(sessDir, { recursive: true, force: true }).catch((err) => {
|
|
16498
|
+
console.warn(
|
|
16499
|
+
JSON.stringify({
|
|
16500
|
+
level: "warn",
|
|
16501
|
+
event: "session_store.rmdir_failed",
|
|
16502
|
+
sessionId: id,
|
|
16503
|
+
message: toErrorMessage(err),
|
|
16504
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16505
|
+
})
|
|
16506
|
+
);
|
|
16507
|
+
});
|
|
16144
16508
|
}
|
|
16145
|
-
|
|
16146
|
-
const
|
|
16147
|
-
if (
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16161
|
-
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
} catch {
|
|
16166
|
-
completeLength = lastNewline + 1;
|
|
16167
|
-
}
|
|
16168
|
-
}
|
|
16169
|
-
return {
|
|
16170
|
-
raw: buffer.subarray(0, completeLength).toString("utf8"),
|
|
16171
|
-
end: start + completeLength
|
|
16172
|
-
};
|
|
16173
|
-
} catch {
|
|
16174
|
-
return null;
|
|
16175
|
-
} finally {
|
|
16176
|
-
await handle?.close().catch(() => void 0);
|
|
16509
|
+
function warnDeleteFailure(id, reason) {
|
|
16510
|
+
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
16511
|
+
if (reason?.code === "ENOENT") return;
|
|
16512
|
+
console.warn(
|
|
16513
|
+
JSON.stringify({
|
|
16514
|
+
level: "warn",
|
|
16515
|
+
event: "session_store.delete_failed",
|
|
16516
|
+
sessionId: id,
|
|
16517
|
+
message: msg,
|
|
16518
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16519
|
+
})
|
|
16520
|
+
);
|
|
16521
|
+
}
|
|
16522
|
+
|
|
16523
|
+
// src/storage/session-store/delete-session-guards.ts
|
|
16524
|
+
async function assertSessionCanBeDeleted(sessionId, isSessionInUse) {
|
|
16525
|
+
if (!isSessionInUse) return;
|
|
16526
|
+
const reason = await isSessionInUse(sessionId);
|
|
16527
|
+
if (reason) {
|
|
16528
|
+
throw new Error(`Session ${sessionId} is in use (${reason}) and cannot be deleted.`);
|
|
16177
16529
|
}
|
|
16178
16530
|
}
|
|
16179
16531
|
|
|
@@ -16187,7 +16539,7 @@ function isSessionJsonlFileName(name) {
|
|
|
16187
16539
|
|
|
16188
16540
|
// src/storage/session-store/directory-session-files.ts
|
|
16189
16541
|
import * as fsp11 from "node:fs/promises";
|
|
16190
|
-
import * as
|
|
16542
|
+
import * as path18 from "node:path";
|
|
16191
16543
|
function sessionIdForFile(prefix, name) {
|
|
16192
16544
|
const base = name.replace(/\.jsonl$/, "");
|
|
16193
16545
|
return prefix ? `${prefix}/${base}` : base;
|
|
@@ -16209,12 +16561,12 @@ async function collectSessionFiles(dir, prefix = "", depth = 0) {
|
|
|
16209
16561
|
if (entry.isDirectory()) {
|
|
16210
16562
|
dirEntries.push(entry);
|
|
16211
16563
|
} else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {
|
|
16212
|
-
files.push({ id: sessionIdForFile(prefix, entry.name), filePath:
|
|
16564
|
+
files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path18.join(dir, entry.name) });
|
|
16213
16565
|
}
|
|
16214
16566
|
}
|
|
16215
16567
|
const childFileArrays = await Promise.all(
|
|
16216
16568
|
dirEntries.map(
|
|
16217
|
-
(entry) => collectSessionFiles(
|
|
16569
|
+
(entry) => collectSessionFiles(path18.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
|
|
16218
16570
|
)
|
|
16219
16571
|
);
|
|
16220
16572
|
return [...childFileArrays.flat(), ...files];
|
|
@@ -16238,210 +16590,35 @@ async function collectSessionIds(dir, prefix = "", depth = 0) {
|
|
|
16238
16590
|
}
|
|
16239
16591
|
const childIdArrays = await Promise.all(
|
|
16240
16592
|
dirEntries.map(
|
|
16241
|
-
(entry) => collectSessionIds(
|
|
16593
|
+
(entry) => collectSessionIds(path18.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
|
|
16242
16594
|
)
|
|
16243
16595
|
);
|
|
16244
16596
|
return [...childIdArrays.flat(), ...fileIds];
|
|
16245
16597
|
}
|
|
16246
16598
|
|
|
16247
|
-
// src/storage/session-store/
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
|
|
16251
|
-
|
|
16252
|
-
|
|
16253
|
-
|
|
16254
|
-
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16599
|
+
// src/storage/session-store/events.ts
|
|
16600
|
+
function emitSessionStoreRead(events, sessionId, filePath, operation, outcome, durationMs, error2) {
|
|
16601
|
+
events?.emit("storage.read", {
|
|
16602
|
+
sessionId,
|
|
16603
|
+
store: "session",
|
|
16604
|
+
filePath,
|
|
16605
|
+
operation,
|
|
16606
|
+
outcome,
|
|
16607
|
+
durationMs,
|
|
16608
|
+
...error2 !== void 0 ? { error: error2 } : {}
|
|
16609
|
+
});
|
|
16258
16610
|
}
|
|
16259
|
-
function
|
|
16260
|
-
|
|
16261
|
-
|
|
16262
|
-
|
|
16263
|
-
|
|
16264
|
-
|
|
16265
|
-
|
|
16266
|
-
|
|
16267
|
-
}
|
|
16268
|
-
|
|
16269
|
-
|
|
16270
|
-
async function deleteSessionArtifacts({
|
|
16271
|
-
rootDir,
|
|
16272
|
-
id,
|
|
16273
|
-
jsonlPath
|
|
16274
|
-
}) {
|
|
16275
|
-
const shardDir = path18.dirname(jsonlPath);
|
|
16276
|
-
const base = path18.basename(id);
|
|
16277
|
-
const sessDir = path18.join(shardDir, base);
|
|
16278
|
-
const deletions = [
|
|
16279
|
-
fsp12.unlink(jsonlPath),
|
|
16280
|
-
fsp12.unlink(sessionPath(rootDir, id, ".summary.json")),
|
|
16281
|
-
fsp12.unlink(sessionPath(rootDir, id, ".plan.json")),
|
|
16282
|
-
fsp12.unlink(sessionPath(rootDir, id, ".tasks.json")),
|
|
16283
|
-
fsp12.unlink(sessionPath(rootDir, id, ".todos.json"))
|
|
16284
|
-
];
|
|
16285
|
-
const results = await Promise.allSettled(deletions);
|
|
16286
|
-
for (const result of results) {
|
|
16287
|
-
if (result.status === "rejected") {
|
|
16288
|
-
warnDeleteFailure(id, result.reason);
|
|
16289
|
-
}
|
|
16290
|
-
}
|
|
16291
|
-
await fsp12.rm(sessDir, { recursive: true, force: true }).catch((err) => {
|
|
16292
|
-
console.warn(
|
|
16293
|
-
JSON.stringify({
|
|
16294
|
-
level: "warn",
|
|
16295
|
-
event: "session_store.rmdir_failed",
|
|
16296
|
-
sessionId: id,
|
|
16297
|
-
message: toErrorMessage(err),
|
|
16298
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16299
|
-
})
|
|
16300
|
-
);
|
|
16301
|
-
});
|
|
16302
|
-
}
|
|
16303
|
-
function warnDeleteFailure(id, reason) {
|
|
16304
|
-
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
16305
|
-
if (reason?.code === "ENOENT") return;
|
|
16306
|
-
console.warn(
|
|
16307
|
-
JSON.stringify({
|
|
16308
|
-
level: "warn",
|
|
16309
|
-
event: "session_store.delete_failed",
|
|
16310
|
-
sessionId: id,
|
|
16311
|
-
message: msg,
|
|
16312
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16313
|
-
})
|
|
16314
|
-
);
|
|
16315
|
-
}
|
|
16316
|
-
|
|
16317
|
-
// src/storage/session-store/delete-session-guards.ts
|
|
16318
|
-
async function assertSessionCanBeDeleted(sessionId, isSessionInUse) {
|
|
16319
|
-
if (!isSessionInUse) return;
|
|
16320
|
-
const reason = await isSessionInUse(sessionId);
|
|
16321
|
-
if (reason) {
|
|
16322
|
-
throw new Error(`Session ${sessionId} is in use (${reason}) and cannot be deleted.`);
|
|
16323
|
-
}
|
|
16324
|
-
}
|
|
16325
|
-
|
|
16326
|
-
// src/storage/session-store/load-cache.ts
|
|
16327
|
-
var LOAD_CACHE_MAX_ENTRIES = 50;
|
|
16328
|
-
var LOAD_CACHE_MAX_BYTES = 64 * 1024 * 1024;
|
|
16329
|
-
var SessionLoadCache = class {
|
|
16330
|
-
constructor(entries = /* @__PURE__ */ new Map()) {
|
|
16331
|
-
this.entries = entries;
|
|
16332
|
-
}
|
|
16333
|
-
entries;
|
|
16334
|
-
bytes = 0;
|
|
16335
|
-
clear(sessionId) {
|
|
16336
|
-
if (sessionId !== void 0) {
|
|
16337
|
-
this.delete(sessionId);
|
|
16338
|
-
return;
|
|
16339
|
-
}
|
|
16340
|
-
this.entries.clear();
|
|
16341
|
-
this.bytes = 0;
|
|
16342
|
-
}
|
|
16343
|
-
getFresh(id, stat15, full) {
|
|
16344
|
-
const cached = this.entries.get(id);
|
|
16345
|
-
if (!cached || cached.mtimeMs !== stat15.mtimeMs || cached.size !== stat15.size) {
|
|
16346
|
-
return null;
|
|
16347
|
-
}
|
|
16348
|
-
this.entries.delete(id);
|
|
16349
|
-
this.entries.set(id, cached);
|
|
16350
|
-
if (full) return cached.data;
|
|
16351
|
-
return { ...cached.data, messages: [] };
|
|
16352
|
-
}
|
|
16353
|
-
set(id, stat15, data) {
|
|
16354
|
-
this.delete(id);
|
|
16355
|
-
while (stat15.size <= LOAD_CACHE_MAX_BYTES && (this.entries.size >= LOAD_CACHE_MAX_ENTRIES || this.bytes + stat15.size > LOAD_CACHE_MAX_BYTES)) {
|
|
16356
|
-
const oldest = this.entries.keys().next().value;
|
|
16357
|
-
if (oldest === void 0) break;
|
|
16358
|
-
this.delete(oldest);
|
|
16359
|
-
}
|
|
16360
|
-
if (stat15.size <= LOAD_CACHE_MAX_BYTES) {
|
|
16361
|
-
this.entries.set(id, { mtimeMs: stat15.mtimeMs, size: stat15.size, data });
|
|
16362
|
-
this.bytes += stat15.size;
|
|
16363
|
-
}
|
|
16364
|
-
}
|
|
16365
|
-
delete(sessionId) {
|
|
16366
|
-
const cached = this.entries.get(sessionId);
|
|
16367
|
-
if (cached) this.bytes = Math.max(0, this.bytes - cached.size);
|
|
16368
|
-
this.entries.delete(sessionId);
|
|
16369
|
-
}
|
|
16370
|
-
};
|
|
16371
|
-
|
|
16372
|
-
// src/storage/session-store/prune-helpers.ts
|
|
16373
|
-
import * as fsp13 from "node:fs/promises";
|
|
16374
|
-
import * as path19 from "node:path";
|
|
16375
|
-
function isPrunableSessionJsonl(name) {
|
|
16376
|
-
return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
16377
|
-
}
|
|
16378
|
-
async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
|
|
16379
|
-
const cutoff = Date.now() - maxAgeDays * 864e5;
|
|
16380
|
-
let deleted = 0;
|
|
16381
|
-
const pruneFile = async (dir, name, prefix) => {
|
|
16382
|
-
const jsonlPath = path19.join(dir, name);
|
|
16383
|
-
try {
|
|
16384
|
-
const stat15 = await fsp13.stat(jsonlPath);
|
|
16385
|
-
if (stat15.mtimeMs >= cutoff) return;
|
|
16386
|
-
} catch {
|
|
16387
|
-
return;
|
|
16388
|
-
}
|
|
16389
|
-
const base = name.replace(/\.jsonl$/, "");
|
|
16390
|
-
const id = prefix ? `${prefix}/${base}` : base;
|
|
16391
|
-
await deleteSession(id);
|
|
16392
|
-
deleted++;
|
|
16393
|
-
};
|
|
16394
|
-
const entries = await fsp13.readdir(storeDir, { withFileTypes: true }).catch(() => []);
|
|
16395
|
-
for (const entry of entries) {
|
|
16396
|
-
if (entry.isFile()) {
|
|
16397
|
-
if (isPrunableSessionJsonl(entry.name)) await pruneFile(storeDir, entry.name, "");
|
|
16398
|
-
continue;
|
|
16399
|
-
}
|
|
16400
|
-
if (!entry.isDirectory()) continue;
|
|
16401
|
-
const dateDir = path19.join(storeDir, entry.name);
|
|
16402
|
-
const files = await fsp13.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
16403
|
-
for (const file of files) {
|
|
16404
|
-
if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
|
|
16405
|
-
await pruneFile(dateDir, file.name, entry.name);
|
|
16406
|
-
}
|
|
16407
|
-
}
|
|
16408
|
-
for (const entry of entries) {
|
|
16409
|
-
if (!entry.isDirectory()) continue;
|
|
16410
|
-
const dateDir = path19.join(storeDir, entry.name);
|
|
16411
|
-
try {
|
|
16412
|
-
const remaining = await fsp13.readdir(dateDir);
|
|
16413
|
-
if (remaining.length === 0) {
|
|
16414
|
-
await fsp13.rmdir(dateDir).catch(() => void 0);
|
|
16415
|
-
}
|
|
16416
|
-
} catch {
|
|
16417
|
-
}
|
|
16418
|
-
}
|
|
16419
|
-
return deleted;
|
|
16420
|
-
}
|
|
16421
|
-
|
|
16422
|
-
// src/storage/session-store/events.ts
|
|
16423
|
-
function emitSessionStoreRead(events, sessionId, filePath, operation, outcome, durationMs, error2) {
|
|
16424
|
-
events?.emit("storage.read", {
|
|
16425
|
-
sessionId,
|
|
16426
|
-
store: "session",
|
|
16427
|
-
filePath,
|
|
16428
|
-
operation,
|
|
16429
|
-
outcome,
|
|
16430
|
-
durationMs,
|
|
16431
|
-
...error2 !== void 0 ? { error: error2 } : {}
|
|
16432
|
-
});
|
|
16433
|
-
}
|
|
16434
|
-
function emitSessionStoreWrite(events, sessionId, filePath, operation, outcome, durationMs, eventCount, error2) {
|
|
16435
|
-
events?.emit("storage.write", {
|
|
16436
|
-
sessionId,
|
|
16437
|
-
store: "session",
|
|
16438
|
-
filePath,
|
|
16439
|
-
operation,
|
|
16440
|
-
outcome,
|
|
16441
|
-
durationMs,
|
|
16442
|
-
...eventCount !== void 0 ? { eventCount } : {},
|
|
16443
|
-
...error2 !== void 0 ? { error: error2 } : {}
|
|
16444
|
-
});
|
|
16611
|
+
function emitSessionStoreWrite(events, sessionId, filePath, operation, outcome, durationMs, eventCount, error2) {
|
|
16612
|
+
events?.emit("storage.write", {
|
|
16613
|
+
sessionId,
|
|
16614
|
+
store: "session",
|
|
16615
|
+
filePath,
|
|
16616
|
+
operation,
|
|
16617
|
+
outcome,
|
|
16618
|
+
durationMs,
|
|
16619
|
+
...eventCount !== void 0 ? { eventCount } : {},
|
|
16620
|
+
...error2 !== void 0 ? { error: error2 } : {}
|
|
16621
|
+
});
|
|
16445
16622
|
}
|
|
16446
16623
|
function emitSessionStoreError(events, sessionId, filePath, operation, error2, recoverable) {
|
|
16447
16624
|
events?.emit("storage.error", {
|
|
@@ -16568,6 +16745,106 @@ async function forkSession(host, id, opts = {}) {
|
|
|
16568
16745
|
}
|
|
16569
16746
|
}
|
|
16570
16747
|
|
|
16748
|
+
// src/storage/session-store/index-reader.ts
|
|
16749
|
+
import * as fsp12 from "node:fs/promises";
|
|
16750
|
+
function applySessionIndexLines(raw, byId, deleted) {
|
|
16751
|
+
for (const line of raw.split("\n")) {
|
|
16752
|
+
if (!line.trim()) continue;
|
|
16753
|
+
try {
|
|
16754
|
+
const entry = JSON.parse(line);
|
|
16755
|
+
if (entry.action === "delete" && entry.id) {
|
|
16756
|
+
deleted.add(entry.id);
|
|
16757
|
+
byId.delete(entry.id);
|
|
16758
|
+
continue;
|
|
16759
|
+
}
|
|
16760
|
+
if (entry.id && !deleted.has(entry.id)) {
|
|
16761
|
+
byId.set(entry.id, entry);
|
|
16762
|
+
}
|
|
16763
|
+
} catch {
|
|
16764
|
+
}
|
|
16765
|
+
}
|
|
16766
|
+
}
|
|
16767
|
+
async function readFileRange(file, start, end) {
|
|
16768
|
+
const length = end - start;
|
|
16769
|
+
if (length <= 0) return { raw: "", end: start };
|
|
16770
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
16771
|
+
let offset = 0;
|
|
16772
|
+
let handle;
|
|
16773
|
+
try {
|
|
16774
|
+
handle = await fsp12.open(file, "r");
|
|
16775
|
+
while (offset < length) {
|
|
16776
|
+
const { bytesRead } = await handle.read(buffer, offset, length - offset, start + offset);
|
|
16777
|
+
if (bytesRead === 0) return null;
|
|
16778
|
+
offset += bytesRead;
|
|
16779
|
+
}
|
|
16780
|
+
let completeLength = buffer.length;
|
|
16781
|
+
if (buffer.at(-1) !== 10) {
|
|
16782
|
+
const lastNewline = buffer.lastIndexOf(10);
|
|
16783
|
+
const trailing = buffer.subarray(lastNewline + 1).toString("utf8").trim();
|
|
16784
|
+
try {
|
|
16785
|
+
if (trailing) JSON.parse(trailing);
|
|
16786
|
+
else completeLength = lastNewline + 1;
|
|
16787
|
+
} catch {
|
|
16788
|
+
completeLength = lastNewline + 1;
|
|
16789
|
+
}
|
|
16790
|
+
}
|
|
16791
|
+
return {
|
|
16792
|
+
raw: buffer.subarray(0, completeLength).toString("utf8"),
|
|
16793
|
+
end: start + completeLength
|
|
16794
|
+
};
|
|
16795
|
+
} catch {
|
|
16796
|
+
return null;
|
|
16797
|
+
} finally {
|
|
16798
|
+
await handle?.close().catch(() => void 0);
|
|
16799
|
+
}
|
|
16800
|
+
}
|
|
16801
|
+
|
|
16802
|
+
// src/storage/session-store/load-cache.ts
|
|
16803
|
+
var LOAD_CACHE_MAX_ENTRIES = 50;
|
|
16804
|
+
var LOAD_CACHE_MAX_BYTES = 64 * 1024 * 1024;
|
|
16805
|
+
var SessionLoadCache = class {
|
|
16806
|
+
constructor(entries = /* @__PURE__ */ new Map()) {
|
|
16807
|
+
this.entries = entries;
|
|
16808
|
+
}
|
|
16809
|
+
entries;
|
|
16810
|
+
bytes = 0;
|
|
16811
|
+
clear(sessionId) {
|
|
16812
|
+
if (sessionId !== void 0) {
|
|
16813
|
+
this.delete(sessionId);
|
|
16814
|
+
return;
|
|
16815
|
+
}
|
|
16816
|
+
this.entries.clear();
|
|
16817
|
+
this.bytes = 0;
|
|
16818
|
+
}
|
|
16819
|
+
getFresh(id, stat15, full) {
|
|
16820
|
+
const cached = this.entries.get(id);
|
|
16821
|
+
if (!cached || cached.mtimeMs !== stat15.mtimeMs || cached.size !== stat15.size) {
|
|
16822
|
+
return null;
|
|
16823
|
+
}
|
|
16824
|
+
this.entries.delete(id);
|
|
16825
|
+
this.entries.set(id, cached);
|
|
16826
|
+
if (full) return cached.data;
|
|
16827
|
+
return { ...cached.data, messages: [] };
|
|
16828
|
+
}
|
|
16829
|
+
set(id, stat15, data) {
|
|
16830
|
+
this.delete(id);
|
|
16831
|
+
while (stat15.size <= LOAD_CACHE_MAX_BYTES && (this.entries.size >= LOAD_CACHE_MAX_ENTRIES || this.bytes + stat15.size > LOAD_CACHE_MAX_BYTES)) {
|
|
16832
|
+
const oldest = this.entries.keys().next().value;
|
|
16833
|
+
if (oldest === void 0) break;
|
|
16834
|
+
this.delete(oldest);
|
|
16835
|
+
}
|
|
16836
|
+
if (stat15.size <= LOAD_CACHE_MAX_BYTES) {
|
|
16837
|
+
this.entries.set(id, { mtimeMs: stat15.mtimeMs, size: stat15.size, data });
|
|
16838
|
+
this.bytes += stat15.size;
|
|
16839
|
+
}
|
|
16840
|
+
}
|
|
16841
|
+
delete(sessionId) {
|
|
16842
|
+
const cached = this.entries.get(sessionId);
|
|
16843
|
+
if (cached) this.bytes = Math.max(0, this.bytes - cached.size);
|
|
16844
|
+
this.entries.delete(sessionId);
|
|
16845
|
+
}
|
|
16846
|
+
};
|
|
16847
|
+
|
|
16571
16848
|
// src/storage/session-store/load-session-data.ts
|
|
16572
16849
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
16573
16850
|
import { createInterface as createInterface2 } from "node:readline";
|
|
@@ -16790,36 +17067,208 @@ function replaySessionEvent(params) {
|
|
|
16790
17067
|
emitDamaged(params, `Orphan tool_result "${ev.id}" has no matching tool_use`);
|
|
16791
17068
|
return { exactJournalActive };
|
|
16792
17069
|
}
|
|
16793
|
-
openToolUses.delete(ev.id);
|
|
16794
|
-
const resultBlock = {
|
|
16795
|
-
type: "tool_result",
|
|
16796
|
-
tool_use_id: ev.id,
|
|
16797
|
-
content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
|
|
16798
|
-
is_error: ev.isError
|
|
16799
|
-
};
|
|
16800
|
-
const last = messages[messages.length - 1];
|
|
16801
|
-
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
16802
|
-
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
16803
|
-
last.content.push(resultBlock);
|
|
16804
|
-
} else {
|
|
16805
|
-
messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
|
|
17070
|
+
openToolUses.delete(ev.id);
|
|
17071
|
+
const resultBlock = {
|
|
17072
|
+
type: "tool_result",
|
|
17073
|
+
tool_use_id: ev.id,
|
|
17074
|
+
content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
|
|
17075
|
+
is_error: ev.isError
|
|
17076
|
+
};
|
|
17077
|
+
const last = messages[messages.length - 1];
|
|
17078
|
+
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
17079
|
+
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
17080
|
+
last.content.push(resultBlock);
|
|
17081
|
+
} else {
|
|
17082
|
+
messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
|
|
17083
|
+
}
|
|
17084
|
+
}
|
|
17085
|
+
return { exactJournalActive };
|
|
17086
|
+
}
|
|
17087
|
+
function accumulateUsage(usage, ev) {
|
|
17088
|
+
return {
|
|
17089
|
+
input: usage.input + (ev.usage.input ?? 0),
|
|
17090
|
+
output: usage.output + (ev.usage.output ?? 0),
|
|
17091
|
+
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
17092
|
+
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
17093
|
+
};
|
|
17094
|
+
}
|
|
17095
|
+
function emitDamaged(params, detail) {
|
|
17096
|
+
params.events?.emit("session.damaged", {
|
|
17097
|
+
sessionId: params.id,
|
|
17098
|
+
detail
|
|
17099
|
+
});
|
|
17100
|
+
}
|
|
17101
|
+
|
|
17102
|
+
// src/storage/session-store/prune-helpers.ts
|
|
17103
|
+
import * as fsp13 from "node:fs/promises";
|
|
17104
|
+
import * as path19 from "node:path";
|
|
17105
|
+
function isPrunableSessionJsonl(name) {
|
|
17106
|
+
return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
17107
|
+
}
|
|
17108
|
+
async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
|
|
17109
|
+
const cutoff = Date.now() - maxAgeDays * 864e5;
|
|
17110
|
+
let deleted = 0;
|
|
17111
|
+
const pruneFile = async (dir, name, prefix) => {
|
|
17112
|
+
const jsonlPath = path19.join(dir, name);
|
|
17113
|
+
try {
|
|
17114
|
+
const stat15 = await fsp13.stat(jsonlPath);
|
|
17115
|
+
if (stat15.mtimeMs >= cutoff) return;
|
|
17116
|
+
} catch {
|
|
17117
|
+
return;
|
|
17118
|
+
}
|
|
17119
|
+
const base = name.replace(/\.jsonl$/, "");
|
|
17120
|
+
const id = prefix ? `${prefix}/${base}` : base;
|
|
17121
|
+
await deleteSession(id);
|
|
17122
|
+
deleted++;
|
|
17123
|
+
};
|
|
17124
|
+
const entries = await fsp13.readdir(storeDir, { withFileTypes: true }).catch(() => []);
|
|
17125
|
+
for (const entry of entries) {
|
|
17126
|
+
if (entry.isFile()) {
|
|
17127
|
+
if (isPrunableSessionJsonl(entry.name)) await pruneFile(storeDir, entry.name, "");
|
|
17128
|
+
continue;
|
|
17129
|
+
}
|
|
17130
|
+
if (!entry.isDirectory()) continue;
|
|
17131
|
+
const dateDir = path19.join(storeDir, entry.name);
|
|
17132
|
+
const files = await fsp13.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
17133
|
+
for (const file of files) {
|
|
17134
|
+
if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
|
|
17135
|
+
await pruneFile(dateDir, file.name, entry.name);
|
|
17136
|
+
}
|
|
17137
|
+
}
|
|
17138
|
+
for (const entry of entries) {
|
|
17139
|
+
if (!entry.isDirectory()) continue;
|
|
17140
|
+
const dateDir = path19.join(storeDir, entry.name);
|
|
17141
|
+
try {
|
|
17142
|
+
const remaining = await fsp13.readdir(dateDir);
|
|
17143
|
+
if (remaining.length === 0) {
|
|
17144
|
+
await fsp13.rmdir(dateDir).catch(() => void 0);
|
|
17145
|
+
}
|
|
17146
|
+
} catch {
|
|
17147
|
+
}
|
|
17148
|
+
}
|
|
17149
|
+
return deleted;
|
|
17150
|
+
}
|
|
17151
|
+
|
|
17152
|
+
// src/storage/session-store/search-events.ts
|
|
17153
|
+
import * as fsp14 from "node:fs/promises";
|
|
17154
|
+
async function searchSessionEvents(args) {
|
|
17155
|
+
const { file, secretScrubber, predicate, limit, signal } = args;
|
|
17156
|
+
const out = [];
|
|
17157
|
+
let stat15;
|
|
17158
|
+
try {
|
|
17159
|
+
stat15 = await fsp14.stat(file);
|
|
17160
|
+
} catch (err) {
|
|
17161
|
+
if (err.code === "ENOENT") return [];
|
|
17162
|
+
throw err;
|
|
17163
|
+
}
|
|
17164
|
+
if (stat15.size === 0) return [];
|
|
17165
|
+
let fh;
|
|
17166
|
+
try {
|
|
17167
|
+
fh = await fsp14.open(file, "r");
|
|
17168
|
+
const CHUNK = 64 * 1024;
|
|
17169
|
+
const buf = Buffer.alloc(CHUNK);
|
|
17170
|
+
let leftover = "";
|
|
17171
|
+
let eventIndex = 0;
|
|
17172
|
+
for (let position = 0; ; position += buf.byteLength) {
|
|
17173
|
+
if (signal?.aborted) {
|
|
17174
|
+
const reason = signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
17175
|
+
throw reason;
|
|
17176
|
+
}
|
|
17177
|
+
const { bytesRead } = await fh.read(buf, 0, CHUNK, position);
|
|
17178
|
+
if (bytesRead === 0) break;
|
|
17179
|
+
const text = leftover + buf.subarray(0, bytesRead).toString("utf8");
|
|
17180
|
+
const parts = text.split("\n");
|
|
17181
|
+
leftover = parts.pop() ?? "";
|
|
17182
|
+
for (const line of parts) {
|
|
17183
|
+
if (!line) continue;
|
|
17184
|
+
const ev = parseSessionEventLine(line, secretScrubber);
|
|
17185
|
+
if (!ev) continue;
|
|
17186
|
+
if (predicate(ev, eventIndex, ev.ts)) {
|
|
17187
|
+
out.push({ event: ev, eventIndex, ts: ev.ts });
|
|
17188
|
+
if (limit !== void 0 && out.length >= limit) return out;
|
|
17189
|
+
}
|
|
17190
|
+
eventIndex++;
|
|
17191
|
+
}
|
|
17192
|
+
}
|
|
17193
|
+
const trailing = parseSessionEventLine(leftover.trim() ? leftover : "", secretScrubber);
|
|
17194
|
+
if (trailing && predicate(trailing, eventIndex, trailing.ts)) {
|
|
17195
|
+
out.push({ event: trailing, eventIndex, ts: trailing.ts });
|
|
16806
17196
|
}
|
|
17197
|
+
return out;
|
|
17198
|
+
} finally {
|
|
17199
|
+
if (fh) await fh.close().catch(() => void 0);
|
|
16807
17200
|
}
|
|
16808
|
-
return { exactJournalActive };
|
|
16809
17201
|
}
|
|
16810
|
-
function
|
|
16811
|
-
return
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
17202
|
+
function parseSessionEventLine(line, secretScrubber) {
|
|
17203
|
+
if (!line) return null;
|
|
17204
|
+
try {
|
|
17205
|
+
const parsed = JSON.parse(line);
|
|
17206
|
+
if (parsed === null || typeof parsed !== "object" || typeof parsed.type !== "string" || typeof parsed.ts !== "string") {
|
|
17207
|
+
return null;
|
|
17208
|
+
}
|
|
17209
|
+
return scrubPersistedSessionEvent(parsed, secretScrubber);
|
|
17210
|
+
} catch {
|
|
17211
|
+
return null;
|
|
17212
|
+
}
|
|
16817
17213
|
}
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
|
|
16822
|
-
|
|
17214
|
+
|
|
17215
|
+
// src/storage/session-store/shard-manifest.ts
|
|
17216
|
+
import * as fsp15 from "node:fs/promises";
|
|
17217
|
+
|
|
17218
|
+
// src/storage/session-summary.ts
|
|
17219
|
+
function compareSessionSummaries(a, b) {
|
|
17220
|
+
const aActivity = a.lastActivityAt ?? a.endedAt ?? a.startedAt;
|
|
17221
|
+
const bActivity = b.lastActivityAt ?? b.endedAt ?? b.startedAt;
|
|
17222
|
+
if (aActivity < bActivity) return 1;
|
|
17223
|
+
if (aActivity > bActivity) return -1;
|
|
17224
|
+
if (a.startedAt < b.startedAt) return 1;
|
|
17225
|
+
if (a.startedAt > b.startedAt) return -1;
|
|
17226
|
+
return a.id.localeCompare(b.id);
|
|
17227
|
+
}
|
|
17228
|
+
function matchesSessionFilter(summary, criteria) {
|
|
17229
|
+
if (criteria.since && summary.startedAt < criteria.since) return false;
|
|
17230
|
+
if (criteria.until && summary.startedAt > criteria.until) return false;
|
|
17231
|
+
if (criteria.provider && summary.provider !== criteria.provider) return false;
|
|
17232
|
+
if (criteria.model && summary.model !== criteria.model) return false;
|
|
17233
|
+
if (criteria.minTokens !== void 0 && summary.tokenTotal < criteria.minTokens) return false;
|
|
17234
|
+
if (criteria.titleContains) {
|
|
17235
|
+
const needle = criteria.titleContains.toLocaleLowerCase();
|
|
17236
|
+
if (!summary.title.toLocaleLowerCase().includes(needle)) return false;
|
|
17237
|
+
}
|
|
17238
|
+
return true;
|
|
17239
|
+
}
|
|
17240
|
+
|
|
17241
|
+
// src/storage/session-store/shard-manifest.ts
|
|
17242
|
+
async function readOrBuildShardManifestEntry(opts) {
|
|
17243
|
+
try {
|
|
17244
|
+
const raw = await fsp15.readFile(opts.manifestPath, "utf8");
|
|
17245
|
+
const parsed = JSON.parse(raw);
|
|
17246
|
+
return {
|
|
17247
|
+
summaries: Array.isArray(parsed.summaries) ? parsed.summaries : [],
|
|
17248
|
+
ids: Array.isArray(parsed.ids) ? parsed.ids : []
|
|
17249
|
+
};
|
|
17250
|
+
} catch {
|
|
17251
|
+
}
|
|
17252
|
+
const refs = await opts.collectSessionFilesInShard(opts.shardKey);
|
|
17253
|
+
const candidates = await mapWithConcurrency(
|
|
17254
|
+
refs,
|
|
17255
|
+
opts.concurrency,
|
|
17256
|
+
async (ref) => {
|
|
17257
|
+
const manifest = await opts.readSummaryManifest(ref.id);
|
|
17258
|
+
if (manifest) return { summary: manifest, needsBackfill: false };
|
|
17259
|
+
const summary = await opts.summaryHeaderFor(ref);
|
|
17260
|
+
if (!summary) return null;
|
|
17261
|
+
const hydrated = await opts.summaryFor(summary.id).catch(() => summary);
|
|
17262
|
+
return { summary: hydrated, needsBackfill: false };
|
|
17263
|
+
}
|
|
17264
|
+
);
|
|
17265
|
+
const summaries = candidates.filter((candidate) => candidate !== null).map((candidate) => candidate.summary);
|
|
17266
|
+
summaries.sort(compareSessionSummaries);
|
|
17267
|
+
const entry = { summaries, ids: summaries.map((summary) => summary.id) };
|
|
17268
|
+
await atomicWrite(opts.manifestPath, JSON.stringify(entry), { mode: 384 }).catch(
|
|
17269
|
+
() => void 0
|
|
17270
|
+
);
|
|
17271
|
+
return entry;
|
|
16823
17272
|
}
|
|
16824
17273
|
|
|
16825
17274
|
// src/storage/session-store/summary-builder.ts
|
|
@@ -16960,177 +17409,55 @@ async function* iterateSessionEvents(file, secretScrubber) {
|
|
|
16960
17409
|
}
|
|
16961
17410
|
}
|
|
16962
17411
|
|
|
16963
|
-
// src/storage/session-store/summary-header.ts
|
|
16964
|
-
import * as
|
|
16965
|
-
async function readSessionSummaryHeader(ref, secretScrubber) {
|
|
16966
|
-
let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
16967
|
-
try {
|
|
16968
|
-
const stat15 = await
|
|
16969
|
-
if (!stat15.isFile()) return damagedSummary(ref.id, stat15.mtime.toISOString());
|
|
16970
|
-
mtime = stat15.mtime.toISOString();
|
|
16971
|
-
} catch {
|
|
16972
|
-
return null;
|
|
16973
|
-
}
|
|
16974
|
-
try {
|
|
16975
|
-
for await (const event of iterateSessionEvents(ref.filePath, secretScrubber)) {
|
|
16976
|
-
if (event.type === "session_start") {
|
|
16977
|
-
return {
|
|
16978
|
-
id: ref.id,
|
|
16979
|
-
title: "(empty session)",
|
|
16980
|
-
startedAt: event.ts,
|
|
16981
|
-
model: event.model ?? "unknown",
|
|
16982
|
-
provider: event.provider ?? "unknown",
|
|
16983
|
-
tokenTotal: 0
|
|
16984
|
-
};
|
|
16985
|
-
}
|
|
16986
|
-
}
|
|
16987
|
-
return emptySessionSummary(ref.id, (/* @__PURE__ */ new Date(0)).toISOString());
|
|
16988
|
-
} catch {
|
|
16989
|
-
return damagedSummary(ref.id, mtime);
|
|
16990
|
-
}
|
|
16991
|
-
}
|
|
16992
|
-
function emptySessionSummary(id, startedAt) {
|
|
16993
|
-
return {
|
|
16994
|
-
id,
|
|
16995
|
-
title: "(empty session)",
|
|
16996
|
-
startedAt,
|
|
16997
|
-
model: "unknown",
|
|
16998
|
-
provider: "unknown",
|
|
16999
|
-
tokenTotal: 0
|
|
17000
|
-
};
|
|
17001
|
-
}
|
|
17002
|
-
function damagedSummary(id, startedAt) {
|
|
17003
|
-
return {
|
|
17004
|
-
id,
|
|
17005
|
-
title: "(damaged)",
|
|
17006
|
-
startedAt,
|
|
17007
|
-
model: "unknown",
|
|
17008
|
-
provider: "unknown",
|
|
17009
|
-
tokenTotal: 0
|
|
17010
|
-
};
|
|
17011
|
-
}
|
|
17012
|
-
|
|
17013
|
-
// src/storage/session-store/shard-manifest.ts
|
|
17014
|
-
import * as fsp15 from "node:fs/promises";
|
|
17015
|
-
|
|
17016
|
-
// src/storage/session-summary.ts
|
|
17017
|
-
function compareSessionSummaries(a, b) {
|
|
17018
|
-
const aActivity = a.lastActivityAt ?? a.endedAt ?? a.startedAt;
|
|
17019
|
-
const bActivity = b.lastActivityAt ?? b.endedAt ?? b.startedAt;
|
|
17020
|
-
if (aActivity < bActivity) return 1;
|
|
17021
|
-
if (aActivity > bActivity) return -1;
|
|
17022
|
-
if (a.startedAt < b.startedAt) return 1;
|
|
17023
|
-
if (a.startedAt > b.startedAt) return -1;
|
|
17024
|
-
return a.id.localeCompare(b.id);
|
|
17025
|
-
}
|
|
17026
|
-
function matchesSessionFilter(summary, criteria) {
|
|
17027
|
-
if (criteria.since && summary.startedAt < criteria.since) return false;
|
|
17028
|
-
if (criteria.until && summary.startedAt > criteria.until) return false;
|
|
17029
|
-
if (criteria.provider && summary.provider !== criteria.provider) return false;
|
|
17030
|
-
if (criteria.model && summary.model !== criteria.model) return false;
|
|
17031
|
-
if (criteria.minTokens !== void 0 && summary.tokenTotal < criteria.minTokens) return false;
|
|
17032
|
-
if (criteria.titleContains) {
|
|
17033
|
-
const needle = criteria.titleContains.toLocaleLowerCase();
|
|
17034
|
-
if (!summary.title.toLocaleLowerCase().includes(needle)) return false;
|
|
17035
|
-
}
|
|
17036
|
-
return true;
|
|
17037
|
-
}
|
|
17038
|
-
|
|
17039
|
-
// src/storage/session-store/shard-manifest.ts
|
|
17040
|
-
async function readOrBuildShardManifestEntry(opts) {
|
|
17041
|
-
try {
|
|
17042
|
-
const raw = await fsp15.readFile(opts.manifestPath, "utf8");
|
|
17043
|
-
const parsed = JSON.parse(raw);
|
|
17044
|
-
return {
|
|
17045
|
-
summaries: Array.isArray(parsed.summaries) ? parsed.summaries : [],
|
|
17046
|
-
ids: Array.isArray(parsed.ids) ? parsed.ids : []
|
|
17047
|
-
};
|
|
17048
|
-
} catch {
|
|
17049
|
-
}
|
|
17050
|
-
const refs = await opts.collectSessionFilesInShard(opts.shardKey);
|
|
17051
|
-
const candidates = await mapWithConcurrency(
|
|
17052
|
-
refs,
|
|
17053
|
-
opts.concurrency,
|
|
17054
|
-
async (ref) => {
|
|
17055
|
-
const manifest = await opts.readSummaryManifest(ref.id);
|
|
17056
|
-
if (manifest) return { summary: manifest, needsBackfill: false };
|
|
17057
|
-
const summary = await opts.summaryHeaderFor(ref);
|
|
17058
|
-
if (!summary) return null;
|
|
17059
|
-
const hydrated = await opts.summaryFor(summary.id).catch(() => summary);
|
|
17060
|
-
return { summary: hydrated, needsBackfill: false };
|
|
17061
|
-
}
|
|
17062
|
-
);
|
|
17063
|
-
const summaries = candidates.filter((candidate) => candidate !== null).map((candidate) => candidate.summary);
|
|
17064
|
-
summaries.sort(compareSessionSummaries);
|
|
17065
|
-
const entry = { summaries, ids: summaries.map((summary) => summary.id) };
|
|
17066
|
-
await atomicWrite(opts.manifestPath, JSON.stringify(entry), { mode: 384 }).catch(
|
|
17067
|
-
() => void 0
|
|
17068
|
-
);
|
|
17069
|
-
return entry;
|
|
17070
|
-
}
|
|
17071
|
-
|
|
17072
|
-
// src/storage/session-store/search-events.ts
|
|
17073
|
-
import * as fsp16 from "node:fs/promises";
|
|
17074
|
-
async function searchSessionEvents(args) {
|
|
17075
|
-
const { file, secretScrubber, predicate, limit, signal } = args;
|
|
17076
|
-
const out = [];
|
|
17077
|
-
let stat15;
|
|
17078
|
-
try {
|
|
17079
|
-
stat15 = await fsp16.stat(file);
|
|
17080
|
-
} catch (err) {
|
|
17081
|
-
if (err.code === "ENOENT") return [];
|
|
17082
|
-
throw err;
|
|
17083
|
-
}
|
|
17084
|
-
if (stat15.size === 0) return [];
|
|
17085
|
-
let fh;
|
|
17086
|
-
try {
|
|
17087
|
-
fh = await fsp16.open(file, "r");
|
|
17088
|
-
const CHUNK = 64 * 1024;
|
|
17089
|
-
const buf = Buffer.alloc(CHUNK);
|
|
17090
|
-
let leftover = "";
|
|
17091
|
-
let eventIndex = 0;
|
|
17092
|
-
for (let position = 0; ; position += buf.byteLength) {
|
|
17093
|
-
if (signal?.aborted) {
|
|
17094
|
-
const reason = signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
17095
|
-
throw reason;
|
|
17096
|
-
}
|
|
17097
|
-
const { bytesRead } = await fh.read(buf, 0, CHUNK, position);
|
|
17098
|
-
if (bytesRead === 0) break;
|
|
17099
|
-
const text = leftover + buf.subarray(0, bytesRead).toString("utf8");
|
|
17100
|
-
const parts = text.split("\n");
|
|
17101
|
-
leftover = parts.pop() ?? "";
|
|
17102
|
-
for (const line of parts) {
|
|
17103
|
-
if (!line) continue;
|
|
17104
|
-
const ev = parseSessionEventLine(line, secretScrubber);
|
|
17105
|
-
if (!ev) continue;
|
|
17106
|
-
if (predicate(ev, eventIndex, ev.ts)) {
|
|
17107
|
-
out.push({ event: ev, eventIndex, ts: ev.ts });
|
|
17108
|
-
if (limit !== void 0 && out.length >= limit) return out;
|
|
17109
|
-
}
|
|
17110
|
-
eventIndex++;
|
|
17111
|
-
}
|
|
17112
|
-
}
|
|
17113
|
-
const trailing = parseSessionEventLine(leftover.trim() ? leftover : "", secretScrubber);
|
|
17114
|
-
if (trailing && predicate(trailing, eventIndex, trailing.ts)) {
|
|
17115
|
-
out.push({ event: trailing, eventIndex, ts: trailing.ts });
|
|
17116
|
-
}
|
|
17117
|
-
return out;
|
|
17118
|
-
} finally {
|
|
17119
|
-
if (fh) await fh.close().catch(() => void 0);
|
|
17412
|
+
// src/storage/session-store/summary-header.ts
|
|
17413
|
+
import * as fsp16 from "node:fs/promises";
|
|
17414
|
+
async function readSessionSummaryHeader(ref, secretScrubber) {
|
|
17415
|
+
let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
17416
|
+
try {
|
|
17417
|
+
const stat15 = await fsp16.stat(ref.filePath);
|
|
17418
|
+
if (!stat15.isFile()) return damagedSummary(ref.id, stat15.mtime.toISOString());
|
|
17419
|
+
mtime = stat15.mtime.toISOString();
|
|
17420
|
+
} catch {
|
|
17421
|
+
return null;
|
|
17120
17422
|
}
|
|
17121
|
-
}
|
|
17122
|
-
function parseSessionEventLine(line, secretScrubber) {
|
|
17123
|
-
if (!line) return null;
|
|
17124
17423
|
try {
|
|
17125
|
-
const
|
|
17126
|
-
|
|
17127
|
-
|
|
17424
|
+
for await (const event of iterateSessionEvents(ref.filePath, secretScrubber)) {
|
|
17425
|
+
if (event.type === "session_start") {
|
|
17426
|
+
return {
|
|
17427
|
+
id: ref.id,
|
|
17428
|
+
title: "(empty session)",
|
|
17429
|
+
startedAt: event.ts,
|
|
17430
|
+
model: event.model ?? "unknown",
|
|
17431
|
+
provider: event.provider ?? "unknown",
|
|
17432
|
+
tokenTotal: 0
|
|
17433
|
+
};
|
|
17434
|
+
}
|
|
17128
17435
|
}
|
|
17129
|
-
return
|
|
17436
|
+
return emptySessionSummary(ref.id, (/* @__PURE__ */ new Date(0)).toISOString());
|
|
17130
17437
|
} catch {
|
|
17131
|
-
return
|
|
17438
|
+
return damagedSummary(ref.id, mtime);
|
|
17132
17439
|
}
|
|
17133
17440
|
}
|
|
17441
|
+
function emptySessionSummary(id, startedAt) {
|
|
17442
|
+
return {
|
|
17443
|
+
id,
|
|
17444
|
+
title: "(empty session)",
|
|
17445
|
+
startedAt,
|
|
17446
|
+
model: "unknown",
|
|
17447
|
+
provider: "unknown",
|
|
17448
|
+
tokenTotal: 0
|
|
17449
|
+
};
|
|
17450
|
+
}
|
|
17451
|
+
function damagedSummary(id, startedAt) {
|
|
17452
|
+
return {
|
|
17453
|
+
id,
|
|
17454
|
+
title: "(damaged)",
|
|
17455
|
+
startedAt,
|
|
17456
|
+
model: "unknown",
|
|
17457
|
+
provider: "unknown",
|
|
17458
|
+
tokenTotal: 0
|
|
17459
|
+
};
|
|
17460
|
+
}
|
|
17134
17461
|
|
|
17135
17462
|
// src/storage/session-store.ts
|
|
17136
17463
|
var DefaultSessionStore = class _DefaultSessionStore {
|
|
@@ -17272,13 +17599,17 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17272
17599
|
async fork(id, opts = {}) {
|
|
17273
17600
|
return forkSession(this, id, opts);
|
|
17274
17601
|
}
|
|
17602
|
+
/**
|
|
17603
|
+
* Capture the deterministic post-tool workspace identity through the store-owned CAS.
|
|
17604
|
+
*/
|
|
17605
|
+
async captureWorkspaceCheckpoint(sessionId, promptIndex) {
|
|
17606
|
+
return captureCheckpoint(this.checkpointCas, sessionId, promptIndex);
|
|
17607
|
+
}
|
|
17608
|
+
/**
|
|
17609
|
+
* Materialize a store-owned checkpoint into an already isolated workspace.
|
|
17610
|
+
*/
|
|
17275
17611
|
async materializeWorkspaceCheckpoint(checkpoint, targetRoot) {
|
|
17276
|
-
|
|
17277
|
-
throw new Error(
|
|
17278
|
-
"Workspace checkpoint materialization requires a projectRoot-aware session store"
|
|
17279
|
-
);
|
|
17280
|
-
}
|
|
17281
|
-
return this.checkpointCas.materialize(checkpoint, targetRoot);
|
|
17612
|
+
return materializeCheckpoint(this.checkpointCas, checkpoint, targetRoot);
|
|
17282
17613
|
}
|
|
17283
17614
|
async resolveId(query) {
|
|
17284
17615
|
const normalized = query.trim();
|
|
@@ -17363,9 +17694,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17363
17694
|
handle = await fsp17.open(file, "a", 384);
|
|
17364
17695
|
} catch (err) {
|
|
17365
17696
|
emitSessionStoreError(this.events, canonicalId, file, "resume", toErrorMessage(err), false);
|
|
17366
|
-
throw new Error(
|
|
17367
|
-
|
|
17368
|
-
|
|
17697
|
+
throw new Error(
|
|
17698
|
+
`Failed to open session "${canonicalId}" for append: ${toErrorMessage(err)}`,
|
|
17699
|
+
{
|
|
17700
|
+
cause: err
|
|
17701
|
+
}
|
|
17702
|
+
);
|
|
17369
17703
|
}
|
|
17370
17704
|
try {
|
|
17371
17705
|
const writer = new FileSessionWriter(
|
|
@@ -17562,7 +17896,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17562
17896
|
await withFileLock(this.indexFile, async () => {
|
|
17563
17897
|
await this.invalidateShardManifestBySessionId(summary.id);
|
|
17564
17898
|
const line = JSON.stringify(summary) + "\n";
|
|
17565
|
-
await fsp17.appendFile(this.indexFile, line, "utf8");
|
|
17899
|
+
await fsp17.appendFile(this.indexFile, line, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
17566
17900
|
this._indexCache = null;
|
|
17567
17901
|
this.indexAppendCount++;
|
|
17568
17902
|
if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
|
|
@@ -17585,7 +17919,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17585
17919
|
await ensureDir(this.dir);
|
|
17586
17920
|
await withFileLock(this.indexFile, async () => {
|
|
17587
17921
|
const line = JSON.stringify({ action: "delete", id }) + "\n";
|
|
17588
|
-
await fsp17.appendFile(this.indexFile, line, "utf8");
|
|
17922
|
+
await fsp17.appendFile(this.indexFile, line, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
17589
17923
|
this._indexCache = null;
|
|
17590
17924
|
await this.invalidateShardManifestBySessionId(id);
|
|
17591
17925
|
this.indexAppendCount++;
|
|
@@ -17793,12 +18127,28 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17793
18127
|
});
|
|
17794
18128
|
outcome = "failure";
|
|
17795
18129
|
errorMsg = "summary fallback \xE2\u20AC\u201D manifest rebuilt";
|
|
17796
|
-
emitSessionStoreRead(
|
|
18130
|
+
emitSessionStoreRead(
|
|
18131
|
+
this.events,
|
|
18132
|
+
id,
|
|
18133
|
+
manifest,
|
|
18134
|
+
"summary",
|
|
18135
|
+
outcome,
|
|
18136
|
+
Date.now() - t0,
|
|
18137
|
+
errorMsg
|
|
18138
|
+
);
|
|
17797
18139
|
return summary;
|
|
17798
18140
|
} catch (err) {
|
|
17799
18141
|
outcome = "failure";
|
|
17800
18142
|
errorMsg = toErrorMessage(err);
|
|
17801
|
-
emitSessionStoreRead(
|
|
18143
|
+
emitSessionStoreRead(
|
|
18144
|
+
this.events,
|
|
18145
|
+
id,
|
|
18146
|
+
manifest,
|
|
18147
|
+
"summary",
|
|
18148
|
+
outcome,
|
|
18149
|
+
Date.now() - t0,
|
|
18150
|
+
errorMsg
|
|
18151
|
+
);
|
|
17802
18152
|
return {
|
|
17803
18153
|
id,
|
|
17804
18154
|
title: "(damaged)",
|
|
@@ -17813,14 +18163,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17813
18163
|
const manifest = this.sessionPath(id, ".summary.json");
|
|
17814
18164
|
try {
|
|
17815
18165
|
const raw = await fsp17.readFile(manifest, "utf8");
|
|
17816
|
-
emitSessionStoreRead(
|
|
17817
|
-
this.events,
|
|
17818
|
-
id,
|
|
17819
|
-
manifest,
|
|
17820
|
-
"summary",
|
|
17821
|
-
"success",
|
|
17822
|
-
Date.now() - startTime
|
|
17823
|
-
);
|
|
18166
|
+
emitSessionStoreRead(this.events, id, manifest, "summary", "success", Date.now() - startTime);
|
|
17824
18167
|
return JSON.parse(raw);
|
|
17825
18168
|
} catch {
|
|
17826
18169
|
return null;
|
|
@@ -18502,6 +18845,10 @@ var Director = class _Director {
|
|
|
18502
18845
|
this.taskWorktrees.delete(tid);
|
|
18503
18846
|
}
|
|
18504
18847
|
}
|
|
18848
|
+
this.tasks.removeTasksOwnedBy(subagentId);
|
|
18849
|
+
for (const [taskId, update] of this.taskWorktrees) {
|
|
18850
|
+
if (update.subagentId === subagentId) this.taskWorktrees.delete(taskId);
|
|
18851
|
+
}
|
|
18505
18852
|
this.budgetPolicy.removeSubagent(subagentId);
|
|
18506
18853
|
this.manifestEntries.delete(subagentId);
|
|
18507
18854
|
const meta = this.subagentMeta.get(subagentId);
|
|
@@ -20050,7 +20397,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
20050
20397
|
root.pendingFileWrites += 1;
|
|
20051
20398
|
root.pendingFileBytes += bytes;
|
|
20052
20399
|
this.enqueueRotate(this.file);
|
|
20053
|
-
this._tail = this._tail.then(() => fsp20.appendFile(this.file, line)).catch(() => void 0).finally(() => {
|
|
20400
|
+
this._tail = this._tail.then(() => fsp20.appendFile(this.file, line, { mode: 384 })).catch(() => void 0).finally(() => {
|
|
20054
20401
|
root.pendingFileWrites = Math.max(0, root.pendingFileWrites - 1);
|
|
20055
20402
|
root.pendingFileBytes = Math.max(0, root.pendingFileBytes - bytes);
|
|
20056
20403
|
});
|
|
@@ -20725,14 +21072,6 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
20725
21072
|
var KIT_FILE = "KIT.md";
|
|
20726
21073
|
var TOKENS_FILE = "tokens.json";
|
|
20727
21074
|
var FOUNDATIONS_ID = "_foundations";
|
|
20728
|
-
function stripFrontmatter(raw) {
|
|
20729
|
-
if (!raw.startsWith("---")) return raw;
|
|
20730
|
-
const end = raw.indexOf("\n---", 4);
|
|
20731
|
-
if (end === -1) return raw;
|
|
20732
|
-
let body = raw.slice(end + 4);
|
|
20733
|
-
if (body.startsWith("\n")) body = body.slice(1);
|
|
20734
|
-
return body;
|
|
20735
|
-
}
|
|
20736
21075
|
function parseList(value) {
|
|
20737
21076
|
const trimmed = value.trim();
|
|
20738
21077
|
const inner = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
|
|
@@ -21281,12 +21620,12 @@ function verifyFiles(tokens, files) {
|
|
|
21281
21620
|
const violations = [];
|
|
21282
21621
|
let onPalette = 0;
|
|
21283
21622
|
let offPalette = 0;
|
|
21284
|
-
for (const { path:
|
|
21623
|
+
for (const { path: path40, text } of files) {
|
|
21285
21624
|
const lines = text.split("\n");
|
|
21286
21625
|
lines.forEach((lineText, i) => {
|
|
21287
21626
|
const lineNo = i + 1;
|
|
21288
21627
|
const flag = (snippet, reason, axis = "color") => {
|
|
21289
|
-
violations.push({ file:
|
|
21628
|
+
violations.push({ file: path40, line: lineNo, snippet: snippet.slice(0, 80), reason, axis });
|
|
21290
21629
|
};
|
|
21291
21630
|
for (const re of [HEX_RE, FUNC_COLOR_RE]) {
|
|
21292
21631
|
re.lastIndex = 0;
|
|
@@ -21969,20 +22308,23 @@ function buildRecoveryStrategies(opts) {
|
|
|
21969
22308
|
const delayMs = err.body?.retryAfterMs ?? 5e3;
|
|
21970
22309
|
const delay = Math.min(6e4, Math.max(1e3, delayMs));
|
|
21971
22310
|
if (ctx?.signal) {
|
|
21972
|
-
await new Promise((
|
|
22311
|
+
await new Promise((resolve17) => {
|
|
21973
22312
|
if (ctx.signal.aborted) {
|
|
21974
|
-
|
|
22313
|
+
resolve17();
|
|
21975
22314
|
return;
|
|
21976
22315
|
}
|
|
21977
|
-
|
|
21978
|
-
|
|
21979
|
-
|
|
21980
|
-
|
|
21981
|
-
|
|
21982
|
-
|
|
21983
|
-
|
|
21984
|
-
|
|
21985
|
-
|
|
22316
|
+
let settled = false;
|
|
22317
|
+
let timer;
|
|
22318
|
+
const onAbort = () => finish();
|
|
22319
|
+
const finish = () => {
|
|
22320
|
+
if (settled) return;
|
|
22321
|
+
settled = true;
|
|
22322
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
22323
|
+
ctx.signal.removeEventListener("abort", onAbort);
|
|
22324
|
+
resolve17();
|
|
22325
|
+
};
|
|
22326
|
+
timer = setTimeout(finish, delay);
|
|
22327
|
+
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
21986
22328
|
});
|
|
21987
22329
|
} else {
|
|
21988
22330
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -24172,8 +24514,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
24172
24514
|
});
|
|
24173
24515
|
await Promise.race([
|
|
24174
24516
|
drainPromise,
|
|
24175
|
-
new Promise((
|
|
24176
|
-
drainTimer = setTimeout(
|
|
24517
|
+
new Promise((resolve17) => {
|
|
24518
|
+
drainTimer = setTimeout(resolve17, STREAM_DRAIN_TIMEOUT_MS);
|
|
24177
24519
|
})
|
|
24178
24520
|
]);
|
|
24179
24521
|
} finally {
|
|
@@ -24470,7 +24812,7 @@ async function runProviderWithRetry(opts) {
|
|
|
24470
24812
|
status: isProviderErr ? err.status : 0,
|
|
24471
24813
|
description
|
|
24472
24814
|
});
|
|
24473
|
-
await new Promise((
|
|
24815
|
+
await new Promise((resolve17, reject) => {
|
|
24474
24816
|
let settled = false;
|
|
24475
24817
|
const cleanup = () => {
|
|
24476
24818
|
clearTimeout(t);
|
|
@@ -24486,7 +24828,7 @@ async function runProviderWithRetry(opts) {
|
|
|
24486
24828
|
if (settled) return;
|
|
24487
24829
|
settled = true;
|
|
24488
24830
|
cleanup();
|
|
24489
|
-
|
|
24831
|
+
resolve17();
|
|
24490
24832
|
}, delay);
|
|
24491
24833
|
if (signal.aborted) {
|
|
24492
24834
|
onAbort();
|
|
@@ -25148,93 +25490,7 @@ function resolveForeignToolIdsWithWarnings(opt) {
|
|
|
25148
25490
|
return { ids: FOREIGN_SKILL_TOOLS.map((t) => t.id), unknownIds: [] };
|
|
25149
25491
|
}
|
|
25150
25492
|
|
|
25151
|
-
// src/skills/frontmatter.ts
|
|
25152
|
-
var SCALAR_KEYS = /* @__PURE__ */ new Set(["name", "description", "version", "license", "compatibility"]);
|
|
25153
|
-
function parseSkillFrontmatter(raw) {
|
|
25154
|
-
const text = normalizeLineEndings(raw);
|
|
25155
|
-
if (!text.startsWith("---")) return {};
|
|
25156
|
-
const end = text.indexOf("\n---", 4);
|
|
25157
|
-
if (end === -1) return {};
|
|
25158
|
-
return parseFrontmatterBlock(text.slice(4, end));
|
|
25159
|
-
}
|
|
25160
|
-
function normalizeLineEndings(s) {
|
|
25161
|
-
return s.replace(/\r\n?/g, "\n");
|
|
25162
|
-
}
|
|
25163
|
-
function parseFrontmatterBlock(block) {
|
|
25164
|
-
const out = {};
|
|
25165
|
-
const lines = block.split("\n");
|
|
25166
|
-
let i = 0;
|
|
25167
|
-
while (i < lines.length) {
|
|
25168
|
-
const line = lines[i] ?? "";
|
|
25169
|
-
const m = /^([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)$/.exec(line);
|
|
25170
|
-
if (!m) {
|
|
25171
|
-
i++;
|
|
25172
|
-
continue;
|
|
25173
|
-
}
|
|
25174
|
-
const key = m[1] ?? "";
|
|
25175
|
-
const rest = (m[2] ?? "").trim();
|
|
25176
|
-
if (key === "metadata") {
|
|
25177
|
-
const map = {};
|
|
25178
|
-
i++;
|
|
25179
|
-
while (i < lines.length) {
|
|
25180
|
-
const sub = lines[i] ?? "";
|
|
25181
|
-
const sm = /^\s+([a-zA-Z0-9_.-]+):\s*(.*)$/.exec(sub);
|
|
25182
|
-
if (!sm) break;
|
|
25183
|
-
map[sm[1] ?? ""] = unquote((sm[2] ?? "").trim());
|
|
25184
|
-
i++;
|
|
25185
|
-
}
|
|
25186
|
-
if (Object.keys(map).length > 0) out.metadata = map;
|
|
25187
|
-
continue;
|
|
25188
|
-
}
|
|
25189
|
-
if (rest === "|" || rest === ">") {
|
|
25190
|
-
const collected = [];
|
|
25191
|
-
i++;
|
|
25192
|
-
while (i < lines.length) {
|
|
25193
|
-
const sub = lines[i] ?? "";
|
|
25194
|
-
if (sub === "" || sub.startsWith(" ") || sub.startsWith(" ")) {
|
|
25195
|
-
collected.push(sub.replace(/^\s+/, ""));
|
|
25196
|
-
i++;
|
|
25197
|
-
} else break;
|
|
25198
|
-
}
|
|
25199
|
-
out[normalizeKey(key)] = collected.join("\n").trim();
|
|
25200
|
-
continue;
|
|
25201
|
-
}
|
|
25202
|
-
if (key === "allowed-tools" || key === "allowedTools") {
|
|
25203
|
-
out.allowedTools = rest.split(/[\s,]+/).filter(Boolean);
|
|
25204
|
-
i++;
|
|
25205
|
-
continue;
|
|
25206
|
-
}
|
|
25207
|
-
if (SCALAR_KEYS.has(key)) {
|
|
25208
|
-
out[key] = unquote(rest);
|
|
25209
|
-
i++;
|
|
25210
|
-
continue;
|
|
25211
|
-
}
|
|
25212
|
-
i++;
|
|
25213
|
-
}
|
|
25214
|
-
return out;
|
|
25215
|
-
}
|
|
25216
|
-
function normalizeKey(key) {
|
|
25217
|
-
return key === "allowed-tools" ? "allowedTools" : key;
|
|
25218
|
-
}
|
|
25219
|
-
function unquote(s) {
|
|
25220
|
-
if (s.length >= 2 && (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'"))) {
|
|
25221
|
-
return s.slice(1, -1);
|
|
25222
|
-
}
|
|
25223
|
-
return s;
|
|
25224
|
-
}
|
|
25225
|
-
function isValidSkillNameFormat(name) {
|
|
25226
|
-
return name.length >= 1 && name.length <= 64 && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
|
25227
|
-
}
|
|
25228
|
-
|
|
25229
25493
|
// src/execution/skill-loader.ts
|
|
25230
|
-
function stripFrontmatter2(raw) {
|
|
25231
|
-
if (!raw.startsWith("---")) return raw;
|
|
25232
|
-
const end = raw.indexOf("\n---", 4);
|
|
25233
|
-
if (end === -1) return raw;
|
|
25234
|
-
let body = raw.slice(end + 4);
|
|
25235
|
-
if (body.startsWith("\n")) body = body.slice(1);
|
|
25236
|
-
return body;
|
|
25237
|
-
}
|
|
25238
25494
|
function compactSkillBody(body) {
|
|
25239
25495
|
const sections = [];
|
|
25240
25496
|
const overviewMatch = body.match(/##\s*Overview\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
|
|
@@ -25273,6 +25529,9 @@ var DefaultSkillLoader = class {
|
|
|
25273
25529
|
cache;
|
|
25274
25530
|
entriesCache;
|
|
25275
25531
|
bodyCache = /* @__PURE__ */ new Map();
|
|
25532
|
+
/** Load-time diagnostics from the most recent full `list()` scan. */
|
|
25533
|
+
skipped = [];
|
|
25534
|
+
shadowed = [];
|
|
25276
25535
|
constructor(opts) {
|
|
25277
25536
|
const readClaude = opts.readClaudeSkills !== false;
|
|
25278
25537
|
const foreignIds = resolveForeignToolIds(opts.foreignSources);
|
|
@@ -25302,6 +25561,9 @@ var DefaultSkillLoader = class {
|
|
|
25302
25561
|
if (this.cache) return this.cache;
|
|
25303
25562
|
const found = [];
|
|
25304
25563
|
const seen = /* @__PURE__ */ new Set();
|
|
25564
|
+
const claimedBy = /* @__PURE__ */ new Map();
|
|
25565
|
+
this.skipped = [];
|
|
25566
|
+
this.shadowed = [];
|
|
25305
25567
|
for (const { dir, source, originTool } of this.dirs) {
|
|
25306
25568
|
try {
|
|
25307
25569
|
const entries = (await fs5.readdir(dir, { withFileTypes: true })).sort(
|
|
@@ -25310,27 +25572,61 @@ var DefaultSkillLoader = class {
|
|
|
25310
25572
|
for (const e of entries) {
|
|
25311
25573
|
if (!await entryIsDirectory(dir, e)) continue;
|
|
25312
25574
|
const skillFile = path25.join(dir, e.name, "SKILL.md");
|
|
25575
|
+
let raw;
|
|
25313
25576
|
try {
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
|
|
25320
|
-
|
|
25577
|
+
raw = await fs5.readFile(skillFile, "utf8");
|
|
25578
|
+
} catch {
|
|
25579
|
+
continue;
|
|
25580
|
+
}
|
|
25581
|
+
const fm = parseSkillFrontmatter(raw);
|
|
25582
|
+
if (!fm.name) {
|
|
25583
|
+
this.skipped.push({ dir, entry: e.name, reason: "missing-name" });
|
|
25584
|
+
continue;
|
|
25585
|
+
}
|
|
25586
|
+
if (!fm.description) {
|
|
25587
|
+
this.skipped.push({
|
|
25588
|
+
dir,
|
|
25589
|
+
entry: e.name,
|
|
25590
|
+
reason: "missing-description",
|
|
25591
|
+
name: fm.name
|
|
25592
|
+
});
|
|
25593
|
+
continue;
|
|
25594
|
+
}
|
|
25595
|
+
if (!isValidSkillNameFormat(fm.name)) {
|
|
25596
|
+
this.skipped.push({
|
|
25597
|
+
dir,
|
|
25598
|
+
entry: e.name,
|
|
25599
|
+
reason: "invalid-name-format",
|
|
25600
|
+
name: fm.name
|
|
25601
|
+
});
|
|
25602
|
+
continue;
|
|
25603
|
+
}
|
|
25604
|
+
if (seen.has(fm.name)) {
|
|
25605
|
+
const claimant = claimedBy.get(fm.name);
|
|
25606
|
+
this.shadowed.push({
|
|
25321
25607
|
name: fm.name,
|
|
25322
|
-
description: fm.description,
|
|
25323
|
-
version: fm.version,
|
|
25324
|
-
license: fm.license,
|
|
25325
|
-
compatibility: fm.compatibility,
|
|
25326
|
-
metadata: fm.metadata,
|
|
25327
|
-
allowedTools: fm.allowedTools,
|
|
25328
|
-
path: skillFile,
|
|
25329
25608
|
source,
|
|
25330
|
-
|
|
25609
|
+
path: skillFile,
|
|
25610
|
+
shadowedBy: claimant?.source ?? "unknown",
|
|
25611
|
+
shadowedByPath: claimant?.path
|
|
25331
25612
|
});
|
|
25332
|
-
|
|
25613
|
+
continue;
|
|
25333
25614
|
}
|
|
25615
|
+
seen.add(fm.name);
|
|
25616
|
+
claimedBy.set(fm.name, { source, path: skillFile });
|
|
25617
|
+
found.push({
|
|
25618
|
+
name: fm.name,
|
|
25619
|
+
description: fm.description,
|
|
25620
|
+
trigger: fm.trigger,
|
|
25621
|
+
version: fm.version,
|
|
25622
|
+
license: fm.license,
|
|
25623
|
+
compatibility: fm.compatibility,
|
|
25624
|
+
metadata: fm.metadata,
|
|
25625
|
+
allowedTools: fm.allowedTools,
|
|
25626
|
+
path: skillFile,
|
|
25627
|
+
source,
|
|
25628
|
+
originTool
|
|
25629
|
+
});
|
|
25334
25630
|
}
|
|
25335
25631
|
} catch {
|
|
25336
25632
|
}
|
|
@@ -25359,11 +25655,12 @@ var DefaultSkillLoader = class {
|
|
|
25359
25655
|
const skills = await this.list();
|
|
25360
25656
|
const entries = [];
|
|
25361
25657
|
for (const s of skills) {
|
|
25362
|
-
const
|
|
25658
|
+
const parsed = parseDescriptionFromText(s.description ?? "");
|
|
25659
|
+
const trigger = s.trigger?.trim() || parsed.trigger;
|
|
25363
25660
|
entries.push({
|
|
25364
25661
|
name: s.name,
|
|
25365
25662
|
trigger,
|
|
25366
|
-
scope,
|
|
25663
|
+
scope: parsed.scope,
|
|
25367
25664
|
source: s.source,
|
|
25368
25665
|
originTool: s.originTool,
|
|
25369
25666
|
path: s.path
|
|
@@ -25376,6 +25673,16 @@ var DefaultSkillLoader = class {
|
|
|
25376
25673
|
this.cache = void 0;
|
|
25377
25674
|
this.entriesCache = void 0;
|
|
25378
25675
|
this.bodyCache.clear();
|
|
25676
|
+
this.skipped = [];
|
|
25677
|
+
this.shadowed = [];
|
|
25678
|
+
}
|
|
25679
|
+
/**
|
|
25680
|
+
* Load-time diagnostics from the most recent full `list()` scan: skills
|
|
25681
|
+
* skipped as malformed and skills shadowed by a higher-priority layer.
|
|
25682
|
+
* Returns copies so callers cannot mutate internal state.
|
|
25683
|
+
*/
|
|
25684
|
+
diagnostics() {
|
|
25685
|
+
return { skipped: [...this.skipped], shadowed: [...this.shadowed] };
|
|
25379
25686
|
}
|
|
25380
25687
|
async readBody(name) {
|
|
25381
25688
|
const key = name.toLowerCase();
|
|
@@ -25399,7 +25706,7 @@ var DefaultSkillLoader = class {
|
|
|
25399
25706
|
result = await fs5.readFile(savePath, "utf8");
|
|
25400
25707
|
} catch {
|
|
25401
25708
|
const full = await fs5.readFile(m.path, "utf8");
|
|
25402
|
-
const body =
|
|
25709
|
+
const body = stripFrontmatter(full);
|
|
25403
25710
|
const compact = compactSkillBody(body);
|
|
25404
25711
|
if (compact) {
|
|
25405
25712
|
result = `## Overview
|
|
@@ -25935,6 +26242,9 @@ function readPolicy(ctx) {
|
|
|
25935
26242
|
|
|
25936
26243
|
// src/execution/tool-executor.ts
|
|
25937
26244
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
26245
|
+
import * as fs9 from "node:fs/promises";
|
|
26246
|
+
import * as path30 from "node:path";
|
|
26247
|
+
import { isDeepStrictEqual } from "node:util";
|
|
25938
26248
|
|
|
25939
26249
|
// src/observability/process-telemetry.ts
|
|
25940
26250
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
@@ -25943,11 +26253,6 @@ function runWithProcessTelemetry(context, run) {
|
|
|
25943
26253
|
return storage2.run(context, run);
|
|
25944
26254
|
}
|
|
25945
26255
|
|
|
25946
|
-
// src/execution/tool-executor.ts
|
|
25947
|
-
import { isDeepStrictEqual } from "node:util";
|
|
25948
|
-
import * as fs9 from "node:fs/promises";
|
|
25949
|
-
import * as path30 from "node:path";
|
|
25950
|
-
|
|
25951
26256
|
// src/security/kanban-boundary.ts
|
|
25952
26257
|
import { realpath as realpath3 } from "node:fs/promises";
|
|
25953
26258
|
import * as path28 from "node:path";
|
|
@@ -26101,8 +26406,8 @@ function resolveInputPath(value, ctx) {
|
|
|
26101
26406
|
return relativeToProject(absolute, ctx.projectRoot);
|
|
26102
26407
|
}
|
|
26103
26408
|
function relativeToProject(absolute, projectRoot) {
|
|
26104
|
-
const
|
|
26105
|
-
return
|
|
26409
|
+
const relative8 = path28.relative(projectRoot, absolute).replace(/\\/g, "/");
|
|
26410
|
+
return relative8.startsWith("../") || path28.isAbsolute(relative8) ? absolute : relative8 || ".";
|
|
26106
26411
|
}
|
|
26107
26412
|
function extractPatchTargets(patchText, strip) {
|
|
26108
26413
|
const targets = [];
|
|
@@ -26309,51 +26614,10 @@ function sliceUtf8Suffix(text, maxBytes) {
|
|
|
26309
26614
|
if (Buffer.byteLength(text.slice(text.length - chars), "utf8") <= maxBytes) low = chars;
|
|
26310
26615
|
else high = chars - 1;
|
|
26311
26616
|
}
|
|
26312
|
-
let start = text.length - low;
|
|
26313
|
-
const first = text.charCodeAt(start);
|
|
26314
|
-
if (first >= 56320 && first <= 57343) start++;
|
|
26315
|
-
return text.slice(start);
|
|
26316
|
-
}
|
|
26317
|
-
|
|
26318
|
-
// src/execution/tool-executor-results.ts
|
|
26319
|
-
function unknownToolResult(use, listFns) {
|
|
26320
|
-
return {
|
|
26321
|
-
type: "tool_result",
|
|
26322
|
-
tool_use_id: use.id,
|
|
26323
|
-
content: `Tool "${use.name}" is not registered. Available tools: ${listFns().join(", ")}`,
|
|
26324
|
-
is_error: true
|
|
26325
|
-
};
|
|
26326
|
-
}
|
|
26327
|
-
function malformedInputResult(use, raw) {
|
|
26328
|
-
let content = `Tool "${use.name}" received arguments that were not a valid JSON object, so they could not be parsed. Re-issue the call with the arguments encoded as a single well-formed JSON object matching the tool's input schema.`;
|
|
26329
|
-
if (raw) {
|
|
26330
|
-
const max = 800;
|
|
26331
|
-
const excerpt2 = raw.length > max ? `${raw.slice(0, max)}\u2026 (truncated, ${raw.length} chars total)` : raw;
|
|
26332
|
-
content += ` Common cause: a string field (e.g. code in old_string/new_string) contains literal newlines, quotes, or backslashes that must be JSON-escaped, or the payload was cut off mid-stream. The raw arguments received were:
|
|
26333
|
-
${excerpt2}`;
|
|
26334
|
-
}
|
|
26335
|
-
return {
|
|
26336
|
-
type: "tool_result",
|
|
26337
|
-
tool_use_id: use.id,
|
|
26338
|
-
content,
|
|
26339
|
-
is_error: true
|
|
26340
|
-
};
|
|
26341
|
-
}
|
|
26342
|
-
function deniedResult(use, reason) {
|
|
26343
|
-
return {
|
|
26344
|
-
type: "tool_result",
|
|
26345
|
-
tool_use_id: use.id,
|
|
26346
|
-
content: `Tool "${use.name}" denied: ${reason ?? "policy"}`,
|
|
26347
|
-
is_error: true
|
|
26348
|
-
};
|
|
26349
|
-
}
|
|
26350
|
-
function blockedByHookResult(use, reason) {
|
|
26351
|
-
return {
|
|
26352
|
-
type: "tool_result",
|
|
26353
|
-
tool_use_id: use.id,
|
|
26354
|
-
content: `Tool "${use.name}" was blocked by a PreToolUse hook: ${reason ?? "no reason given"}`,
|
|
26355
|
-
is_error: true
|
|
26356
|
-
};
|
|
26617
|
+
let start = text.length - low;
|
|
26618
|
+
const first = text.charCodeAt(start);
|
|
26619
|
+
if (first >= 56320 && first <= 57343) start++;
|
|
26620
|
+
return text.slice(start);
|
|
26357
26621
|
}
|
|
26358
26622
|
|
|
26359
26623
|
// src/execution/tool-executor-logging.ts
|
|
@@ -26420,6 +26684,82 @@ function logToolFailure(opts, ctx, use, toolName, durationMs, err) {
|
|
|
26420
26684
|
});
|
|
26421
26685
|
}
|
|
26422
26686
|
|
|
26687
|
+
// src/execution/tool-executor-results.ts
|
|
26688
|
+
function unknownToolResult(use, listFns) {
|
|
26689
|
+
return {
|
|
26690
|
+
type: "tool_result",
|
|
26691
|
+
tool_use_id: use.id,
|
|
26692
|
+
content: `Tool "${use.name}" is not registered. Available tools: ${listFns().join(", ")}`,
|
|
26693
|
+
is_error: true
|
|
26694
|
+
};
|
|
26695
|
+
}
|
|
26696
|
+
function malformedInputResult(use, raw) {
|
|
26697
|
+
let content = `Tool "${use.name}" received arguments that were not a valid JSON object, so they could not be parsed. Re-issue the call with the arguments encoded as a single well-formed JSON object matching the tool's input schema.`;
|
|
26698
|
+
if (raw) {
|
|
26699
|
+
const max = 800;
|
|
26700
|
+
const excerpt2 = raw.length > max ? `${raw.slice(0, max)}\u2026 (truncated, ${raw.length} chars total)` : raw;
|
|
26701
|
+
content += ` Common cause: a string field (e.g. code in old_string/new_string) contains literal newlines, quotes, or backslashes that must be JSON-escaped, or the payload was cut off mid-stream. The raw arguments received were:
|
|
26702
|
+
${excerpt2}`;
|
|
26703
|
+
}
|
|
26704
|
+
return {
|
|
26705
|
+
type: "tool_result",
|
|
26706
|
+
tool_use_id: use.id,
|
|
26707
|
+
content,
|
|
26708
|
+
is_error: true
|
|
26709
|
+
};
|
|
26710
|
+
}
|
|
26711
|
+
function deniedResult(use, reason) {
|
|
26712
|
+
return {
|
|
26713
|
+
type: "tool_result",
|
|
26714
|
+
tool_use_id: use.id,
|
|
26715
|
+
content: `Tool "${use.name}" denied: ${reason ?? "policy"}`,
|
|
26716
|
+
is_error: true
|
|
26717
|
+
};
|
|
26718
|
+
}
|
|
26719
|
+
function blockedByHookResult(use, reason) {
|
|
26720
|
+
return {
|
|
26721
|
+
type: "tool_result",
|
|
26722
|
+
tool_use_id: use.id,
|
|
26723
|
+
content: `Tool "${use.name}" was blocked by a PreToolUse hook: ${reason ?? "no reason given"}`,
|
|
26724
|
+
is_error: true
|
|
26725
|
+
};
|
|
26726
|
+
}
|
|
26727
|
+
|
|
26728
|
+
// src/execution/tool-error-taxonomy.ts
|
|
26729
|
+
function toolErrorResult(use, err, opts = {}) {
|
|
26730
|
+
const { category, retryable, detail } = classifyToolError(err);
|
|
26731
|
+
const rawMessage = err instanceof Error ? err.message : typeof err === "string" ? err : "Unknown error";
|
|
26732
|
+
const userMessage = opts.scrubber ? opts.scrubber(rawMessage) : rawMessage;
|
|
26733
|
+
const info = {
|
|
26734
|
+
category,
|
|
26735
|
+
retryable,
|
|
26736
|
+
userMessage,
|
|
26737
|
+
...detail !== void 0 ? { detail } : {}
|
|
26738
|
+
};
|
|
26739
|
+
const prefix = formatErrorPrefix(category, retryable);
|
|
26740
|
+
const detailSuffix = detail ? ` [${detail}]` : "";
|
|
26741
|
+
return {
|
|
26742
|
+
type: "tool_result",
|
|
26743
|
+
tool_use_id: use.id,
|
|
26744
|
+
content: `${prefix}: ${userMessage}${detailSuffix}`,
|
|
26745
|
+
is_error: true,
|
|
26746
|
+
// Attach structured info for programmatic consumers (retry logic,
|
|
26747
|
+
// audit log, observability).
|
|
26748
|
+
_toolErrorInfo: info
|
|
26749
|
+
};
|
|
26750
|
+
}
|
|
26751
|
+
function formatErrorPrefix(category, retryable) {
|
|
26752
|
+
const label = CATEGORY_LABELS[category] ?? "Error";
|
|
26753
|
+
return retryable ? `${label} (retryable)` : label;
|
|
26754
|
+
}
|
|
26755
|
+
var CATEGORY_LABELS = {
|
|
26756
|
+
["transient" /* TRANSIENT */]: "Transient error",
|
|
26757
|
+
["not_found" /* NOT_FOUND */]: "Not found",
|
|
26758
|
+
["permission" /* PERMISSION */]: "Permission denied",
|
|
26759
|
+
["validation" /* VALIDATION */]: "Validation error",
|
|
26760
|
+
["fatal" /* FATAL */]: "Error"
|
|
26761
|
+
};
|
|
26762
|
+
|
|
26423
26763
|
// src/execution/tool-executor-stream.ts
|
|
26424
26764
|
async function executeStreamedTool({
|
|
26425
26765
|
tool,
|
|
@@ -26520,12 +26860,27 @@ ${progressTail}` : progressTail;
|
|
|
26520
26860
|
}
|
|
26521
26861
|
|
|
26522
26862
|
// src/execution/tool-executor.ts
|
|
26863
|
+
async function mapWithConcurrency2(items, limit, run) {
|
|
26864
|
+
if (items.length === 0) return [];
|
|
26865
|
+
const results = new Array(items.length);
|
|
26866
|
+
let nextIndex = 0;
|
|
26867
|
+
const worker = async () => {
|
|
26868
|
+
while (nextIndex < items.length) {
|
|
26869
|
+
const index = nextIndex++;
|
|
26870
|
+
results[index] = await run(items[index]);
|
|
26871
|
+
}
|
|
26872
|
+
};
|
|
26873
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()));
|
|
26874
|
+
return results;
|
|
26875
|
+
}
|
|
26523
26876
|
var ToolExecutor = class _ToolExecutor {
|
|
26524
26877
|
constructor(registry, opts) {
|
|
26525
26878
|
this.registry = registry;
|
|
26526
26879
|
this.opts = opts;
|
|
26527
26880
|
this.iterationTimeoutMs = opts.iterationTimeoutMs ?? 3e5;
|
|
26528
26881
|
this.maxToolTimeoutMs = opts.maxToolTimeoutMs ?? 3e5;
|
|
26882
|
+
const requestedParallelism = opts.maxParallelTools ?? 4;
|
|
26883
|
+
this.maxParallelTools = Number.isFinite(requestedParallelism) ? Math.max(1, Math.min(16, Math.floor(requestedParallelism))) : 4;
|
|
26529
26884
|
this.serializer = createToolOutputSerializer({
|
|
26530
26885
|
perIterationOutputCapBytes: opts.perIterationOutputCapBytes ?? 1e5
|
|
26531
26886
|
});
|
|
@@ -26541,6 +26896,7 @@ var ToolExecutor = class _ToolExecutor {
|
|
|
26541
26896
|
serializer;
|
|
26542
26897
|
iterationTimeoutMs;
|
|
26543
26898
|
maxToolTimeoutMs;
|
|
26899
|
+
maxParallelTools;
|
|
26544
26900
|
/**
|
|
26545
26901
|
* Clear the interactive confirm awaiter so the executor returns
|
|
26546
26902
|
* `ToolConfirmPendingResult` instead of blocking on stdin. Used by
|
|
@@ -26734,18 +27090,18 @@ ${errorDetails}`,
|
|
|
26734
27090
|
if (this.opts.confirmAwaiter) {
|
|
26735
27091
|
const awaiter = this.opts.confirmAwaiter;
|
|
26736
27092
|
const choice = await new Promise(
|
|
26737
|
-
(
|
|
27093
|
+
(resolve17, reject) => {
|
|
26738
27094
|
const signal = ctx.signal;
|
|
26739
|
-
const onAbort = () =>
|
|
27095
|
+
const onAbort = () => resolve17("abort");
|
|
26740
27096
|
if (signal.aborted) {
|
|
26741
|
-
|
|
27097
|
+
resolve17("abort");
|
|
26742
27098
|
return;
|
|
26743
27099
|
}
|
|
26744
27100
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
26745
27101
|
awaiter(tool, use.input, use.id, suggestedPattern).then(
|
|
26746
27102
|
(c) => {
|
|
26747
27103
|
signal.removeEventListener("abort", onAbort);
|
|
26748
|
-
|
|
27104
|
+
resolve17(c);
|
|
26749
27105
|
},
|
|
26750
27106
|
(e) => {
|
|
26751
27107
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -26868,12 +27224,9 @@ ${post.additionalContext}`;
|
|
|
26868
27224
|
const { category, retryable, detail } = classifyToolError(err);
|
|
26869
27225
|
this.hintRenderMode(tool.name);
|
|
26870
27226
|
this.opts.renderer?.writeToolResult(tool.name, scrubbed, true);
|
|
26871
|
-
const result = {
|
|
26872
|
-
|
|
26873
|
-
|
|
26874
|
-
content: `Tool "${tool.name}" threw: ${scrubbed}`,
|
|
26875
|
-
is_error: true
|
|
26876
|
-
};
|
|
27227
|
+
const result = toolErrorResult(use, err, {
|
|
27228
|
+
scrubber: (s) => this.opts.secretScrubber.scrub(s)
|
|
27229
|
+
});
|
|
26877
27230
|
budget = this.budgetForString(result.content, budget);
|
|
26878
27231
|
if (err instanceof Error) span?.recordError(err);
|
|
26879
27232
|
span?.setAttribute("tool.is_error", true);
|
|
@@ -26893,21 +27246,17 @@ ${post.additionalContext}`;
|
|
|
26893
27246
|
const isStructured = isWrongStackError(err);
|
|
26894
27247
|
const msg = isStructured ? err.describe() : toErrorMessage(err);
|
|
26895
27248
|
const scrubbed = this.opts.secretScrubber.scrub(msg);
|
|
26896
|
-
const { category, retryable, detail } = classifyToolError(err);
|
|
26897
27249
|
const tool = this.registry.get(use.name);
|
|
26898
27250
|
const toolName = tool?.name ?? use.name;
|
|
26899
27251
|
this.hintRenderMode(toolName);
|
|
26900
27252
|
this.opts.renderer?.writeToolResult(toolName, scrubbed, true);
|
|
26901
|
-
const result = {
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
|
|
26905
|
-
|
|
26906
|
-
}
|
|
27253
|
+
const result = toolErrorResult(use, err, {
|
|
27254
|
+
scrubber: (s) => this.opts.secretScrubber.scrub(s)
|
|
27255
|
+
});
|
|
27256
|
+
if (isStructured) {
|
|
27257
|
+
result.content = scrubbed;
|
|
27258
|
+
}
|
|
26907
27259
|
budget = this.budgetForString(result.content, budget);
|
|
26908
|
-
void category;
|
|
26909
|
-
void retryable;
|
|
26910
|
-
void detail;
|
|
26911
27260
|
return { result, tool, durationMs: 0 };
|
|
26912
27261
|
}
|
|
26913
27262
|
};
|
|
@@ -26919,7 +27268,7 @@ ${post.additionalContext}`;
|
|
|
26919
27268
|
return { outputs, remainingBudget: budget };
|
|
26920
27269
|
}
|
|
26921
27270
|
if (strategy === "parallel") {
|
|
26922
|
-
const outputs = await
|
|
27271
|
+
const outputs = await mapWithConcurrency2(toolUses, this.maxParallelTools, safeRun);
|
|
26923
27272
|
return { outputs, remainingBudget: budget };
|
|
26924
27273
|
}
|
|
26925
27274
|
const nonMutating = [];
|
|
@@ -26930,7 +27279,7 @@ ${post.additionalContext}`;
|
|
|
26930
27279
|
if (tool?.mutating) mutating.push(use);
|
|
26931
27280
|
else nonMutating.push(use);
|
|
26932
27281
|
}
|
|
26933
|
-
const firstPass = await
|
|
27282
|
+
const firstPass = await mapWithConcurrency2(nonMutating, this.maxParallelTools, safeRun);
|
|
26934
27283
|
const secondPass = [];
|
|
26935
27284
|
for (const use of mutating) {
|
|
26936
27285
|
secondPass.push(await safeRun(use));
|
|
@@ -27065,29 +27414,32 @@ ${post.additionalContext}`;
|
|
|
27065
27414
|
let output;
|
|
27066
27415
|
const execute = () => typeof tool.executeStream === "function" ? this.runStreamedTool(tool, input, ctx, combined, toolUseId) : (async () => tool.execute(input, ctx, { signal: combined }))();
|
|
27067
27416
|
const telemetryToolCallId = toolUseId ?? `nested-${randomUUID16()}`;
|
|
27068
|
-
const toolPromise = this.opts.events ? runWithNetworkTelemetry(
|
|
27069
|
-
events: this.opts.events,
|
|
27070
|
-
sessionId: resolveEventSessionId(ctx),
|
|
27071
|
-
...ctx.traceId ? { traceId: ctx.traceId } : {},
|
|
27072
|
-
...ctx.agentId ? { agentId: ctx.agentId } : {},
|
|
27073
|
-
toolCallId: telemetryToolCallId,
|
|
27074
|
-
initiator: "tool",
|
|
27075
|
-
operationName: tool.name
|
|
27076
|
-
}, () => runWithProcessTelemetry(
|
|
27417
|
+
const toolPromise = this.opts.events ? runWithNetworkTelemetry(
|
|
27077
27418
|
{
|
|
27078
27419
|
events: this.opts.events,
|
|
27079
27420
|
sessionId: resolveEventSessionId(ctx),
|
|
27080
27421
|
...ctx.traceId ? { traceId: ctx.traceId } : {},
|
|
27081
27422
|
...ctx.agentId ? { agentId: ctx.agentId } : {},
|
|
27082
27423
|
toolCallId: telemetryToolCallId,
|
|
27083
|
-
|
|
27424
|
+
initiator: "tool",
|
|
27425
|
+
operationName: tool.name
|
|
27084
27426
|
},
|
|
27085
|
-
|
|
27086
|
-
|
|
27427
|
+
() => runWithProcessTelemetry(
|
|
27428
|
+
{
|
|
27429
|
+
events: this.opts.events,
|
|
27430
|
+
sessionId: resolveEventSessionId(ctx),
|
|
27431
|
+
...ctx.traceId ? { traceId: ctx.traceId } : {},
|
|
27432
|
+
...ctx.agentId ? { agentId: ctx.agentId } : {},
|
|
27433
|
+
toolCallId: telemetryToolCallId,
|
|
27434
|
+
toolName: tool.name
|
|
27435
|
+
},
|
|
27436
|
+
execute
|
|
27437
|
+
)
|
|
27438
|
+
) : execute();
|
|
27087
27439
|
toolPromise.catch(() => {
|
|
27088
27440
|
});
|
|
27089
27441
|
try {
|
|
27090
|
-
output = await new Promise((
|
|
27442
|
+
output = await new Promise((resolve17, reject) => {
|
|
27091
27443
|
const onAbort = () => {
|
|
27092
27444
|
setTimeout(() => reject(abortReasonToError(combined.reason)), 0);
|
|
27093
27445
|
};
|
|
@@ -27095,7 +27447,7 @@ ${post.additionalContext}`;
|
|
|
27095
27447
|
toolPromise.then(
|
|
27096
27448
|
(v) => {
|
|
27097
27449
|
combined.removeEventListener("abort", onAbort);
|
|
27098
|
-
|
|
27450
|
+
resolve17(v);
|
|
27099
27451
|
},
|
|
27100
27452
|
(e) => {
|
|
27101
27453
|
combined.removeEventListener("abort", onAbort);
|
|
@@ -28627,9 +28979,9 @@ var DefaultHealthRegistry = class {
|
|
|
28627
28979
|
}
|
|
28628
28980
|
async runOne(check) {
|
|
28629
28981
|
let timer = null;
|
|
28630
|
-
const timeout = new Promise((
|
|
28982
|
+
const timeout = new Promise((resolve17) => {
|
|
28631
28983
|
timer = setTimeout(
|
|
28632
|
-
() =>
|
|
28984
|
+
() => resolve17({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
|
|
28633
28985
|
this.timeoutMs
|
|
28634
28986
|
);
|
|
28635
28987
|
});
|
|
@@ -28753,6 +29105,7 @@ function metricToolName(name) {
|
|
|
28753
29105
|
}
|
|
28754
29106
|
|
|
28755
29107
|
// src/observability/prometheus.ts
|
|
29108
|
+
import { timingSafeEqual } from "node:crypto";
|
|
28756
29109
|
var NUMBER_FORMAT_INFINITY = "NaN";
|
|
28757
29110
|
function escapeLabelValue(v) {
|
|
28758
29111
|
return v.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, '\\"');
|
|
@@ -28809,21 +29162,47 @@ function renderPrometheus(snapshot) {
|
|
|
28809
29162
|
return lines.join("\n") + "\n";
|
|
28810
29163
|
}
|
|
28811
29164
|
var PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
|
|
29165
|
+
function isLoopbackBindHost(host) {
|
|
29166
|
+
const h = host.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
|
29167
|
+
return h === "127.0.0.1" || h === "localhost" || h === "::1" || h.startsWith("127.");
|
|
29168
|
+
}
|
|
29169
|
+
function bearerMatches(header, token) {
|
|
29170
|
+
if (!header) return false;
|
|
29171
|
+
const prefix = "bearer ";
|
|
29172
|
+
if (!header.toLowerCase().startsWith(prefix)) return false;
|
|
29173
|
+
const supplied = Buffer.from(header.slice(prefix.length).trim(), "utf8");
|
|
29174
|
+
const expected = Buffer.from(token, "utf8");
|
|
29175
|
+
if (supplied.length !== expected.length) return false;
|
|
29176
|
+
return timingSafeEqual(supplied, expected);
|
|
29177
|
+
}
|
|
28812
29178
|
async function startMetricsServer(opts) {
|
|
28813
29179
|
const tls = opts.tls;
|
|
28814
29180
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
28815
29181
|
const host = opts.host ?? "127.0.0.1";
|
|
28816
|
-
const
|
|
29182
|
+
const path40 = opts.path ?? "/metrics";
|
|
28817
29183
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
28818
29184
|
const healthRegistry = opts.healthRegistry;
|
|
29185
|
+
const token = opts.token;
|
|
29186
|
+
if (!isLoopbackBindHost(host) && !token) {
|
|
29187
|
+
throw new Error(
|
|
29188
|
+
`startMetricsServer: refusing to bind to non-loopback host "${host}" without a token \u2014 pass a token to expose metrics to the network, or bind to 127.0.0.1.`
|
|
29189
|
+
);
|
|
29190
|
+
}
|
|
28819
29191
|
const listener = (req, res) => {
|
|
28820
29192
|
if (!req.url || req.method !== "GET") {
|
|
28821
29193
|
res.statusCode = req.url ? 405 : 400;
|
|
28822
29194
|
res.end();
|
|
28823
29195
|
return;
|
|
28824
29196
|
}
|
|
29197
|
+
if (token && !bearerMatches(req.headers.authorization, token)) {
|
|
29198
|
+
res.statusCode = 401;
|
|
29199
|
+
res.setHeader("www-authenticate", "Bearer");
|
|
29200
|
+
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
29201
|
+
res.end("Unauthorized");
|
|
29202
|
+
return;
|
|
29203
|
+
}
|
|
28825
29204
|
const url = req.url.split("?")[0];
|
|
28826
|
-
if (url ===
|
|
29205
|
+
if (url === path40) {
|
|
28827
29206
|
let body;
|
|
28828
29207
|
try {
|
|
28829
29208
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -28869,14 +29248,14 @@ async function startMetricsServer(opts) {
|
|
|
28869
29248
|
const { createServer } = await import("node:http");
|
|
28870
29249
|
server = createServer(listener);
|
|
28871
29250
|
}
|
|
28872
|
-
await new Promise((
|
|
29251
|
+
await new Promise((resolve17, reject) => {
|
|
28873
29252
|
const onError = (err) => {
|
|
28874
29253
|
server.off("listening", onListening);
|
|
28875
29254
|
reject(err);
|
|
28876
29255
|
};
|
|
28877
29256
|
const onListening = () => {
|
|
28878
29257
|
server.off("error", onError);
|
|
28879
|
-
|
|
29258
|
+
resolve17();
|
|
28880
29259
|
};
|
|
28881
29260
|
server.once("error", onError);
|
|
28882
29261
|
server.once("listening", onListening);
|
|
@@ -28887,9 +29266,9 @@ async function startMetricsServer(opts) {
|
|
|
28887
29266
|
const protocol = useHttps ? "https" : "http";
|
|
28888
29267
|
return {
|
|
28889
29268
|
port: boundPort,
|
|
28890
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
28891
|
-
close: () => new Promise((
|
|
28892
|
-
server.close((err) => err ? reject(err) :
|
|
29269
|
+
url: `${protocol}://${host}:${boundPort}${path40}`,
|
|
29270
|
+
close: () => new Promise((resolve17, reject) => {
|
|
29271
|
+
server.close((err) => err ? reject(err) : resolve17());
|
|
28893
29272
|
})
|
|
28894
29273
|
};
|
|
28895
29274
|
}
|
|
@@ -29204,19 +29583,19 @@ var UNSAFE_PROPERTY_NAMES = /* @__PURE__ */ new Set(["__proto__", "prototype", "
|
|
|
29204
29583
|
function isRecord5(value) {
|
|
29205
29584
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29206
29585
|
}
|
|
29207
|
-
function error(diagnostics, code,
|
|
29208
|
-
diagnostics.push({ severity: "error", code, path:
|
|
29586
|
+
function error(diagnostics, code, path40, message) {
|
|
29587
|
+
diagnostics.push({ severity: "error", code, path: path40, message });
|
|
29209
29588
|
}
|
|
29210
|
-
function validatePatterns(value,
|
|
29589
|
+
function validatePatterns(value, path40, diagnostics) {
|
|
29211
29590
|
if (!Array.isArray(value)) {
|
|
29212
|
-
error(diagnostics, "invalid_pattern_list",
|
|
29591
|
+
error(diagnostics, "invalid_pattern_list", path40, "must be an array of strings");
|
|
29213
29592
|
return void 0;
|
|
29214
29593
|
}
|
|
29215
29594
|
if (value.length > TRUST_POLICY_LIMITS.maxPatternsPerRule) {
|
|
29216
29595
|
error(
|
|
29217
29596
|
diagnostics,
|
|
29218
29597
|
"too_many_patterns",
|
|
29219
|
-
|
|
29598
|
+
path40,
|
|
29220
29599
|
`must contain at most ${TRUST_POLICY_LIMITS.maxPatternsPerRule} patterns`
|
|
29221
29600
|
);
|
|
29222
29601
|
return void 0;
|
|
@@ -29224,7 +29603,7 @@ function validatePatterns(value, path39, diagnostics) {
|
|
|
29224
29603
|
const patterns = [];
|
|
29225
29604
|
const seen = /* @__PURE__ */ new Set();
|
|
29226
29605
|
for (const [index, pattern] of value.entries()) {
|
|
29227
|
-
const itemPath = `${
|
|
29606
|
+
const itemPath = `${path40}[${index}]`;
|
|
29228
29607
|
if (typeof pattern !== "string" || pattern.length === 0 || pattern.length > TRUST_POLICY_LIMITS.maxPatternChars) {
|
|
29229
29608
|
error(
|
|
29230
29609
|
diagnostics,
|
|
@@ -29328,16 +29707,344 @@ function validateTrustPolicy(value) {
|
|
|
29328
29707
|
}
|
|
29329
29708
|
|
|
29330
29709
|
// src/security/yolo-risk.ts
|
|
29710
|
+
import * as path34 from "node:path";
|
|
29711
|
+
var CATASTROPHIC_PATTERNS = [
|
|
29712
|
+
/\b(?:mkfs(?:\.[a-z0-9]+)?|mke2fs|newfs)\b/i,
|
|
29713
|
+
// make a filesystem — wipes a partition
|
|
29714
|
+
/\bformat\s+[A-Za-z]:/i,
|
|
29715
|
+
// format C: — wipes a Windows volume
|
|
29716
|
+
/\bdiskpart\b/i,
|
|
29717
|
+
// Windows partition editor
|
|
29718
|
+
/\bdd\b[^|]*\bof=(?:\/dev\/|\\\\[.?]\\)/i,
|
|
29719
|
+
// dd writing straight to a raw device
|
|
29720
|
+
/>\s*\/dev\/(?:sd|hd|nvme|disk|mapper|vd)/i,
|
|
29721
|
+
// redirect into a raw block device
|
|
29722
|
+
/:\(\)\s*\{\s*:\|:&\s*\}\s*;/
|
|
29723
|
+
// classic fork bomb
|
|
29724
|
+
];
|
|
29725
|
+
var HIGH_IMPACT_PATTERNS = [
|
|
29726
|
+
/\b(?:curl|wget|fetch|httpie|http|irm|iwr|Invoke-WebRequest|Invoke-RestMethod)\b[\s\S]{0,300}\|\s*(?:sudo\s+)?(?:sh|bash|zsh|fish|pwsh|powershell|iex|Invoke-Expression)\b/i,
|
|
29727
|
+
/\b(?:powershell|pwsh)(?:\.exe)?\b[\s\S]{0,120}-(?:enc|encodedcommand)\b/i,
|
|
29728
|
+
/\b(?:shutdown|reboot)\b/i
|
|
29729
|
+
];
|
|
29730
|
+
var CATASTROPHIC_POSIX_ROOTS = /* @__PURE__ */ new Set([
|
|
29731
|
+
"/etc",
|
|
29732
|
+
"/usr",
|
|
29733
|
+
"/bin",
|
|
29734
|
+
"/sbin",
|
|
29735
|
+
"/lib",
|
|
29736
|
+
"/lib64",
|
|
29737
|
+
"/var",
|
|
29738
|
+
"/boot",
|
|
29739
|
+
"/dev",
|
|
29740
|
+
"/sys",
|
|
29741
|
+
"/proc",
|
|
29742
|
+
"/opt",
|
|
29743
|
+
"/root",
|
|
29744
|
+
"/home",
|
|
29745
|
+
"/srv",
|
|
29746
|
+
"/run",
|
|
29747
|
+
"/system",
|
|
29748
|
+
"/library",
|
|
29749
|
+
"/applications",
|
|
29750
|
+
"/users"
|
|
29751
|
+
]);
|
|
29752
|
+
var CATASTROPHIC_WIN_SUBDIRS = /* @__PURE__ */ new Set([
|
|
29753
|
+
"windows",
|
|
29754
|
+
"system32",
|
|
29755
|
+
"winnt",
|
|
29756
|
+
"program files",
|
|
29757
|
+
"program files (x86)",
|
|
29758
|
+
"programdata",
|
|
29759
|
+
"users"
|
|
29760
|
+
]);
|
|
29761
|
+
var SHELL_OPERATORS = /* @__PURE__ */ new Set(["&&", "||", "|", ";", ">", ">>", "<", "2>", "2>>"]);
|
|
29331
29762
|
function getInputString(input, key) {
|
|
29332
29763
|
if (!input || typeof input !== "object") return void 0;
|
|
29333
29764
|
const value = input[key];
|
|
29334
29765
|
return typeof value === "string" ? value : void 0;
|
|
29335
29766
|
}
|
|
29767
|
+
function pathLooksInsideProject(rawPath, projectRoot) {
|
|
29768
|
+
if (!projectRoot) return false;
|
|
29769
|
+
if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
|
|
29770
|
+
const resolved = path34.resolve(projectRoot, rawPath);
|
|
29771
|
+
const relative8 = path34.relative(projectRoot, resolved);
|
|
29772
|
+
return !!relative8 && !relative8.startsWith("..") && !path34.isAbsolute(relative8);
|
|
29773
|
+
}
|
|
29774
|
+
function tokenizeShell(command) {
|
|
29775
|
+
return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
29776
|
+
}
|
|
29777
|
+
function commandSegment(tokens, start) {
|
|
29778
|
+
const out = [];
|
|
29779
|
+
for (let i = start; i < tokens.length; i++) {
|
|
29780
|
+
const token = tokens[i];
|
|
29781
|
+
if (token === void 0 || SHELL_OPERATORS.has(token)) break;
|
|
29782
|
+
out.push(token);
|
|
29783
|
+
}
|
|
29784
|
+
return out;
|
|
29785
|
+
}
|
|
29786
|
+
function hasShortFlag(args, letters) {
|
|
29787
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29788
|
+
for (const arg of args) {
|
|
29789
|
+
if (!arg.startsWith("-") || arg.startsWith("--")) continue;
|
|
29790
|
+
for (const ch of arg.replace(/^-+/, "")) seen.add(ch.toLowerCase());
|
|
29791
|
+
}
|
|
29792
|
+
return letters.split("").every((letter) => seen.has(letter));
|
|
29793
|
+
}
|
|
29794
|
+
function hasRecursiveForceDelete(command, projectRoot) {
|
|
29795
|
+
const tokens = tokenizeShell(command);
|
|
29796
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
29797
|
+
const token = tokens[i]?.toLowerCase();
|
|
29798
|
+
if (!token) continue;
|
|
29799
|
+
if (token === "rm" || token === "rmdir") {
|
|
29800
|
+
const args = commandSegment(tokens, i + 1);
|
|
29801
|
+
const recursiveForce = hasShortFlag(args, "rf") || args.some((arg) => arg === "--recursive") && args.some((arg) => arg === "--force");
|
|
29802
|
+
if (recursiveForce) {
|
|
29803
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !SHELL_OPERATORS.has(arg));
|
|
29804
|
+
if (targets.length > 0 && targets.every((target) => target.trim().length === 0)) {
|
|
29805
|
+
continue;
|
|
29806
|
+
}
|
|
29807
|
+
if (targets.length === 0) return true;
|
|
29808
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
29809
|
+
if (targets.some((target) => !pathLooksInsideProject(target, projectRoot))) return true;
|
|
29810
|
+
}
|
|
29811
|
+
}
|
|
29812
|
+
if (token === "remove-item" || token === "ri") {
|
|
29813
|
+
const args = commandSegment(tokens, i + 1).map((arg) => arg.toLowerCase());
|
|
29814
|
+
const recurse = args.some((arg) => arg === "-recurse" || arg === "-r");
|
|
29815
|
+
const force = args.some((arg) => arg === "-force" || arg === "-f");
|
|
29816
|
+
if (recurse && force && !args.includes("-whatif")) {
|
|
29817
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !SHELL_OPERATORS.has(arg));
|
|
29818
|
+
if (targets.length === 0) return true;
|
|
29819
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
29820
|
+
if (targets.some((target) => !pathLooksInsideProject(target, projectRoot))) return true;
|
|
29821
|
+
}
|
|
29822
|
+
}
|
|
29823
|
+
if (token === "rd" || token === "rmdir") {
|
|
29824
|
+
const args = commandSegment(tokens, i + 1).map((arg) => arg.toLowerCase());
|
|
29825
|
+
if (args.includes("/s")) {
|
|
29826
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !arg.startsWith("/") && !SHELL_OPERATORS.has(arg));
|
|
29827
|
+
if (targets.length === 0) return true;
|
|
29828
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
29829
|
+
if (targets.some((target) => !pathLooksInsideProject(target, projectRoot))) return true;
|
|
29830
|
+
}
|
|
29831
|
+
}
|
|
29832
|
+
}
|
|
29833
|
+
return false;
|
|
29834
|
+
}
|
|
29835
|
+
function hasGitHistoryRewrite(command) {
|
|
29836
|
+
const tokens = tokenizeShell(command).map((token) => token.toLowerCase());
|
|
29837
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
29838
|
+
if (tokens[i] !== "git") continue;
|
|
29839
|
+
const args = commandSegment(tokens, i + 1);
|
|
29840
|
+
if (args.includes("reset") && args.some((arg) => arg === "--hard" || arg.startsWith("--hard="))) {
|
|
29841
|
+
return true;
|
|
29842
|
+
}
|
|
29843
|
+
const cleanIdx = args.indexOf("clean");
|
|
29844
|
+
if (cleanIdx >= 0) {
|
|
29845
|
+
const cleanArgs = args.slice(cleanIdx + 1);
|
|
29846
|
+
if (cleanArgs.some((arg) => arg === "-f" || arg === "--force" || /^-[a-z]*f[a-z]*$/i.test(arg))) {
|
|
29847
|
+
return true;
|
|
29848
|
+
}
|
|
29849
|
+
}
|
|
29850
|
+
const pushIdx = args.indexOf("push");
|
|
29851
|
+
if (pushIdx >= 0) {
|
|
29852
|
+
const pushArgs = args.slice(pushIdx + 1);
|
|
29853
|
+
if (pushArgs.some(
|
|
29854
|
+
(arg) => arg === "-f" || arg === "--force" || arg === "--force-with-lease" || arg.startsWith("--force=") || arg.startsWith("--force-with-lease=")
|
|
29855
|
+
)) {
|
|
29856
|
+
return true;
|
|
29857
|
+
}
|
|
29858
|
+
}
|
|
29859
|
+
}
|
|
29860
|
+
return false;
|
|
29861
|
+
}
|
|
29862
|
+
function hasExternalPublish(command) {
|
|
29863
|
+
const tokens = tokenizeShell(command).map((token) => token.toLowerCase());
|
|
29864
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
29865
|
+
const cmd = tokens[i];
|
|
29866
|
+
if (!cmd) continue;
|
|
29867
|
+
const args = commandSegment(tokens, i + 1);
|
|
29868
|
+
if (["npm", "pnpm", "yarn", "bun"].includes(cmd) && (args.includes("publish") || args.includes("deploy"))) {
|
|
29869
|
+
return true;
|
|
29870
|
+
}
|
|
29871
|
+
if (cmd === "cargo" && (args.includes("publish") || args.includes("yank"))) return true;
|
|
29872
|
+
if ((cmd === "docker" || cmd === "podman") && args.includes("push")) return true;
|
|
29873
|
+
if (cmd === "kubectl") {
|
|
29874
|
+
const deleteIdx = args.indexOf("delete");
|
|
29875
|
+
if (deleteIdx >= 0 && (args[deleteIdx + 1] === "namespace" || args[deleteIdx + 1] === "ns")) {
|
|
29876
|
+
return true;
|
|
29877
|
+
}
|
|
29878
|
+
if (args.includes("drain")) return true;
|
|
29879
|
+
}
|
|
29880
|
+
}
|
|
29881
|
+
return false;
|
|
29882
|
+
}
|
|
29883
|
+
function hasFindExec(command) {
|
|
29884
|
+
const tokens = tokenizeShell(command).map((token) => token.toLowerCase());
|
|
29885
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
29886
|
+
if (tokens[i] !== "find") continue;
|
|
29887
|
+
const args = commandSegment(tokens, i + 1);
|
|
29888
|
+
if (args.some((arg) => arg === "-exec" || arg === "-ok" || arg === "-execdir")) return true;
|
|
29889
|
+
}
|
|
29890
|
+
return false;
|
|
29891
|
+
}
|
|
29892
|
+
function isCatastrophicDeleteTarget(rawTarget) {
|
|
29893
|
+
const t = rawTarget.replace(/^['"]|['"]$/g, "").trim();
|
|
29894
|
+
if (!t) return false;
|
|
29895
|
+
if (t === "*" || t === "." || t === "./" || t === ".\\" || t === "./*" || t === ".\\*") return true;
|
|
29896
|
+
const s = t.replace(/[\\/]\*+$/, "").replace(/[\\/]+$/, "");
|
|
29897
|
+
if (s === "") return true;
|
|
29898
|
+
if (s === "~" || /^\$HOME$/i.test(s) || /^%USERPROFILE%$/i.test(s)) return true;
|
|
29899
|
+
if (/^[A-Za-z]:$/.test(s)) return true;
|
|
29900
|
+
const norm = s.toLowerCase().replace(/\\/g, "/");
|
|
29901
|
+
if (CATASTROPHIC_POSIX_ROOTS.has(norm)) return true;
|
|
29902
|
+
const win = norm.match(/^[a-z]:\/([^/]+)$/);
|
|
29903
|
+
if (win?.[1] && CATASTROPHIC_WIN_SUBDIRS.has(win[1])) return true;
|
|
29904
|
+
return false;
|
|
29905
|
+
}
|
|
29906
|
+
function hasCatastrophicDelete(command) {
|
|
29907
|
+
const tokens = tokenizeShell(command);
|
|
29908
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
29909
|
+
const token = tokens[i]?.toLowerCase();
|
|
29910
|
+
if (!token) continue;
|
|
29911
|
+
if (token === "rm") {
|
|
29912
|
+
const args = tokens.slice(i + 1);
|
|
29913
|
+
const recursiveOrForce = args.some(
|
|
29914
|
+
(arg) => /^-[^-]*[rf]/i.test(arg) || arg === "--recursive" || arg === "--force" || arg === "--no-preserve-root"
|
|
29915
|
+
);
|
|
29916
|
+
if (!recursiveOrForce) continue;
|
|
29917
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !SHELL_OPERATORS.has(arg));
|
|
29918
|
+
if (targets.length === 0) return true;
|
|
29919
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
29920
|
+
}
|
|
29921
|
+
if (token === "remove-item" || token === "ri") {
|
|
29922
|
+
const args = tokens.slice(i + 1);
|
|
29923
|
+
const recursive = args.some((arg) => {
|
|
29924
|
+
const a = arg.toLowerCase();
|
|
29925
|
+
return a === "-recurse" || a === "-force";
|
|
29926
|
+
});
|
|
29927
|
+
if (!recursive) continue;
|
|
29928
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !SHELL_OPERATORS.has(arg));
|
|
29929
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
29930
|
+
}
|
|
29931
|
+
if (token === "rmdir" || token === "rd") {
|
|
29932
|
+
const args = tokens.slice(i + 1);
|
|
29933
|
+
const recursive = args.some((arg) => arg.toLowerCase() === "/s");
|
|
29934
|
+
if (!recursive) continue;
|
|
29935
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !arg.startsWith("/") && !SHELL_OPERATORS.has(arg));
|
|
29936
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
29937
|
+
}
|
|
29938
|
+
if (token === "del" || token === "erase") {
|
|
29939
|
+
const args = tokens.slice(i + 1);
|
|
29940
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !arg.startsWith("/") && !SHELL_OPERATORS.has(arg));
|
|
29941
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
29942
|
+
}
|
|
29943
|
+
}
|
|
29944
|
+
return false;
|
|
29945
|
+
}
|
|
29946
|
+
function isClearlyDestructiveBashCommand(command, projectRoot) {
|
|
29947
|
+
const trimmed = command.trim();
|
|
29948
|
+
if (!trimmed) return false;
|
|
29949
|
+
if (hasRecursiveForceDelete(trimmed, projectRoot)) return true;
|
|
29950
|
+
if (hasGitHistoryRewrite(trimmed)) return true;
|
|
29951
|
+
if (hasExternalPublish(trimmed)) return true;
|
|
29952
|
+
if (hasFindExec(trimmed)) return true;
|
|
29953
|
+
if (hasCatastrophicDelete(trimmed)) return true;
|
|
29954
|
+
if (CATASTROPHIC_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
|
|
29955
|
+
if (HIGH_IMPACT_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
|
|
29956
|
+
return false;
|
|
29957
|
+
}
|
|
29336
29958
|
|
|
29337
|
-
// src/security/
|
|
29959
|
+
// src/security/auto-approve-policy.ts
|
|
29960
|
+
var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
29961
|
+
allowedCapabilities;
|
|
29962
|
+
constructor(allowedCapabilities) {
|
|
29963
|
+
this.allowedCapabilities = allowedCapabilities ?? [
|
|
29964
|
+
ToolCapabilities.FS_READ,
|
|
29965
|
+
ToolCapabilities.NET_OUTBOUND
|
|
29966
|
+
];
|
|
29967
|
+
}
|
|
29968
|
+
static isMcpTool(name) {
|
|
29969
|
+
return name.startsWith("mcp__");
|
|
29970
|
+
}
|
|
29971
|
+
async evaluate(tool) {
|
|
29972
|
+
const caps = tool.capabilities ?? [];
|
|
29973
|
+
const hasAllowedCap = caps.some((c) => this.allowedCapabilities.includes(c));
|
|
29974
|
+
const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
29975
|
+
const mcpProxyAllowed = this.allowedCapabilities.includes(ToolCapabilities.MCP_PROXY);
|
|
29976
|
+
const dangerousNotAllowed = getDangerousCapabilities(tool).filter(
|
|
29977
|
+
(c) => !this.allowedCapabilities.includes(c)
|
|
29978
|
+
);
|
|
29979
|
+
const blocked = tool.permission === "deny" || isMcp && !mcpProxyAllowed || !hasAllowedCap || dangerousNotAllowed.length > 0;
|
|
29980
|
+
if (blocked) {
|
|
29981
|
+
const reason = isMcp && !mcpProxyAllowed ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow mcp.proxy explicitly` : tool.permission === "deny" ? "tool default deny" : dangerousNotAllowed.length > 0 ? `tool requires un-granted dangerous capability (needs: ${dangerousNotAllowed.join(", ")}, allowed: ${this.allowedCapabilities.join(", ")})` : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
|
|
29982
|
+
return {
|
|
29983
|
+
permission: "deny",
|
|
29984
|
+
source: "subagent_guard",
|
|
29985
|
+
reason
|
|
29986
|
+
};
|
|
29987
|
+
}
|
|
29988
|
+
return { permission: "auto", source: "yolo" };
|
|
29989
|
+
}
|
|
29990
|
+
async trust() {
|
|
29991
|
+
}
|
|
29992
|
+
async deny() {
|
|
29993
|
+
}
|
|
29994
|
+
denyOnce() {
|
|
29995
|
+
}
|
|
29996
|
+
allowOnce() {
|
|
29997
|
+
}
|
|
29998
|
+
async explain(tool) {
|
|
29999
|
+
const decision = await this.evaluate(tool);
|
|
30000
|
+
return {
|
|
30001
|
+
toolName: tool.name,
|
|
30002
|
+
subject: null,
|
|
30003
|
+
steps: [
|
|
30004
|
+
{
|
|
30005
|
+
rule: "subagent auto",
|
|
30006
|
+
matched: decision.permission === "auto",
|
|
30007
|
+
decision: decision.permission,
|
|
30008
|
+
source: decision.source,
|
|
30009
|
+
detail: decision.reason ?? `subagent policy: ${decision.permission}`
|
|
30010
|
+
}
|
|
30011
|
+
],
|
|
30012
|
+
winnerIndex: 0,
|
|
30013
|
+
decision
|
|
30014
|
+
};
|
|
30015
|
+
}
|
|
30016
|
+
async reload() {
|
|
30017
|
+
}
|
|
30018
|
+
};
|
|
30019
|
+
|
|
30020
|
+
// src/security/permission-helpers.ts
|
|
29338
30021
|
function matchesTrust(patterns, subject) {
|
|
29339
30022
|
return patterns.includes(subject) || matchAny(patterns, subject);
|
|
29340
30023
|
}
|
|
30024
|
+
function matchesCommandTrust(patterns, subject) {
|
|
30025
|
+
return patterns.includes(subject) || matchAnyCommand(patterns, subject);
|
|
30026
|
+
}
|
|
30027
|
+
function hasShellSubject(tool) {
|
|
30028
|
+
return tool.name === "bash" || tool.name === "shell" || tool.name === "exec" || hasCapability(tool, [
|
|
30029
|
+
ToolCapabilities.SHELL_ARBITRARY,
|
|
30030
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
30031
|
+
ToolCapabilities.SHELL_EXEC
|
|
30032
|
+
]);
|
|
30033
|
+
}
|
|
30034
|
+
function alwaysAllowUnavailableReason(tool, input) {
|
|
30035
|
+
const subject = subjectForToolInput(tool.name, input, tool.subjectKey);
|
|
30036
|
+
if (subject !== void 0) return void 0;
|
|
30037
|
+
return `"always allow" needs a subject to remember, and ${tool.name} calls do not carry one (no subjectKey, and no path/url/name input). Recording it would store a rule that can never match. Approve this call, or set a trust rule for ${tool.name} explicitly.`;
|
|
30038
|
+
}
|
|
30039
|
+
function permissionFingerprint(tool) {
|
|
30040
|
+
const caps = [...tool.capabilities ?? []].sort();
|
|
30041
|
+
return JSON.stringify([
|
|
30042
|
+
tool.permission ?? "",
|
|
30043
|
+
tool.riskTier ?? "",
|
|
30044
|
+
tool.mutating ? "m" : "",
|
|
30045
|
+
caps
|
|
30046
|
+
]);
|
|
30047
|
+
}
|
|
29341
30048
|
function shellCommandLineFromInput(input) {
|
|
29342
30049
|
const command = getInputString(input, "command") ?? getInputString(input, "cmd") ?? getInputString(input, "script");
|
|
29343
30050
|
if (!command) return void 0;
|
|
@@ -29405,6 +30112,8 @@ function shellCommandReadsSensitivePath(command) {
|
|
|
29405
30112
|
}
|
|
29406
30113
|
return false;
|
|
29407
30114
|
}
|
|
30115
|
+
|
|
30116
|
+
// src/security/permission-policy.ts
|
|
29408
30117
|
var DefaultPermissionPolicy = class {
|
|
29409
30118
|
policy = {};
|
|
29410
30119
|
loaded = false;
|
|
@@ -29441,9 +30150,13 @@ var DefaultPermissionPolicy = class {
|
|
|
29441
30150
|
/** Pre-compiled wildcard patterns — rebuilt on reload for O(1) lookup. */
|
|
29442
30151
|
wildcardEntries = [];
|
|
29443
30152
|
/**
|
|
29444
|
-
* Evaluate-result cache. Keyed by `tool.name::subject`
|
|
29445
|
-
* with the same tool+input skip namespace matching,
|
|
29446
|
-
* and pattern matching (matchAny).
|
|
30153
|
+
* Evaluate-result cache. Keyed by `tool.name::subject::permissionFingerprint`
|
|
30154
|
+
* so repeated calls with the same tool+input skip namespace matching,
|
|
30155
|
+
* subject computation, and pattern matching (matchAny). The fingerprint
|
|
30156
|
+
* suffix covers the `Tool` fields `evaluate()` reads (`permission`,
|
|
30157
|
+
* `riskTier`, `mutating`, `capabilities`), so a tool redefined via
|
|
30158
|
+
* `ToolRegistry.wrap()` misses the cache instead of inheriting a stale
|
|
30159
|
+
* verdict.
|
|
29447
30160
|
*
|
|
29448
30161
|
* Cleared on any state change (reload, trust, deny, yolo toggle) because
|
|
29449
30162
|
* the result depends on the full policy state. The write-tool smart-bypass
|
|
@@ -29459,11 +30172,46 @@ var DefaultPermissionPolicy = class {
|
|
|
29459
30172
|
_evalCache = new LruCache(500);
|
|
29460
30173
|
policyDiagnostics = [];
|
|
29461
30174
|
policyInvalid = false;
|
|
30175
|
+
/**
|
|
30176
|
+
* When true, YOLO also auto-approves clearly destructive shell commands.
|
|
30177
|
+
*
|
|
30178
|
+
* WS-008: `isClearlyDestructiveBashCommand` was exported and heavily tested
|
|
30179
|
+
* but called from zero production modules, so YOLO returned `auto` for
|
|
30180
|
+
* everything — including `rm -rf`, git history rewrites, and external
|
|
30181
|
+
* publishes. The `'yolo_destructive'` decision source, the getter/setter on
|
|
30182
|
+
* PermissionPolicy, and the TUI branch that reads it all existed; only the
|
|
30183
|
+
* gate itself was missing. Defaults to false: destructive commands still ask,
|
|
30184
|
+
* even in YOLO.
|
|
30185
|
+
*/
|
|
30186
|
+
yoloDestructive;
|
|
29462
30187
|
constructor(opts) {
|
|
29463
30188
|
this.trustFile = opts.trustFile;
|
|
29464
30189
|
this.yolo = opts.yolo ?? false;
|
|
30190
|
+
this.yoloDestructive = opts.yoloDestructive ?? false;
|
|
29465
30191
|
this.promptDelegate = opts.promptDelegate;
|
|
29466
30192
|
}
|
|
30193
|
+
/** Allow YOLO to cover destructive shell commands too (off by default). */
|
|
30194
|
+
setYoloDestructive(enabled) {
|
|
30195
|
+
if (this.yoloDestructive !== enabled) this._evalCache.clear();
|
|
30196
|
+
this.yoloDestructive = enabled;
|
|
30197
|
+
}
|
|
30198
|
+
/** Whether YOLO currently extends to destructive shell commands. */
|
|
30199
|
+
getYoloDestructive() {
|
|
30200
|
+
return this.yoloDestructive;
|
|
30201
|
+
}
|
|
30202
|
+
/**
|
|
30203
|
+
* True when YOLO is on but this specific call is a destructive shell command
|
|
30204
|
+
* the user has not opted to auto-approve. Shared by `evaluate()` and the
|
|
30205
|
+
* explain/trace path so the two cannot drift.
|
|
30206
|
+
*/
|
|
30207
|
+
yoloBlockedAsDestructive(tool, input, ctx) {
|
|
30208
|
+
if (!this.yolo || this.yoloDestructive) return false;
|
|
30209
|
+
const isShellSurface = tool.name === "bash" || tool.name === "exec" || (tool.capabilities ?? []).includes("shell.arbitrary");
|
|
30210
|
+
if (!isShellSurface) return false;
|
|
30211
|
+
const command = getInputString(input, "command") ?? shellCommandLineFromInput(input);
|
|
30212
|
+
if (!command) return false;
|
|
30213
|
+
return isClearlyDestructiveBashCommand(command, ctx.projectRoot);
|
|
30214
|
+
}
|
|
29467
30215
|
/**
|
|
29468
30216
|
* Replace (or clear) the interactive prompt delegate at runtime.
|
|
29469
30217
|
* Used by the CLI to switch from inline prompts (REPL) to event-driven
|
|
@@ -29537,15 +30285,17 @@ var DefaultPermissionPolicy = class {
|
|
|
29537
30285
|
this.loaded = true;
|
|
29538
30286
|
}
|
|
29539
30287
|
_logDeny(tool, subject, reason) {
|
|
29540
|
-
console.warn(
|
|
29541
|
-
|
|
29542
|
-
|
|
29543
|
-
|
|
29544
|
-
|
|
29545
|
-
|
|
29546
|
-
|
|
29547
|
-
|
|
29548
|
-
|
|
30288
|
+
console.warn(
|
|
30289
|
+
JSON.stringify({
|
|
30290
|
+
level: "warn",
|
|
30291
|
+
event: "permission.denied",
|
|
30292
|
+
message: `Permission denied: ${tool}${subject ? ` (subject: ${subject})` : ""} \u2014 ${reason}`,
|
|
30293
|
+
tool,
|
|
30294
|
+
subject,
|
|
30295
|
+
reason,
|
|
30296
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
30297
|
+
})
|
|
30298
|
+
);
|
|
29549
30299
|
}
|
|
29550
30300
|
async evaluate(tool, input, ctx) {
|
|
29551
30301
|
if (!this.loaded) await this.reload();
|
|
@@ -29561,14 +30311,19 @@ var DefaultPermissionPolicy = class {
|
|
|
29561
30311
|
const entry = this.policy[tool.name] ?? namespaceEntry;
|
|
29562
30312
|
const subject = subjectForToolInput(tool.name, input, tool.subjectKey);
|
|
29563
30313
|
const cacheKey = `${tool.name}::${subject ?? tool.name}`;
|
|
30314
|
+
const evalKey = `${cacheKey}::${permissionFingerprint(tool)}`;
|
|
29564
30315
|
if (tool.name !== "write") {
|
|
29565
|
-
const cached = this._evalCache.get(
|
|
30316
|
+
const cached = this._evalCache.get(evalKey);
|
|
29566
30317
|
if (cached !== void 0) return cached;
|
|
29567
30318
|
}
|
|
29568
30319
|
if (this.sessionDenied.has(cacheKey)) {
|
|
29569
30320
|
this._logDeny(tool.name, subject, "session soft deny (user pressed no)");
|
|
29570
|
-
const decision = {
|
|
29571
|
-
|
|
30321
|
+
const decision = {
|
|
30322
|
+
permission: "deny",
|
|
30323
|
+
source: "deny",
|
|
30324
|
+
reason: "session soft deny (user pressed no)"
|
|
30325
|
+
};
|
|
30326
|
+
this._evalCache.set(evalKey, decision);
|
|
29572
30327
|
return decision;
|
|
29573
30328
|
}
|
|
29574
30329
|
if (this.sessionAllowed.has(cacheKey)) {
|
|
@@ -29582,31 +30337,51 @@ var DefaultPermissionPolicy = class {
|
|
|
29582
30337
|
}
|
|
29583
30338
|
if (entry?.deny && subject && matchesTrust(entry.deny, subject)) {
|
|
29584
30339
|
this._logDeny(tool.name, subject, "matched deny pattern");
|
|
29585
|
-
const decision = {
|
|
29586
|
-
|
|
30340
|
+
const decision = {
|
|
30341
|
+
permission: "deny",
|
|
30342
|
+
source: "deny",
|
|
30343
|
+
reason: "matched deny pattern"
|
|
30344
|
+
};
|
|
30345
|
+
this._evalCache.set(evalKey, decision);
|
|
29587
30346
|
return decision;
|
|
29588
30347
|
}
|
|
29589
30348
|
if (tool.permission === "deny") {
|
|
29590
30349
|
this._logDeny(tool.name, subject, "tool default deny");
|
|
29591
|
-
const decision = {
|
|
29592
|
-
|
|
30350
|
+
const decision = {
|
|
30351
|
+
permission: "deny",
|
|
30352
|
+
source: "default",
|
|
30353
|
+
reason: "tool default deny"
|
|
30354
|
+
};
|
|
30355
|
+
this._evalCache.set(evalKey, decision);
|
|
29593
30356
|
return decision;
|
|
29594
30357
|
}
|
|
29595
|
-
|
|
29596
|
-
|
|
29597
|
-
|
|
30358
|
+
const allowMatches = hasShellSubject(tool) ? matchesCommandTrust : matchesTrust;
|
|
30359
|
+
if (entry?.allow && subject && allowMatches(entry.allow, subject)) {
|
|
30360
|
+
const decision = {
|
|
30361
|
+
permission: "auto",
|
|
30362
|
+
source: "trust",
|
|
30363
|
+
reason: "matched allow pattern"
|
|
30364
|
+
};
|
|
30365
|
+
this._evalCache.set(evalKey, decision);
|
|
29598
30366
|
return decision;
|
|
29599
30367
|
}
|
|
29600
30368
|
if (entry?.auto) {
|
|
29601
30369
|
const decision = { permission: "auto", source: "trust" };
|
|
29602
|
-
this._evalCache.set(
|
|
30370
|
+
this._evalCache.set(evalKey, decision);
|
|
29603
30371
|
return decision;
|
|
29604
30372
|
}
|
|
29605
30373
|
if (!this.yolo && this.isSensitiveReadCall(tool, input)) {
|
|
29606
30374
|
if (this.promptDelegate) {
|
|
29607
30375
|
const userDecision = await this.promptDelegate(tool, input, subject ?? tool.name);
|
|
29608
30376
|
if (userDecision === "always") {
|
|
29609
|
-
|
|
30377
|
+
if (subject === void 0) {
|
|
30378
|
+
return {
|
|
30379
|
+
permission: "auto",
|
|
30380
|
+
source: "user",
|
|
30381
|
+
reason: `approved once \u2014 ${alwaysAllowUnavailableReason(tool, input)}`
|
|
30382
|
+
};
|
|
30383
|
+
}
|
|
30384
|
+
await this.trust({ tool: tool.name, pattern: subject });
|
|
29610
30385
|
return {
|
|
29611
30386
|
permission: "auto",
|
|
29612
30387
|
source: "user",
|
|
@@ -29632,8 +30407,16 @@ var DefaultPermissionPolicy = class {
|
|
|
29632
30407
|
};
|
|
29633
30408
|
}
|
|
29634
30409
|
if (this.yolo) {
|
|
30410
|
+
if (this.yoloBlockedAsDestructive(tool, input, ctx)) {
|
|
30411
|
+
return {
|
|
30412
|
+
permission: "confirm",
|
|
30413
|
+
source: "yolo_destructive",
|
|
30414
|
+
riskTier: "destructive",
|
|
30415
|
+
reason: "destructive command needs explicit approval even in YOLO mode"
|
|
30416
|
+
};
|
|
30417
|
+
}
|
|
29635
30418
|
const decision = { permission: "auto", source: "yolo" };
|
|
29636
|
-
this._evalCache.set(
|
|
30419
|
+
this._evalCache.set(evalKey, decision);
|
|
29637
30420
|
return decision;
|
|
29638
30421
|
}
|
|
29639
30422
|
if (tool.name === "write" && subject) {
|
|
@@ -29657,13 +30440,20 @@ var DefaultPermissionPolicy = class {
|
|
|
29657
30440
|
const isMutating = tool.mutating || hasWriteCap || hasShellCap || hasInstallCap || hasConfigCap || hasSubagentCap;
|
|
29658
30441
|
if (tool.permission === "auto" && !isMutating) {
|
|
29659
30442
|
const decision = { permission: "auto", source: "default" };
|
|
29660
|
-
this._evalCache.set(
|
|
30443
|
+
this._evalCache.set(evalKey, decision);
|
|
29661
30444
|
return decision;
|
|
29662
30445
|
}
|
|
29663
30446
|
if (this.promptDelegate) {
|
|
29664
30447
|
const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
|
|
29665
30448
|
if (decision === "always") {
|
|
29666
|
-
|
|
30449
|
+
if (subject === void 0) {
|
|
30450
|
+
return {
|
|
30451
|
+
permission: "auto",
|
|
30452
|
+
source: "user",
|
|
30453
|
+
reason: `approved once \u2014 ${alwaysAllowUnavailableReason(tool, input)}`
|
|
30454
|
+
};
|
|
30455
|
+
}
|
|
30456
|
+
await this.trust({ tool: tool.name, pattern: subject });
|
|
29667
30457
|
return { permission: "auto", source: "user", reason: "user always-allowed" };
|
|
29668
30458
|
}
|
|
29669
30459
|
if (decision === "deny") {
|
|
@@ -29756,7 +30546,13 @@ var DefaultPermissionPolicy = class {
|
|
|
29756
30546
|
if (this.policyInvalid) {
|
|
29757
30547
|
add("policy invalid", true, "deny", "deny", "trust policy is invalid; all tools are denied");
|
|
29758
30548
|
winnerIndex = 0;
|
|
29759
|
-
return {
|
|
30549
|
+
return {
|
|
30550
|
+
toolName: tool.name,
|
|
30551
|
+
subject,
|
|
30552
|
+
steps,
|
|
30553
|
+
winnerIndex,
|
|
30554
|
+
decision: { permission: "deny", source: "deny", reason: "trust policy is invalid" }
|
|
30555
|
+
};
|
|
29760
30556
|
}
|
|
29761
30557
|
add("policy valid", false, "auto", "default", "trust policy loaded successfully");
|
|
29762
30558
|
const namespaceEntry = this.findNamespaceEntry(tool.name);
|
|
@@ -29764,39 +30560,143 @@ var DefaultPermissionPolicy = class {
|
|
|
29764
30560
|
const namespaceSource = namespaceEntry ? `wildcard entry matched (${Object.keys(this.policy).find((k) => k.includes("*") && matchGlob(k, tool.name))})` : "no namespace match";
|
|
29765
30561
|
const cacheKey = `${tool.name}::${subject ?? tool.name}`;
|
|
29766
30562
|
if (this.sessionDenied.has(cacheKey)) {
|
|
29767
|
-
add(
|
|
30563
|
+
add(
|
|
30564
|
+
"session soft deny",
|
|
30565
|
+
true,
|
|
30566
|
+
"deny",
|
|
30567
|
+
"deny",
|
|
30568
|
+
'user pressed "no" earlier in this session \u2014 blocked until reload'
|
|
30569
|
+
);
|
|
29768
30570
|
winnerIndex = steps.length - 1;
|
|
29769
|
-
return {
|
|
30571
|
+
return {
|
|
30572
|
+
toolName: tool.name,
|
|
30573
|
+
subject,
|
|
30574
|
+
steps,
|
|
30575
|
+
winnerIndex,
|
|
30576
|
+
decision: {
|
|
30577
|
+
permission: "deny",
|
|
30578
|
+
source: "deny",
|
|
30579
|
+
reason: "session soft deny (user pressed no)"
|
|
30580
|
+
}
|
|
30581
|
+
};
|
|
29770
30582
|
}
|
|
29771
|
-
add(
|
|
30583
|
+
add(
|
|
30584
|
+
"session soft deny",
|
|
30585
|
+
false,
|
|
30586
|
+
"deny",
|
|
30587
|
+
"deny",
|
|
30588
|
+
"no session-level soft deny for this tool+subject"
|
|
30589
|
+
);
|
|
29772
30590
|
if (this.sessionAllowed.has(cacheKey)) {
|
|
29773
|
-
add(
|
|
30591
|
+
add(
|
|
30592
|
+
"session soft allow",
|
|
30593
|
+
true,
|
|
30594
|
+
"auto",
|
|
30595
|
+
"trust",
|
|
30596
|
+
'user pressed "yes" in current session \u2014 one-shot auto-approve (consumed)'
|
|
30597
|
+
);
|
|
29774
30598
|
winnerIndex = steps.length - 1;
|
|
29775
|
-
return {
|
|
30599
|
+
return {
|
|
30600
|
+
toolName: tool.name,
|
|
30601
|
+
subject,
|
|
30602
|
+
steps,
|
|
30603
|
+
winnerIndex,
|
|
30604
|
+
decision: {
|
|
30605
|
+
permission: "auto",
|
|
30606
|
+
source: "trust",
|
|
30607
|
+
reason: "session one-shot allow (user pressed yes)"
|
|
30608
|
+
}
|
|
30609
|
+
};
|
|
29776
30610
|
}
|
|
29777
|
-
add(
|
|
30611
|
+
add(
|
|
30612
|
+
"session soft allow",
|
|
30613
|
+
false,
|
|
30614
|
+
"auto",
|
|
30615
|
+
"trust",
|
|
30616
|
+
"no session-level one-shot allow for this tool+subject"
|
|
30617
|
+
);
|
|
29778
30618
|
if (entry?.deny && subject && matchesTrust(entry.deny, subject)) {
|
|
29779
|
-
add(
|
|
30619
|
+
add(
|
|
30620
|
+
"trust deny",
|
|
30621
|
+
true,
|
|
30622
|
+
"deny",
|
|
30623
|
+
"deny",
|
|
30624
|
+
`subject "${subject}" matched a deny pattern in trust file`
|
|
30625
|
+
);
|
|
29780
30626
|
winnerIndex = steps.length - 1;
|
|
29781
|
-
return {
|
|
30627
|
+
return {
|
|
30628
|
+
toolName: tool.name,
|
|
30629
|
+
subject,
|
|
30630
|
+
steps,
|
|
30631
|
+
winnerIndex,
|
|
30632
|
+
decision: { permission: "deny", source: "deny", reason: "matched deny pattern" }
|
|
30633
|
+
};
|
|
29782
30634
|
}
|
|
29783
|
-
add(
|
|
30635
|
+
add(
|
|
30636
|
+
"trust deny",
|
|
30637
|
+
false,
|
|
30638
|
+
"deny",
|
|
30639
|
+
"deny",
|
|
30640
|
+
`no deny pattern matched (namespace: ${namespaceSource})`
|
|
30641
|
+
);
|
|
29784
30642
|
if (tool.permission === "deny") {
|
|
29785
|
-
add(
|
|
30643
|
+
add(
|
|
30644
|
+
"tool default deny",
|
|
30645
|
+
true,
|
|
30646
|
+
"deny",
|
|
30647
|
+
"default",
|
|
30648
|
+
`tool "${tool.name}" has permission: deny by default`
|
|
30649
|
+
);
|
|
29786
30650
|
winnerIndex = steps.length - 1;
|
|
29787
|
-
return {
|
|
30651
|
+
return {
|
|
30652
|
+
toolName: tool.name,
|
|
30653
|
+
subject,
|
|
30654
|
+
steps,
|
|
30655
|
+
winnerIndex,
|
|
30656
|
+
decision: { permission: "deny", source: "default", reason: "tool default deny" }
|
|
30657
|
+
};
|
|
29788
30658
|
}
|
|
29789
|
-
add(
|
|
30659
|
+
add(
|
|
30660
|
+
"tool default deny",
|
|
30661
|
+
false,
|
|
30662
|
+
"deny",
|
|
30663
|
+
"default",
|
|
30664
|
+
`tool "${tool.name}" has permission: "${tool.permission}"`
|
|
30665
|
+
);
|
|
29790
30666
|
if (entry?.allow && subject && matchesTrust(entry.allow, subject)) {
|
|
29791
|
-
add(
|
|
30667
|
+
add(
|
|
30668
|
+
"trust allow",
|
|
30669
|
+
true,
|
|
30670
|
+
"auto",
|
|
30671
|
+
"trust",
|
|
30672
|
+
`subject "${subject}" matched an allow pattern in trust file`
|
|
30673
|
+
);
|
|
29792
30674
|
winnerIndex = steps.length - 1;
|
|
29793
|
-
return {
|
|
30675
|
+
return {
|
|
30676
|
+
toolName: tool.name,
|
|
30677
|
+
subject,
|
|
30678
|
+
steps,
|
|
30679
|
+
winnerIndex,
|
|
30680
|
+
decision: { permission: "auto", source: "trust", reason: "matched allow pattern" }
|
|
30681
|
+
};
|
|
29794
30682
|
}
|
|
29795
|
-
add(
|
|
30683
|
+
add(
|
|
30684
|
+
"trust allow",
|
|
30685
|
+
false,
|
|
30686
|
+
"auto",
|
|
30687
|
+
"trust",
|
|
30688
|
+
`no allow pattern matched (namespace: ${namespaceSource})`
|
|
30689
|
+
);
|
|
29796
30690
|
if (entry?.auto) {
|
|
29797
30691
|
add("trust auto", true, "auto", "trust", `trust file has auto: true for "${tool.name}"`);
|
|
29798
30692
|
winnerIndex = steps.length - 1;
|
|
29799
|
-
return {
|
|
30693
|
+
return {
|
|
30694
|
+
toolName: tool.name,
|
|
30695
|
+
subject,
|
|
30696
|
+
steps,
|
|
30697
|
+
winnerIndex,
|
|
30698
|
+
decision: { permission: "auto", source: "trust", reason: "trust auto" }
|
|
30699
|
+
};
|
|
29800
30700
|
}
|
|
29801
30701
|
add("trust auto", false, "auto", "trust", `no auto flag for "${tool.name}" in trust file`);
|
|
29802
30702
|
if (!this.yolo && this.isSensitiveReadCall(tool, input)) {
|
|
@@ -29809,13 +30709,64 @@ var DefaultPermissionPolicy = class {
|
|
|
29809
30709
|
hasDelegate2 ? "sensitive file read detected \u2014 would prompt user for approval" : "sensitive file read detected \u2014 returns confirm (no prompt delegate)"
|
|
29810
30710
|
);
|
|
29811
30711
|
winnerIndex = steps.length - 1;
|
|
29812
|
-
return {
|
|
30712
|
+
return {
|
|
30713
|
+
toolName: tool.name,
|
|
30714
|
+
subject,
|
|
30715
|
+
steps,
|
|
30716
|
+
winnerIndex,
|
|
30717
|
+
decision: {
|
|
30718
|
+
permission: "confirm",
|
|
30719
|
+
source: "default",
|
|
30720
|
+
riskTier: "standard",
|
|
30721
|
+
reason: "sensitive file read needs explicit approval"
|
|
30722
|
+
}
|
|
30723
|
+
};
|
|
29813
30724
|
}
|
|
29814
|
-
add(
|
|
30725
|
+
add(
|
|
30726
|
+
"sensitive read",
|
|
30727
|
+
false,
|
|
30728
|
+
"confirm",
|
|
30729
|
+
"default",
|
|
30730
|
+
"not a sensitive read call (or YOLO bypasses this check)"
|
|
30731
|
+
);
|
|
29815
30732
|
if (this.yolo) {
|
|
29816
|
-
|
|
30733
|
+
if (this.yoloBlockedAsDestructive(tool, input, ctx)) {
|
|
30734
|
+
add(
|
|
30735
|
+
"yolo destructive gate",
|
|
30736
|
+
true,
|
|
30737
|
+
"confirm",
|
|
30738
|
+
"yolo_destructive",
|
|
30739
|
+
"YOLO is active but this is a clearly destructive command \u2014 still asking"
|
|
30740
|
+
);
|
|
30741
|
+
winnerIndex = steps.length - 1;
|
|
30742
|
+
return {
|
|
30743
|
+
toolName: tool.name,
|
|
30744
|
+
subject,
|
|
30745
|
+
steps,
|
|
30746
|
+
winnerIndex,
|
|
30747
|
+
decision: {
|
|
30748
|
+
permission: "confirm",
|
|
30749
|
+
source: "yolo_destructive",
|
|
30750
|
+
riskTier: "destructive",
|
|
30751
|
+
reason: "destructive command needs explicit approval even in YOLO mode"
|
|
30752
|
+
}
|
|
30753
|
+
};
|
|
30754
|
+
}
|
|
30755
|
+
add(
|
|
30756
|
+
"yolo",
|
|
30757
|
+
true,
|
|
30758
|
+
"auto",
|
|
30759
|
+
"yolo",
|
|
30760
|
+
"YOLO mode is active \u2014 auto-approving every non-denied call"
|
|
30761
|
+
);
|
|
29817
30762
|
winnerIndex = steps.length - 1;
|
|
29818
|
-
return {
|
|
30763
|
+
return {
|
|
30764
|
+
toolName: tool.name,
|
|
30765
|
+
subject,
|
|
30766
|
+
steps,
|
|
30767
|
+
winnerIndex,
|
|
30768
|
+
decision: { permission: "auto", source: "yolo" }
|
|
30769
|
+
};
|
|
29819
30770
|
}
|
|
29820
30771
|
add("yolo", false, "auto", "yolo", "YOLO mode is not active");
|
|
29821
30772
|
if (tool.name === "write" && subject) {
|
|
@@ -29829,10 +30780,26 @@ var DefaultPermissionPolicy = class {
|
|
|
29829
30780
|
);
|
|
29830
30781
|
if (hasRead) {
|
|
29831
30782
|
winnerIndex = steps.length - 1;
|
|
29832
|
-
return {
|
|
30783
|
+
return {
|
|
30784
|
+
toolName: tool.name,
|
|
30785
|
+
subject,
|
|
30786
|
+
steps,
|
|
30787
|
+
winnerIndex,
|
|
30788
|
+
decision: {
|
|
30789
|
+
permission: "auto",
|
|
30790
|
+
source: "context",
|
|
30791
|
+
reason: "file already read in this session"
|
|
30792
|
+
}
|
|
30793
|
+
};
|
|
29833
30794
|
}
|
|
29834
30795
|
} else {
|
|
29835
|
-
add(
|
|
30796
|
+
add(
|
|
30797
|
+
"write smart bypass",
|
|
30798
|
+
false,
|
|
30799
|
+
"auto",
|
|
30800
|
+
"context",
|
|
30801
|
+
'tool is not "write" or has no subject \u2014 bypass does not apply'
|
|
30802
|
+
);
|
|
29836
30803
|
}
|
|
29837
30804
|
const hasWriteCap = hasCapability(tool, ToolCapabilities.FS_WRITE);
|
|
29838
30805
|
const hasShellCap = hasCapability(tool, [
|
|
@@ -29845,9 +30812,21 @@ var DefaultPermissionPolicy = class {
|
|
|
29845
30812
|
const hasSubagentCap = hasCapability(tool, ToolCapabilities.SUBAGENT_SPAWN);
|
|
29846
30813
|
const isMutating = tool.mutating || hasWriteCap || hasShellCap || hasInstallCap || hasConfigCap || hasSubagentCap;
|
|
29847
30814
|
if (tool.permission === "auto" && !isMutating) {
|
|
29848
|
-
add(
|
|
30815
|
+
add(
|
|
30816
|
+
"safe default auto",
|
|
30817
|
+
true,
|
|
30818
|
+
"auto",
|
|
30819
|
+
"default",
|
|
30820
|
+
`tool "${tool.name}" has auto permission and is not mutating \u2014 safe to auto-approve`
|
|
30821
|
+
);
|
|
29849
30822
|
winnerIndex = steps.length - 1;
|
|
29850
|
-
return {
|
|
30823
|
+
return {
|
|
30824
|
+
toolName: tool.name,
|
|
30825
|
+
subject,
|
|
30826
|
+
steps,
|
|
30827
|
+
winnerIndex,
|
|
30828
|
+
decision: { permission: "auto", source: "default" }
|
|
30829
|
+
};
|
|
29851
30830
|
}
|
|
29852
30831
|
add(
|
|
29853
30832
|
"mutating default confirm",
|
|
@@ -29877,71 +30856,12 @@ var DefaultPermissionPolicy = class {
|
|
|
29877
30856
|
return void 0;
|
|
29878
30857
|
}
|
|
29879
30858
|
};
|
|
29880
|
-
var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
29881
|
-
allowedCapabilities;
|
|
29882
|
-
constructor(allowedCapabilities) {
|
|
29883
|
-
this.allowedCapabilities = allowedCapabilities ?? [
|
|
29884
|
-
ToolCapabilities.FS_READ,
|
|
29885
|
-
ToolCapabilities.NET_OUTBOUND
|
|
29886
|
-
];
|
|
29887
|
-
}
|
|
29888
|
-
static isMcpTool(name) {
|
|
29889
|
-
return name.startsWith("mcp__");
|
|
29890
|
-
}
|
|
29891
|
-
async evaluate(tool) {
|
|
29892
|
-
const caps = tool.capabilities ?? [];
|
|
29893
|
-
const hasAllowedCap = caps.some((c) => this.allowedCapabilities.includes(c));
|
|
29894
|
-
const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
29895
|
-
const mcpProxyAllowed = this.allowedCapabilities.includes(ToolCapabilities.MCP_PROXY);
|
|
29896
|
-
const dangerousNotAllowed = getDangerousCapabilities(tool).filter(
|
|
29897
|
-
(c) => !this.allowedCapabilities.includes(c)
|
|
29898
|
-
);
|
|
29899
|
-
const blocked = tool.permission === "deny" || isMcp && !mcpProxyAllowed || !hasAllowedCap || dangerousNotAllowed.length > 0;
|
|
29900
|
-
if (blocked) {
|
|
29901
|
-
const reason = isMcp && !mcpProxyAllowed ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow mcp.proxy explicitly` : tool.permission === "deny" ? "tool default deny" : dangerousNotAllowed.length > 0 ? `tool requires un-granted dangerous capability (needs: ${dangerousNotAllowed.join(", ")}, allowed: ${this.allowedCapabilities.join(", ")})` : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
|
|
29902
|
-
return {
|
|
29903
|
-
permission: "deny",
|
|
29904
|
-
source: "subagent_guard",
|
|
29905
|
-
reason
|
|
29906
|
-
};
|
|
29907
|
-
}
|
|
29908
|
-
return { permission: "auto", source: "yolo" };
|
|
29909
|
-
}
|
|
29910
|
-
async trust() {
|
|
29911
|
-
}
|
|
29912
|
-
async deny() {
|
|
29913
|
-
}
|
|
29914
|
-
denyOnce() {
|
|
29915
|
-
}
|
|
29916
|
-
allowOnce() {
|
|
29917
|
-
}
|
|
29918
|
-
async explain(tool) {
|
|
29919
|
-
const decision = await this.evaluate(tool);
|
|
29920
|
-
return {
|
|
29921
|
-
toolName: tool.name,
|
|
29922
|
-
subject: null,
|
|
29923
|
-
steps: [
|
|
29924
|
-
{
|
|
29925
|
-
rule: "subagent auto",
|
|
29926
|
-
matched: decision.permission === "auto",
|
|
29927
|
-
decision: decision.permission,
|
|
29928
|
-
source: decision.source,
|
|
29929
|
-
detail: decision.reason ?? `subagent policy: ${decision.permission}`
|
|
29930
|
-
}
|
|
29931
|
-
],
|
|
29932
|
-
winnerIndex: 0,
|
|
29933
|
-
decision
|
|
29934
|
-
};
|
|
29935
|
-
}
|
|
29936
|
-
async reload() {
|
|
29937
|
-
}
|
|
29938
|
-
};
|
|
29939
30859
|
|
|
29940
30860
|
// src/security/secret-vault.ts
|
|
29941
30861
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, scryptSync } from "node:crypto";
|
|
29942
30862
|
import * as fs13 from "node:fs";
|
|
29943
30863
|
import * as fsp22 from "node:fs/promises";
|
|
29944
|
-
import * as
|
|
30864
|
+
import * as path35 from "node:path";
|
|
29945
30865
|
|
|
29946
30866
|
// src/types/secret-vault.ts
|
|
29947
30867
|
var ENCRYPTED_PREFIX_PATTERN = /^enc:v(\d+):/;
|
|
@@ -30084,8 +31004,12 @@ function checkKeyFilePermissions(keyFile, opts) {
|
|
|
30084
31004
|
const stat15 = fs13.statSync(keyFile);
|
|
30085
31005
|
const actualMode = stat15.mode & 511;
|
|
30086
31006
|
if (actualMode !== KEY_FILE_MODE) {
|
|
31007
|
+
void restrictFilePermissions(keyFile, {
|
|
31008
|
+
label: "secret-vault",
|
|
31009
|
+
warn
|
|
31010
|
+
}).catch(() => void 0);
|
|
30087
31011
|
warn(
|
|
30088
|
-
`Key file ${keyFile} has mode ${actualMode.toString(8)} \u2014 expected ${KEY_FILE_MODE.toString(8)}.
|
|
31012
|
+
`Key file ${keyFile} has mode ${actualMode.toString(8)} \u2014 expected ${KEY_FILE_MODE.toString(8)}. Hardening\u2026`
|
|
30089
31013
|
);
|
|
30090
31014
|
}
|
|
30091
31015
|
} catch {
|
|
@@ -30118,6 +31042,14 @@ var DefaultSecretVault = class {
|
|
|
30118
31042
|
logger;
|
|
30119
31043
|
key;
|
|
30120
31044
|
_keyVersion = 1;
|
|
31045
|
+
/**
|
|
31046
|
+
* Pending key-file hardening promises (Windows ACL restrictions via icacls).
|
|
31047
|
+
* Tracked so async callers can `flushHardening()` before returning, ensuring
|
|
31048
|
+
* a short-lived CLI process does not exit before the hardening resolves.
|
|
31049
|
+
* Sync-only callers (encrypt/decrypt lazy-load) leave these pending —
|
|
31050
|
+
* hardening remains best-effort in that case.
|
|
31051
|
+
*/
|
|
31052
|
+
pendingHardening = [];
|
|
30121
31053
|
constructor(opts) {
|
|
30122
31054
|
this.keyFile = opts.keyFile;
|
|
30123
31055
|
this.logger = opts.logger;
|
|
@@ -30134,6 +31066,26 @@ var DefaultSecretVault = class {
|
|
|
30134
31066
|
console.warn(JSON.stringify({ ...ctx, message: msg, timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
30135
31067
|
}
|
|
30136
31068
|
}
|
|
31069
|
+
/**
|
|
31070
|
+
* Schedule best-effort key-file hardening and track the promise so async
|
|
31071
|
+
* callers can await it via `flushHardening()` before returning. Rejections
|
|
31072
|
+
* are swallowed here (the helper already warns on failure) so a rejection
|
|
31073
|
+
* can never become an unhandled one.
|
|
31074
|
+
*/
|
|
31075
|
+
scheduleKeyHardening() {
|
|
31076
|
+
const p = restrictFilePermissions(this.keyFile, {
|
|
31077
|
+
label: "secret-vault-key",
|
|
31078
|
+
warn: (msg) => this.logWarn(msg)
|
|
31079
|
+
}).catch(() => void 0);
|
|
31080
|
+
this.pendingHardening.push(p);
|
|
31081
|
+
}
|
|
31082
|
+
/** Flush all pending key-file hardening promises. */
|
|
31083
|
+
flushHardening() {
|
|
31084
|
+
if (this.pendingHardening.length === 0) return Promise.resolve();
|
|
31085
|
+
const all = Promise.all(this.pendingHardening).then(() => void 0);
|
|
31086
|
+
this.pendingHardening = [];
|
|
31087
|
+
return all;
|
|
31088
|
+
}
|
|
30137
31089
|
/** Current key version. Starts at 1; incremented by rotateKey(). */
|
|
30138
31090
|
get keyVersion() {
|
|
30139
31091
|
if (!this.key) this.loadOrCreateKey();
|
|
@@ -30175,16 +31127,18 @@ var DefaultSecretVault = class {
|
|
|
30175
31127
|
const iv = Buffer.from(ivB64, "base64");
|
|
30176
31128
|
const tag = Buffer.from(tagB64, "base64");
|
|
30177
31129
|
const ct = Buffer.from(ctB64, "base64");
|
|
30178
|
-
if (iv.length !== IV_BYTES)
|
|
30179
|
-
|
|
30180
|
-
|
|
30181
|
-
|
|
30182
|
-
|
|
30183
|
-
|
|
30184
|
-
|
|
30185
|
-
|
|
30186
|
-
|
|
30187
|
-
|
|
31130
|
+
if (iv.length !== IV_BYTES)
|
|
31131
|
+
throw new ConfigError({
|
|
31132
|
+
message: "SecretVault: bad IV length",
|
|
31133
|
+
code: ERROR_CODES.CONFIG_PARSE_FAILED,
|
|
31134
|
+
context: { expected: IV_BYTES, actual: iv.length }
|
|
31135
|
+
});
|
|
31136
|
+
if (tag.length !== TAG_BYTES)
|
|
31137
|
+
throw new ConfigError({
|
|
31138
|
+
message: "SecretVault: bad tag length",
|
|
31139
|
+
code: ERROR_CODES.CONFIG_PARSE_FAILED,
|
|
31140
|
+
context: { expected: TAG_BYTES, actual: tag.length }
|
|
31141
|
+
});
|
|
30188
31142
|
const key = this.loadOrCreateKey();
|
|
30189
31143
|
const decipher = createDecipheriv(ALGO, key, iv);
|
|
30190
31144
|
decipher.setAuthTag(tag);
|
|
@@ -30200,7 +31154,7 @@ var DefaultSecretVault = class {
|
|
|
30200
31154
|
const oldVersion = this._keyVersion;
|
|
30201
31155
|
const newKey = randomBytes3(KEY_BYTES);
|
|
30202
31156
|
const newVersion = oldVersion + 1;
|
|
30203
|
-
fs13.mkdirSync(
|
|
31157
|
+
fs13.mkdirSync(path35.dirname(this.keyFile), { recursive: true });
|
|
30204
31158
|
const passphrase = getVaultPassphrase();
|
|
30205
31159
|
if (passphrase) {
|
|
30206
31160
|
writeKeyFileAtomicSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase));
|
|
@@ -30211,6 +31165,7 @@ var DefaultSecretVault = class {
|
|
|
30211
31165
|
newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
|
|
30212
31166
|
writeKeyFileAtomicSync(this.keyFile, keyFileBuf);
|
|
30213
31167
|
}
|
|
31168
|
+
this.scheduleKeyHardening();
|
|
30214
31169
|
checkKeyFilePermissions(this.keyFile, { warn: (msg) => this.logWarn(msg) });
|
|
30215
31170
|
this.key = newKey;
|
|
30216
31171
|
this._keyVersion = newVersion;
|
|
@@ -30227,7 +31182,11 @@ var DefaultSecretVault = class {
|
|
|
30227
31182
|
const passphrase = getVaultPassphrase();
|
|
30228
31183
|
if (!passphrase || !this.key) return;
|
|
30229
31184
|
try {
|
|
30230
|
-
writeKeyFileAtomicSync(
|
|
31185
|
+
writeKeyFileAtomicSync(
|
|
31186
|
+
this.keyFile,
|
|
31187
|
+
wrapDataKey(this.key, this._keyVersion, passphrase)
|
|
31188
|
+
);
|
|
31189
|
+
this.scheduleKeyHardening();
|
|
30231
31190
|
checkKeyFilePermissions(this.keyFile, { warn: (msg) => this.logWarn(msg) });
|
|
30232
31191
|
} catch {
|
|
30233
31192
|
}
|
|
@@ -30282,14 +31241,16 @@ var DefaultSecretVault = class {
|
|
|
30282
31241
|
} catch (err) {
|
|
30283
31242
|
if (err.code !== "ENOENT") throw err;
|
|
30284
31243
|
}
|
|
30285
|
-
fs13.mkdirSync(
|
|
31244
|
+
fs13.mkdirSync(path35.dirname(this.keyFile), { recursive: true });
|
|
30286
31245
|
const key = randomBytes3(KEY_BYTES);
|
|
30287
31246
|
const passphrase = getVaultPassphrase();
|
|
30288
31247
|
const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
|
|
30289
31248
|
try {
|
|
30290
31249
|
fs13.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
|
|
31250
|
+
this.scheduleKeyHardening();
|
|
30291
31251
|
} catch (err) {
|
|
30292
31252
|
if (err.code !== "EEXIST") throw err;
|
|
31253
|
+
this.scheduleKeyHardening();
|
|
30293
31254
|
const buf = fs13.readFileSync(this.keyFile);
|
|
30294
31255
|
if (isWrappedKeyFile(buf)) {
|
|
30295
31256
|
const { key: winnerKey, version } = unwrapDataKey(buf, this.keyFile);
|
|
@@ -30340,9 +31301,10 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
|
30340
31301
|
}
|
|
30341
31302
|
const merged = deepMerge(current, patch ?? {});
|
|
30342
31303
|
const encrypted = encryptConfigSecrets(merged, vault);
|
|
30343
|
-
await fsp22.mkdir(
|
|
31304
|
+
await fsp22.mkdir(path35.dirname(configPath), { recursive: true });
|
|
30344
31305
|
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
30345
|
-
await
|
|
31306
|
+
await restrictFilePermissions2(configPath);
|
|
31307
|
+
await vault.flushHardening?.();
|
|
30346
31308
|
}
|
|
30347
31309
|
async function migratePlaintextSecrets(configPath, vault, logger) {
|
|
30348
31310
|
let raw;
|
|
@@ -30361,45 +31323,15 @@ async function migratePlaintextSecrets(configPath, vault, logger) {
|
|
|
30361
31323
|
const migrated = walkCount(parsed, vault, counter);
|
|
30362
31324
|
if (counter.n === 0) return { migrated: 0, file: configPath };
|
|
30363
31325
|
await atomicWrite(configPath, JSON.stringify(migrated, null, 2), { mode: 384 });
|
|
30364
|
-
await
|
|
31326
|
+
await restrictFilePermissions2(
|
|
30365
31327
|
configPath,
|
|
30366
31328
|
logger ? { warn: (msg) => logger.warn(msg) } : void 0
|
|
30367
31329
|
);
|
|
31330
|
+
await vault.flushHardening?.();
|
|
30368
31331
|
return { migrated: counter.n, file: configPath };
|
|
30369
31332
|
}
|
|
30370
|
-
async function
|
|
30371
|
-
|
|
30372
|
-
if (process.platform === "win32") {
|
|
30373
|
-
try {
|
|
30374
|
-
const { execFile: execFile2 } = await import("node:child_process");
|
|
30375
|
-
const { promisify: promisify2 } = await import("node:util");
|
|
30376
|
-
const execFileAsync = promisify2(execFile2);
|
|
30377
|
-
const user = windowsAccountName();
|
|
30378
|
-
if (!user) {
|
|
30379
|
-
warn(
|
|
30380
|
-
`[secret-vault] Could not determine the current Windows user for ${filePath}; skipping icacls hardening.`
|
|
30381
|
-
);
|
|
30382
|
-
return;
|
|
30383
|
-
}
|
|
30384
|
-
await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${user}:(F)`]);
|
|
30385
|
-
} catch {
|
|
30386
|
-
warn(
|
|
30387
|
-
`[secret-vault] Could not restrict permissions on ${filePath} \u2014 config file may be readable by other users on this system.`
|
|
30388
|
-
);
|
|
30389
|
-
}
|
|
30390
|
-
} else {
|
|
30391
|
-
try {
|
|
30392
|
-
await fsp22.chmod(filePath, 384);
|
|
30393
|
-
} catch {
|
|
30394
|
-
}
|
|
30395
|
-
}
|
|
30396
|
-
}
|
|
30397
|
-
function windowsAccountName() {
|
|
30398
|
-
const username = process.env.USERNAME || process.env.USER;
|
|
30399
|
-
if (!username || username.includes("\0")) return void 0;
|
|
30400
|
-
const domain = process.env.USERDOMAIN;
|
|
30401
|
-
if (domain && !domain.includes("\0")) return `${domain}\\${username}`;
|
|
30402
|
-
return username;
|
|
31333
|
+
async function restrictFilePermissions2(filePath, opts) {
|
|
31334
|
+
await restrictFilePermissions(filePath, { label: "secret-vault", warn: opts?.warn });
|
|
30403
31335
|
}
|
|
30404
31336
|
function walkCount(node, vault, counter) {
|
|
30405
31337
|
if (node === null || node === void 0) return node;
|
|
@@ -30424,7 +31356,7 @@ function walkCount(node, vault, counter) {
|
|
|
30424
31356
|
// src/storage/attachment-store.ts
|
|
30425
31357
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
30426
31358
|
import * as fsp23 from "node:fs/promises";
|
|
30427
|
-
import * as
|
|
31359
|
+
import * as path36 from "node:path";
|
|
30428
31360
|
var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
|
|
30429
31361
|
var PLACEHOLDER_RE = /\[(pasted|image|file) #(\d+)[^\]]*\]|\[file:([^\]]+)\]/g;
|
|
30430
31362
|
var DefaultAttachmentStore = class {
|
|
@@ -30445,7 +31377,7 @@ var DefaultAttachmentStore = class {
|
|
|
30445
31377
|
let data = input.data;
|
|
30446
31378
|
if (this.spoolDir && bytes >= this.spoolThreshold) {
|
|
30447
31379
|
await fsp23.mkdir(this.spoolDir, { recursive: true });
|
|
30448
|
-
spooledPath =
|
|
31380
|
+
spooledPath = path36.join(this.spoolDir, `${id}.bin`);
|
|
30449
31381
|
await atomicWrite(spooledPath, input.data, {
|
|
30450
31382
|
encoding: input.kind === "image" ? "base64" : "utf8"
|
|
30451
31383
|
});
|
|
@@ -30614,7 +31546,13 @@ var CONFIG_BEHAVIOR_DEFAULTS = {
|
|
|
30614
31546
|
// models still get cost savings without capability loss. Explicit tiers
|
|
30615
31547
|
// are respected verbatim.
|
|
30616
31548
|
tokenSavingMode: "auto",
|
|
30617
|
-
|
|
31549
|
+
// Derived, never written by hand: this and `tools.restrictToProjectRoot`
|
|
31550
|
+
// (line 41) are the two halves of one switch, and they used to be declared
|
|
31551
|
+
// independently in this same object — one `true`, one `true` — with
|
|
31552
|
+
// session.ts resolving the contradiction via `??` in favour of the
|
|
31553
|
+
// permissive half. The confinement guard therefore shipped disabled on a
|
|
31554
|
+
// fresh install even though the other half asked for it (WS-075).
|
|
31555
|
+
allowOutsideProjectRoot: !DEFAULT_TOOLS_CONFIG.restrictToProjectRoot
|
|
30618
31556
|
},
|
|
30619
31557
|
Sage: {
|
|
30620
31558
|
enabled: true,
|
|
@@ -30693,9 +31631,11 @@ var CONFIG_BEHAVIOR_DEFAULTS = {
|
|
|
30693
31631
|
enhance: true,
|
|
30694
31632
|
enhanceDelayMs: 6e4,
|
|
30695
31633
|
enhanceLanguage: "original",
|
|
30696
|
-
|
|
31634
|
+
// Product-wide statusline density default. Mirrored by the TUI's
|
|
31635
|
+
// DEFAULT_STATUSLINE_MODE (packages/tui/src/components/settings-picker-model.ts).
|
|
31636
|
+
statuslineMode: "minimum",
|
|
30697
31637
|
thinkingWord: DEFAULT_TUI_THINKING_WORD,
|
|
30698
|
-
showAgentSwarmPanel:
|
|
31638
|
+
showAgentSwarmPanel: "bottom"
|
|
30699
31639
|
},
|
|
30700
31640
|
circuitBreaker: { ...DEFAULT_CIRCUIT_BREAKER_CONFIG },
|
|
30701
31641
|
modelRuntime: {
|
|
@@ -30822,6 +31762,7 @@ var IN_PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
30822
31762
|
"maxConcurrent",
|
|
30823
31763
|
"uiLocale",
|
|
30824
31764
|
"fallbackModels",
|
|
31765
|
+
"fallbackBridge",
|
|
30825
31766
|
"fallbackProfiles",
|
|
30826
31767
|
"favoriteModels",
|
|
30827
31768
|
"favoriteModelsOnly",
|
|
@@ -30884,6 +31825,7 @@ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
30884
31825
|
"tools",
|
|
30885
31826
|
"mcpServers",
|
|
30886
31827
|
"fallbackModels",
|
|
31828
|
+
"fallbackBridge",
|
|
30887
31829
|
"fallbackProfiles",
|
|
30888
31830
|
"favoriteModels",
|
|
30889
31831
|
"favoriteModelsOnly",
|
|
@@ -30918,6 +31860,75 @@ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
30918
31860
|
"brain",
|
|
30919
31861
|
"git"
|
|
30920
31862
|
]);
|
|
31863
|
+
var IN_PROJECT_DENIED_PATHS = [
|
|
31864
|
+
{
|
|
31865
|
+
path: "tools.exec.allow",
|
|
31866
|
+
reason: "Extends the exec allow-list; a repo could authorise its own binaries."
|
|
31867
|
+
},
|
|
31868
|
+
{ path: "tools.exec.danger", reason: "Weakens the destructive-command banner." },
|
|
31869
|
+
{ path: "skills.extraDirs", reason: "Loads skill definitions from repo-chosen directories." },
|
|
31870
|
+
{ path: "skills.registryUrl", reason: "Redirects skill installs to a repo-chosen host." },
|
|
31871
|
+
// Deliberately NOT denied: skills.mode and skills.eagerMaxChars. The audit
|
|
31872
|
+
// suggested stripping both, but config-loader-extra.test.ts classifies `mode`
|
|
31873
|
+
// as a safe preference with a stated rationale, and a repo can only use these
|
|
31874
|
+
// to move injection volume in either direction — the trust problem was that
|
|
31875
|
+
// repo-supplied skill bodies were indistinguishable from the user's own, which
|
|
31876
|
+
// the provenance tag in system-prompt-skill-bodies.ts now fixes. Re-classifying
|
|
31877
|
+
// them is a product call.
|
|
31878
|
+
{ path: "Sage.storage.directory", reason: "Redirects memory storage outside the profile." },
|
|
31879
|
+
{
|
|
31880
|
+
path: "autonomy.yolo",
|
|
31881
|
+
reason: "Alias for the denied top-level `yolo`, and it wins over the user setting \u2014 a repo-committed config could disable every permission prompt."
|
|
31882
|
+
},
|
|
31883
|
+
{
|
|
31884
|
+
path: "autonomy.defaultMode",
|
|
31885
|
+
reason: "Sets the startup autonomy mode; autonomy is user-owned, never repo-owned."
|
|
31886
|
+
},
|
|
31887
|
+
// Deliberately NOT denied: autonomy.autoProceedDelayMs and
|
|
31888
|
+
// autoProceedMaxIterations. They tune a mode the user has already switched on
|
|
31889
|
+
// rather than granting it, and config-loader-extra.test.ts classifies the
|
|
31890
|
+
// delay as a benign project preference. Re-classifying them is a product call.
|
|
31891
|
+
{
|
|
31892
|
+
path: "launch.autonomy",
|
|
31893
|
+
reason: "Launch-time autonomy mode; same user-owned boundary as autonomy.defaultMode."
|
|
31894
|
+
},
|
|
31895
|
+
{
|
|
31896
|
+
path: "features.allowOutsideProjectRoot",
|
|
31897
|
+
reason: "Short-circuits both the project-root containment check and the symlink realpath check in the file tools."
|
|
31898
|
+
},
|
|
31899
|
+
{
|
|
31900
|
+
path: "tools.restrictToProjectRoot",
|
|
31901
|
+
reason: "The other half of the filesystem confinement switch."
|
|
31902
|
+
}
|
|
31903
|
+
];
|
|
31904
|
+
function deleteNestedPath(target, path40) {
|
|
31905
|
+
const segments = path40.split(".");
|
|
31906
|
+
const last = segments[segments.length - 1];
|
|
31907
|
+
if (last === void 0) return false;
|
|
31908
|
+
let cursor = target;
|
|
31909
|
+
const chain = [];
|
|
31910
|
+
for (const segment of segments.slice(0, -1)) {
|
|
31911
|
+
const next = cursor[segment];
|
|
31912
|
+
if (!next || typeof next !== "object" || Array.isArray(next)) return false;
|
|
31913
|
+
chain.push({ parent: cursor, key: segment });
|
|
31914
|
+
cursor = next;
|
|
31915
|
+
}
|
|
31916
|
+
if (!(last in cursor)) return false;
|
|
31917
|
+
const clonedLeaf = { ...cursor };
|
|
31918
|
+
delete clonedLeaf[last];
|
|
31919
|
+
let replacement = clonedLeaf;
|
|
31920
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
31921
|
+
const link2 = chain[i];
|
|
31922
|
+
if (!link2) continue;
|
|
31923
|
+
replacement = { ...link2.parent, [link2.key]: replacement };
|
|
31924
|
+
if (i === 0) {
|
|
31925
|
+
for (const [k, v] of Object.entries(replacement)) target[k] = v;
|
|
31926
|
+
return true;
|
|
31927
|
+
}
|
|
31928
|
+
}
|
|
31929
|
+
for (const [k, v] of Object.entries(replacement)) target[k] = v;
|
|
31930
|
+
return true;
|
|
31931
|
+
}
|
|
30921
31932
|
function assertInProjectAllowListComplete() {
|
|
30922
31933
|
const missingFromBoth = [];
|
|
30923
31934
|
for (const key of KNOWN_CONFIG_TOP_LEVEL_KEYS) {
|
|
@@ -30947,6 +31958,22 @@ function assertInProjectAllowListComplete() {
|
|
|
30947
31958
|
`field(s) appear in BOTH IN_PROJECT_ALLOWED_KEYS and KNOWN_DENIED_IN_PROJECT: ` + duplicate.join(", ") + ". The allow-list wins at runtime; remove from one of the two."
|
|
30948
31959
|
);
|
|
30949
31960
|
}
|
|
31961
|
+
const orphanedPaths = IN_PROJECT_DENIED_PATHS.filter(
|
|
31962
|
+
({ path: path40 }) => !IN_PROJECT_ALLOWED_KEYS.has(path40.split(".")[0] ?? "")
|
|
31963
|
+
).map(({ path: path40 }) => path40);
|
|
31964
|
+
if (orphanedPaths.length > 0) {
|
|
31965
|
+
problems.push(
|
|
31966
|
+
`IN_PROJECT_DENIED_PATHS entr(ies) whose top-level parent is not allowed: ` + orphanedPaths.join(", ") + ". The parent is already stripped wholesale, so the nested denial is dead \u2014 remove it."
|
|
31967
|
+
);
|
|
31968
|
+
}
|
|
31969
|
+
const malformedPaths = IN_PROJECT_DENIED_PATHS.filter(
|
|
31970
|
+
({ path: path40 }) => path40.split(".").length < 2 || path40.split(".").some((s) => s.length === 0)
|
|
31971
|
+
).map(({ path: path40 }) => path40);
|
|
31972
|
+
if (malformedPaths.length > 0) {
|
|
31973
|
+
problems.push(
|
|
31974
|
+
`IN_PROJECT_DENIED_PATHS entr(ies) are not dotted nested paths: ` + malformedPaths.join(", ") + ". Top-level keys belong in KNOWN_DENIED_IN_PROJECT instead."
|
|
31975
|
+
);
|
|
31976
|
+
}
|
|
30950
31977
|
if (problems.length > 0) {
|
|
30951
31978
|
throw new Error(
|
|
30952
31979
|
`stripUnsafeInProjectFields drift check failed:
|
|
@@ -30969,58 +31996,8 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
|
|
|
30969
31996
|
}
|
|
30970
31997
|
stripped.push(k);
|
|
30971
31998
|
}
|
|
30972
|
-
const
|
|
30973
|
-
|
|
30974
|
-
const execCfg = outTools["exec"];
|
|
30975
|
-
if (execCfg && typeof execCfg === "object") {
|
|
30976
|
-
const hasAllow = "allow" in execCfg;
|
|
30977
|
-
const hasDanger = "danger" in execCfg;
|
|
30978
|
-
if (hasAllow || hasDanger) {
|
|
30979
|
-
const clonedExec = { ...execCfg };
|
|
30980
|
-
if (hasAllow) {
|
|
30981
|
-
delete clonedExec["allow"];
|
|
30982
|
-
stripped.push("tools.exec.allow");
|
|
30983
|
-
}
|
|
30984
|
-
if (hasDanger) {
|
|
30985
|
-
delete clonedExec["danger"];
|
|
30986
|
-
stripped.push("tools.exec.danger");
|
|
30987
|
-
}
|
|
30988
|
-
out["tools"] = {
|
|
30989
|
-
...outTools,
|
|
30990
|
-
exec: clonedExec
|
|
30991
|
-
};
|
|
30992
|
-
}
|
|
30993
|
-
}
|
|
30994
|
-
}
|
|
30995
|
-
const outSkills = out["skills"];
|
|
30996
|
-
if (outSkills && typeof outSkills === "object") {
|
|
30997
|
-
const skillsRec = outSkills;
|
|
30998
|
-
const needsClone = "extraDirs" in skillsRec || "registryUrl" in skillsRec;
|
|
30999
|
-
if (needsClone) {
|
|
31000
|
-
const clonedSkills = { ...skillsRec };
|
|
31001
|
-
if ("extraDirs" in clonedSkills) {
|
|
31002
|
-
delete clonedSkills["extraDirs"];
|
|
31003
|
-
stripped.push("skills.extraDirs");
|
|
31004
|
-
}
|
|
31005
|
-
if ("registryUrl" in clonedSkills) {
|
|
31006
|
-
delete clonedSkills["registryUrl"];
|
|
31007
|
-
stripped.push("skills.registryUrl");
|
|
31008
|
-
}
|
|
31009
|
-
out["skills"] = clonedSkills;
|
|
31010
|
-
}
|
|
31011
|
-
}
|
|
31012
|
-
const outSage = out["Sage"];
|
|
31013
|
-
if (outSage && typeof outSage === "object") {
|
|
31014
|
-
const storage3 = outSage["storage"];
|
|
31015
|
-
if (storage3 && typeof storage3 === "object" && "directory" in storage3) {
|
|
31016
|
-
const clonedStorage = { ...storage3 };
|
|
31017
|
-
delete clonedStorage["directory"];
|
|
31018
|
-
out["Sage"] = {
|
|
31019
|
-
...outSage,
|
|
31020
|
-
storage: clonedStorage
|
|
31021
|
-
};
|
|
31022
|
-
stripped.push("Sage.storage.directory");
|
|
31023
|
-
}
|
|
31999
|
+
for (const { path: path40 } of IN_PROJECT_DENIED_PATHS) {
|
|
32000
|
+
if (deleteNestedPath(out, path40)) stripped.push(path40);
|
|
31024
32001
|
}
|
|
31025
32002
|
if (stripped.length > 0) {
|
|
31026
32003
|
warn(
|
|
@@ -31190,10 +32167,10 @@ function removeLegacySageEngine(config) {
|
|
|
31190
32167
|
}
|
|
31191
32168
|
|
|
31192
32169
|
// src/storage/config-loader/path-identity.ts
|
|
31193
|
-
import * as
|
|
32170
|
+
import * as path37 from "node:path";
|
|
31194
32171
|
function samePath(a, b) {
|
|
31195
|
-
let ra =
|
|
31196
|
-
let rb =
|
|
32172
|
+
let ra = path37.resolve(a);
|
|
32173
|
+
let rb = path37.resolve(b);
|
|
31197
32174
|
if (process.platform === "win32" || process.platform === "darwin") {
|
|
31198
32175
|
ra = ra.toLowerCase();
|
|
31199
32176
|
rb = rb.toLowerCase();
|
|
@@ -32174,7 +33151,7 @@ ${cat}:`);
|
|
|
32174
33151
|
|
|
32175
33152
|
// src/storage/queue-store.ts
|
|
32176
33153
|
import * as fsp25 from "node:fs/promises";
|
|
32177
|
-
import * as
|
|
33154
|
+
import * as path38 from "node:path";
|
|
32178
33155
|
var QUEUE_MAX_ITEMS = 100;
|
|
32179
33156
|
var QUEUE_MAX_BYTES = 16 * 1024 * 1024;
|
|
32180
33157
|
var QUEUE_MAX_ITEM_BYTES = 8 * 1024 * 1024;
|
|
@@ -32207,7 +33184,7 @@ var QueueStore = class {
|
|
|
32207
33184
|
traceId;
|
|
32208
33185
|
logger;
|
|
32209
33186
|
constructor(opts) {
|
|
32210
|
-
this.file =
|
|
33187
|
+
this.file = path38.join(opts.dir, "queue.json");
|
|
32211
33188
|
this.events = opts.events;
|
|
32212
33189
|
this.traceId = opts.traceId;
|
|
32213
33190
|
this.logger = opts.logger;
|
|
@@ -32423,7 +33400,7 @@ function isPersistedQueueItem(v) {
|
|
|
32423
33400
|
// src/storage/recovery-lock.ts
|
|
32424
33401
|
import * as fsp26 from "node:fs/promises";
|
|
32425
33402
|
import * as os2 from "node:os";
|
|
32426
|
-
import * as
|
|
33403
|
+
import * as path39 from "node:path";
|
|
32427
33404
|
var LOCK_FILE = "active.json";
|
|
32428
33405
|
var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
32429
33406
|
var RecoveryLock = class {
|
|
@@ -32434,7 +33411,7 @@ var RecoveryLock = class {
|
|
|
32434
33411
|
sessionStore;
|
|
32435
33412
|
probe;
|
|
32436
33413
|
constructor(opts) {
|
|
32437
|
-
this.file =
|
|
33414
|
+
this.file = path39.join(opts.dir, LOCK_FILE);
|
|
32438
33415
|
this.pid = opts.pid ?? process.pid;
|
|
32439
33416
|
this.hostname = opts.hostname ?? os2.hostname();
|
|
32440
33417
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
@@ -32508,7 +33485,7 @@ var RecoveryLock = class {
|
|
|
32508
33485
|
* null return before calling this.
|
|
32509
33486
|
*/
|
|
32510
33487
|
async write(sessionId) {
|
|
32511
|
-
await ensureDir(
|
|
33488
|
+
await ensureDir(path39.dirname(this.file));
|
|
32512
33489
|
const lock = {
|
|
32513
33490
|
v: 1,
|
|
32514
33491
|
sessionId,
|
|
@@ -32756,6 +33733,9 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
|
32756
33733
|
async append(event) {
|
|
32757
33734
|
const target = resolveWriter();
|
|
32758
33735
|
if (!target) return;
|
|
33736
|
+
if (event.type === "tool_call_end") {
|
|
33737
|
+
progressCounters.delete(event.id);
|
|
33738
|
+
}
|
|
32759
33739
|
if (!isAllowed(event.type, currentLevel)) return;
|
|
32760
33740
|
if (!shouldSample(event)) return;
|
|
32761
33741
|
try {
|
|
@@ -32766,6 +33746,9 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
|
32766
33746
|
async appendBatch(events) {
|
|
32767
33747
|
const target = resolveWriter();
|
|
32768
33748
|
if (!target || events.length === 0) return;
|
|
33749
|
+
for (const e of events) {
|
|
33750
|
+
if (e.type === "tool_call_end") progressCounters.delete(e.id);
|
|
33751
|
+
}
|
|
32769
33752
|
const allowed = events.filter((e) => isAllowed(e.type, currentLevel) && shouldSample(e));
|
|
32770
33753
|
if (allowed.length === 0) return;
|
|
32771
33754
|
try {
|