@wrongstack/core 0.297.0 → 0.298.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +2081 -1102
- 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 +8518 -6416
- package/dist/infrastructure/index.js +198 -101
- 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 +950 -509
- package/dist/plugins/review-store-maintenance.d.ts +26 -0
- package/dist/plugins/review-types.d.ts +6 -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 +3007 -2032
- package/dist/security/permission-policy.d.ts +55 -3
- 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 +928 -732
- 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 +12 -3
- 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 +15 -12
- package/dist/utils/index.js +3083 -2801
- package/dist/utils/path-segment.d.ts +29 -0
- package/dist/utils/win32-cmd.d.ts +25 -0
- package/dist/worktree/index.js +5 -0
- package/package.json +8 -3
- package/skills/chimera/SKILL.md +16 -4
- 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
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/storage/session-store.ts", "../../src/security/secret-scrubber.ts", "../../src/utils/atomic-write.ts", "../../src/utils/error.ts", "../../src/types/errors.ts", "../../src/utils/child-env.ts", "../../src/utils/term.ts", "../../src/utils/color.ts", "../../src/utils/config-backup.ts", "../../src/utils/expect-defined.ts", "../../src/utils/message-invariants.ts", "../../src/types/config/runtime.ts", "../../src/utils/deep-merge.ts", "../../src/utils/instruction-file.ts", "../../src/utils/pid.ts", "../../src/utils/ulid.ts", "../../src/utils/regex-guard.ts", "../../src/utils/safe-json.ts", "../../src/utils/session-scoped-path.ts", "../../src/utils/slug.ts", "../../src/utils/wstack-paths.ts", "../../src/storage/file-session-writer.ts", "../../src/storage/session-helpers.ts", "../../src/storage/session-writer-truncate.ts", "../../src/storage/session-writer-scrubber.ts", "../../src/storage/session-checkpoint-cas.ts", "../../src/storage/storage-concurrency.ts", "../../src/storage/session-id.ts", "../../src/storage/session-id-resolver.ts", "../../src/storage/session-read-scrubber.ts", "../../src/storage/session-resume-validation.ts", "../../src/storage/session-store/index-reader.ts", "../../src/storage/session-store/directory-scan.ts", "../../src/storage/session-store/directory-session-files.ts", "../../src/storage/session-store/delete-session-artifacts.ts", "../../src/storage/session-store/paths.ts", "../../src/storage/session-store/delete-session-guards.ts", "../../src/storage/session-store/load-cache.ts", "../../src/storage/session-store/prune-helpers.ts", "../../src/storage/session-store/events.ts", "../../src/storage/session-store/fork-session.ts", "../../src/storage/session-store/replay.ts", "../../src/storage/session-store/load-session-data.ts", "../../src/storage/session-tool-call-ends.ts", "../../src/storage/session-store/summary-builder.ts", "../../src/storage/session-store/summary-header.ts", "../../src/storage/session-store/shard-manifest.ts", "../../src/storage/session-summary.ts", "../../src/storage/session-store/search-events.ts", "../../src/storage/queue-store.ts", "../../src/storage/attachment-store.ts", "../../src/storage/memory-backend.ts", "../../src/types/memory.ts", "../../src/storage/memory-graph-backend.ts", "../../src/storage/memory-consolidator.ts", "../../src/storage/config-store.ts", "../../src/storage/provider-config-watcher.ts", "../../src/security/config-secrets.ts", "../../src/storage/config-loader/inline-provider-models.ts", "../../src/storage/config-loader.ts", "../../src/types/context-window.ts", "../../src/storage/config-loader/bootstrap.ts", "../../src/types/default-config.ts", "../../src/storage/config-loader/defaults.ts", "../../src/storage/config-loader/default-repair.ts", "../../src/storage/config-loader/diagnostics.ts", "../../src/storage/config-loader/env-overrides.ts", "../../src/storage/config-loader/in-project-policy.ts", "../../src/storage/config-loader/legacy-sage-migration.ts", "../../src/storage/config-loader/path-identity.ts", "../../src/storage/config-migration.ts", "../../src/storage/recovery-lock.ts", "../../src/storage/session-reader.ts", "../../src/storage/annotations-store.ts", "../../src/storage/replay-log-store.ts", "../../src/replay/hash.ts", "../../src/storage/session-recovery.ts", "../../src/storage/tool-audit-log.ts", "../../src/storage/session-analyzer.ts", "../../src/session-registry.ts", "../../src/session-registry-atomic-file.ts", "../../src/agent-status-helpers.ts", "../../src/agent-status-tracker.ts", "../../src/fleet-notifier.ts", "../../src/storage/session-rewinder.ts", "../../src/storage/session-rewind-apply.ts", "../../src/storage/todos-checkpoint.ts", "../../src/storage/completed-work-checkpoint.ts", "../../src/storage/plan-store.ts", "../../src/storage/plan-templates.ts", "../../src/storage/task-store.ts", "../../src/storage/director-state.ts", "../../src/storage/goal-store.ts", "../../src/storage/goal-kanban.ts", "../../src/storage/goal-coordination.ts", "../../src/storage/prompt-store.ts", "../../src/storage/prompt-usage-store.ts", "../../src/storage/input-history-store.ts", "../../src/storage/cloud-sync.ts", "../../src/storage/session-event-bridge.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Dirent } from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { EventBus } from '../kernel/events.js';\nimport { DefaultSecretScrubber } from '../security/secret-scrubber.js';\nimport type { Logger } from '../types/logger.js';\nimport type { Message } from '../types/messages.js';\nimport type { SecretScrubber } from '../types/secret-scrubber.js';\nimport type {\n ForkedSession,\n ResumedSession,\n SessionData,\n SessionEvent,\n SessionForkOptions,\n SessionMetadata,\n SessionStore,\n SessionSummary,\n SessionWriter,\n WorkspaceCheckpointRef,\n WorkspaceMaterializationResult,\n} from '../types/session.js';\nimport { atomicWrite, ensureDir, withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/index.js';\nimport { FileSessionWriter } from './file-session-writer.js';\nimport { sessionContentText } from './session-helpers.js';\nimport { SessionCheckpointCas } from './session-checkpoint-cas.js';\nimport { generateSessionId } from './session-id.js';\nimport { resolveSessionId, sessionIdResolutionError } from './session-id-resolver.js';\nimport { scrubPersistedSessionSummary } from './session-read-scrubber.js';\nimport {\n formatInterruptedToolNotice,\n formatResumeValidationNotice,\n validateResumeFileObservations,\n} from './session-resume-validation.js';\nimport { applySessionIndexLines, readFileRange } from './session-store/index-reader.js';\nimport { shouldSkipSessionDirectoryEntry } from './session-store/directory-scan.js';\nimport {\n collectSessionFiles as collectSessionFilesFromDirectory,\n collectSessionIds as collectSessionIdsFromDirectory,\n} from './session-store/directory-session-files.js';\nimport { deleteSessionArtifacts } from './session-store/delete-session-artifacts.js';\nimport { assertSessionCanBeDeleted } from './session-store/delete-session-guards.js';\nimport { SessionLoadCache } from './session-store/load-cache.js';\nimport { pruneSessionFiles } from './session-store/prune-helpers.js';\nimport {\n emitSessionStoreError,\n emitSessionStoreRead,\n emitSessionStoreWrite,\n} from './session-store/events.js';\nimport { forkSession } from './session-store/fork-session.js';\nimport { loadSessionDataFromFile } from './session-store/load-session-data.js';\nimport type {\n DirectorySummaryCandidate,\n IndexCacheEntry,\n SessionFileRef,\n SessionStoreOptions,\n ShardManifestEntry,\n} from './session-store/types.js';\nimport {\n ensureShardDir as ensureSessionShardDir,\n sessionPath as sessionStorePath,\n shardKeyForSessionId,\n shardManifestPath,\n} from './session-store/paths.js';\nimport {\n summarizeSessionEvents,\n summarizeSessionFile,\n} from './session-store/summary-builder.js';\nimport { readSessionSummaryHeader } from './session-store/summary-header.js';\nimport { readOrBuildShardManifestEntry } from './session-store/shard-manifest.js';\nimport { searchSessionEvents } from './session-store/search-events.js';\nimport { compareSessionSummaries, matchesSessionFilter } from './session-summary.js';\nimport { mapWithConcurrency } from './storage-concurrency.js';\n\nexport type { SessionStoreOptions } from './session-store/types.js';\n\nexport class DefaultSessionStore implements SessionStore {\n private readonly dir: string;\n private readonly events?: EventBus | undefined;\n private readonly secretScrubber: SecretScrubber;\n private readonly projectRoot?: string | undefined;\n private readonly checkpointCas?: SessionCheckpointCas | undefined;\n private readonly isSessionInUse?: ((sessionId: string) => Promise<string | null>) | undefined;\n private readonly logger: Logger | undefined;\n private readonly onAppend?: ((event: SessionEvent) => void) | undefined;\n private readonly onAppendBatch?: ((events: SessionEvent[]) => void) | undefined;\n\n /**\n * In-memory cache for load() results, keyed by session ID. The cache is\n * invalidated when the file's mtimeMs or size changes (indicating the\n * file was written to). This eliminates redundant full-file reads and\n * JSON parses when the same session is loaded multiple times within the\n * store's lifetime (e.g., webui session detail views, list() fallbacks).\n *\n * Max size is capped to prevent unbounded memory growth in long-running\n * processes. When the limit is reached, the oldest entry is evicted.\n */\n private readonly _loadCache = new Map<string, import('./session-store/types.js').LoadCacheEntry>();\n private readonly loadCache = new SessionLoadCache(this._loadCache);\n private _indexCache: IndexCacheEntry | null = null;\n private readonly shardManifestCache = new Map<string, ShardManifestEntry>();\n private static readonly LIST_SCAN_CONCURRENCY = 32;\n\n constructor(opts: SessionStoreOptions) {\n this.dir = opts.dir;\n this.projectRoot = opts.projectRoot ? path.resolve(opts.projectRoot) : undefined;\n this.checkpointCas = this.projectRoot\n ? new SessionCheckpointCas({\n rootDir: path.join(this.dir, '_cas'),\n projectRoot: this.projectRoot,\n })\n : undefined;\n this.events = opts.events;\n this.secretScrubber = opts.secretScrubber ?? new DefaultSecretScrubber();\n this.isSessionInUse = opts.isSessionInUse;\n this.logger = opts.logger;\n this.onAppend = opts.onAppend;\n this.onAppendBatch = opts.onAppendBatch;\n }\n\n /**\n * Emit a structured warning. Uses the configured Logger when available;\n * falls back to console.warn(JSON) so warnings are never silently dropped.\n */\n private logWarn(msg: string, ctx?: Record<string, unknown>): void {\n if (this.logger) {\n this.logger.warn(msg, ctx);\n } else {\n console.warn(JSON.stringify({ ...ctx, message: msg, timestamp: new Date().toISOString() }));\n }\n }\n\n private scrubSummaries(summaries: readonly SessionSummary[]): SessionSummary[] {\n return summaries.map((summary) => scrubPersistedSessionSummary(summary, this.secretScrubber));\n }\n\n /**\n * Clear the load() cache. Useful for testing or when the caller knows\n * the file has changed externally (e.g., another process wrote to it).\n */\n clearLoadCache(sessionId?: string): void {\n this.loadCache.clear(sessionId);\n }\n\n // \u00E2\u201D\u20AC\u00E2\u201D\u20AC Storage event helpers \u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\n\n /** Absolute path to the session index file. */\n private get indexFile(): string {\n return path.join(this.dir, '_index.jsonl');\n }\n\n /** Join session ID to its absolute path within the store directory. */\n private sessionPath(id: string, ext: '.jsonl' | '.summary.json'): string {\n return sessionStorePath(this.dir, id, ext);\n }\n\n private shardManifestPath(shardKey: string): string {\n return shardManifestPath(this.dir, shardKey);\n }\n\n private shardKeyForSessionId(id: string): string {\n return shardKeyForSessionId(id);\n }\n\n private async invalidateShardManifestBySessionId(id: string): Promise<void> {\n const shardKey = this.shardKeyForSessionId(id);\n const manifestPath = this.shardManifestPath(shardKey);\n await withFileLock(manifestPath, async () => {\n this.shardManifestCache.delete(shardKey);\n try {\n await fsp.unlink(manifestPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n });\n }\n\n /**\n * Ensure the directory implied by the session ID exists. When the ID\n * contains a date prefix like `2026-06-06/...`, this creates the date\n * subdirectory so sessions group naturally by day.\n */\n private async ensureShardDir(id: string): Promise<string> {\n return ensureSessionShardDir(this.dir, id);\n }\n\n async create(meta: Omit<SessionMetadata, 'startedAt'>): Promise<SessionWriter> {\n const startedAt = new Date().toISOString();\n const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt);\n const shardDir = await this.ensureShardDir(id);\n const file = this.sessionPath(id, '.jsonl');\n const t0 = Date.now();\n let handle: fsp.FileHandle;\n try {\n handle = await fsp.open(file, 'a', 0o600);\n } catch (err) {\n emitSessionStoreError(this.events, id, file, 'create', toErrorMessage(err), false);\n throw new Error(`Failed to open session file: ${toErrorMessage(err)}`, { cause: err });\n }\n try {\n const writer = new FileSessionWriter(id, handle, startedAt, meta, this.events, {\n dir: shardDir,\n filePath: file,\n secretScrubber: this.secretScrubber,\n checkpointCas: this.checkpointCas,\n onAppend: this.onAppend,\n onAppendBatch: this.onAppendBatch,\n resolveName: async () => {\n const current = await this.readSummaryManifest(id);\n if (!current) return null;\n return current.name === undefined\n ? {}\n : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };\n },\n onClose: (s) => this.appendToIndex(s),\n });\n emitSessionStoreWrite(this.events, id, file, 'create', 'success', Date.now() - t0);\n return writer;\n /* v8 ignore start -- defensive: FileSessionWriter ctor does not throw in practice */\n } catch (err) {\n await handle.close().catch((e) =>\n this.logWarn('Session handle close failed', {\n event: 'session_store.handle_close_failed',\n message: e instanceof Error ? e.message : String(e),\n }),\n );\n emitSessionStoreError(this.events, id, file, 'create', toErrorMessage(err), true);\n throw err;\n }\n /* v8 ignore stop */\n }\n\n async fork(id: string, opts: SessionForkOptions = {}): Promise<ForkedSession> {\n return forkSession(this, id, opts);\n }\n\n async materializeWorkspaceCheckpoint(\n checkpoint: WorkspaceCheckpointRef,\n targetRoot: string,\n ): Promise<WorkspaceMaterializationResult> {\n if (!this.checkpointCas) {\n throw new Error(\n 'Workspace checkpoint materialization requires a projectRoot-aware session store',\n );\n }\n return this.checkpointCas.materialize(checkpoint, targetRoot);\n }\n\n async resolveId(query: string): Promise<string> {\n const normalized = query.trim();\n if (!normalized) throw new Error('Session not found: (empty query)');\n if (normalized) {\n try {\n const stat = await fsp.stat(this.sessionPath(normalized, '.jsonl'));\n if (stat.isFile()) return normalized;\n } catch {\n // Fall through to exact-leaf / unique-prefix resolution.\n }\n }\n const ids = await this.collectSessionIds(this.dir);\n const resolution = resolveSessionId(normalized, ids);\n if (resolution.status === 'resolved') return resolution.id;\n throw sessionIdResolutionError(resolution);\n }\n\n async resume(id: string): Promise<ResumedSession> {\n const canonicalId = await this.resolveId(id);\n const file = this.sessionPath(canonicalId, '.jsonl');\n const t0 = Date.now();\n const data = await this.load(canonicalId);\n const persistedSummary = await this.readSummaryManifest(canonicalId);\n const fileStat = await fsp.stat(file);\n // When the events array was front-truncated to stay within the retention\n // budget (`eventsDropped > 0`), the oldest records are missing from memory.\n // The summary must be derived from the full file, and file-observation\n // validation must use the streaming search path to see every observation.\n const eventsDropped = data.eventsDropped ?? 0;\n const derivedSummary = eventsDropped > 0\n ? await summarizeSessionFile({\n id: canonicalId,\n file,\n mtime: fileStat.mtime.toISOString(),\n secretScrubber: this.secretScrubber,\n })\n : await summarizeSessionEvents({\n id: canonicalId,\n events: data.events,\n mtime: fileStat.mtime.toISOString(),\n });\n const initialSummary: SessionSummary = {\n ...derivedSummary,\n ...(persistedSummary?.name !== undefined ? { name: persistedSummary.name } : {}),\n };\n // Ephemeral system notices injected into the first resumed turn. Both are\n // informational only \u2014 neither re-executes any prior work.\n const noticeMessages: Message[] = [];\n let resumeValidation: import('../types/session.js').ResumeValidation | undefined;\n if (this.projectRoot) {\n try {\n const validationEvents = eventsDropped > 0\n ? (await this.searchEvents(\n canonicalId,\n (ev: SessionEvent, _i: number, _ts: string) => ev.type === 'file_observation',\n )).map((h) => h.event)\n : data.events;\n resumeValidation = await validateResumeFileObservations(validationEvents, this.projectRoot);\n const notice = formatResumeValidationNotice(resumeValidation, this.projectRoot);\n if (notice) {\n noticeMessages.push({\n role: 'system',\n content: notice,\n ts: resumeValidation.checkedAt,\n });\n }\n } catch (err) {\n // Validation is a safety signal, not a reason to make an otherwise\n // readable session impossible to resume. Surface diagnostics and\n // continue with the replay if an unexpected filesystem error occurs.\n emitSessionStoreError(\n this.events,\n canonicalId,\n file,\n 'resume_validation',\n toErrorMessage(err),\n true,\n );\n }\n }\n // Interrupted-tool notice is independent of projectRoot \u2014 it reflects the\n // reconstructed conversation, not the filesystem.\n const interruptedNotice = formatInterruptedToolNotice(data.pendingToolUseCount ?? 0);\n if (interruptedNotice) {\n noticeMessages.push({\n role: 'system',\n content: interruptedNotice,\n ts: new Date().toISOString(),\n });\n }\n const resumedData: SessionData = {\n ...data,\n ...(resumeValidation ? { resumeValidation } : {}),\n ...(noticeMessages.length > 0 ? { messages: [...data.messages, ...noticeMessages] } : {}),\n };\n let handle: fsp.FileHandle;\n try {\n handle = await fsp.open(file, 'a', 0o600);\n /* v8 ignore start -- defensive: load() above already validated the file is readable */\n } catch (err) {\n emitSessionStoreError(this.events, canonicalId, file, 'resume', toErrorMessage(err), false);\n throw new Error(`Failed to open session \"${canonicalId}\" for append: ${toErrorMessage(err)}`, {\n cause: err,\n });\n }\n /* v8 ignore stop */\n try {\n const writer = new FileSessionWriter(\n canonicalId,\n handle,\n new Date().toISOString(),\n {\n id: canonicalId,\n model: data.metadata.model,\n provider: data.metadata.provider,\n },\n this.events,\n {\n resumed: true,\n initialSummary,\n // Shard directory (sessions/<date>/) \u2014 must match create() so the\n // .summary.json sidecar lands next to the JSONL instead of the\n // sessions root (where summaryFor() would never find it).\n dir: path.dirname(file),\n filePath: file,\n secretScrubber: this.secretScrubber,\n checkpointCas: this.checkpointCas,\n onAppend: this.onAppend,\n onAppendBatch: this.onAppendBatch,\n resolveName: async () => {\n const current = await this.readSummaryManifest(canonicalId);\n if (!current) return null;\n return current.name === undefined\n ? {}\n : { name: sessionContentText(this.secretScrubber.scrub(current.name)) };\n },\n onClose: (s) => this.appendToIndex(s),\n },\n );\n emitSessionStoreWrite(this.events, canonicalId, file, 'resume', 'success', Date.now() - t0);\n return { writer, data: resumedData };\n /* v8 ignore start -- defensive: FileSessionWriter ctor does not throw in practice */\n } catch (err) {\n await handle.close().catch((e) =>\n this.logWarn('Session handle close failed', {\n event: 'session_store.handle_close_failed',\n message: e instanceof Error ? e.message : String(e),\n }),\n );\n emitSessionStoreError(this.events, canonicalId, file, 'resume', toErrorMessage(err), true);\n throw err;\n }\n /* v8 ignore stop */\n }\n\n async load(id: string): Promise<SessionData> {\n return this.loadInternal(id, { full: true });\n }\n\n /**\n * Fast-path loader that skips message reconstruction and adjacency repair.\n *\n * Use this for callers that only need the raw event stream + session\n * metadata \u2014 e.g. session listers, analytics, audit, and the TUI's\n * \"events only\" views. It avoids the message array build and\n * repairToolUseAdjacency cost on large session files (a long agent\n * run can have 50k+ events; rebuilding messages is O(events) and\n * allocates per-block, so skipping it is a meaningful win).\n *\n * The returned data.messages is an empty array; data.toolCallEnds\n * is computed from the raw events. usage is the sum across all\n * llm_response events \u2014 same as full load().\n */\n async loadEventsOnly(id: string): Promise<SessionData> {\n return this.loadInternal(id, { full: false });\n }\n\n private async loadInternal(\n id: string,\n mode: { full: true } | { full: false },\n ): Promise<SessionData> {\n const file = this.sessionPath(id, '.jsonl');\n const t0 = Date.now();\n let outcome: 'success' | 'failure' = 'success';\n let errorMsg: string | undefined;\n let cacheHit = false;\n try {\n // Stat the file first to check the cache. The stat is cheap (no content\n // read) and lets us skip the full readFile + JSON parse when the file\n // hasn't changed since the last load.\n const s = await fsp.stat(file);\n const stat: { mtimeMs: number; size: number } = { mtimeMs: s.mtimeMs, size: s.size };\n\n // Check cache: if mtimeMs AND size match, the file hasn't changed.\n const cached = this.loadCache.getFresh(id, stat, mode.full);\n if (cached) {\n cacheHit = true;\n return cached;\n }\n\n const data = await loadSessionDataFromFile({\n id,\n file,\n full: mode.full,\n events: this.events,\n secretScrubber: this.secretScrubber,\n });\n\n // Only full loads populate the cache; events-only loads always read\n // through (they're cheap, and a hot loop on events-only would\n // otherwise evict full-load entries that callers also need).\n if (mode.full) {\n this.loadCache.set(id, stat, data);\n }\n\n return data;\n } catch (err) {\n outcome = 'failure';\n errorMsg = toErrorMessage(err);\n throw err;\n } finally {\n emitSessionStoreRead(\n this.events,\n id,\n file,\n mode.full ? 'load' : 'load_events_only',\n outcome,\n Date.now() - t0,\n errorMsg,\n );\n if (cacheHit) {\n this.events?.emit('storage.cache_hit', {\n sessionId: id,\n store: 'session',\n filePath: file,\n operation: mode.full ? 'load' : 'load_events_only',\n durationMs: Date.now() - t0,\n });\n }\n }\n }\n\n /**\n * Streaming search over a session's JSONL. Walks the file once, parses\n * each event lazily, and yields only the events that match `predicate`.\n * Stops as soon as `opts.limit` matches are collected.\n *\n * Why this exists: `load()` parses the entire file into memory and\n * rebuilds `messages`/`toolCallEnds` for every caller. `search()` only\n * needs to know which events contain matching text \u2014 a per-line\n * predicate is enough. The full parse work (and the `_loadCache` poll)\n * is wasted in that case.\n *\n * Memory: O(hits) regardless of file size. Disk: one linear scan,\n * terminated at `limit` if the caller asked for one.\n *\n * Errors: missing file yields []. Corrupt lines are skipped (same\n * policy as `load()`). Aborting via `signal` rejects with `AbortError`.\n */\n async searchEvents(\n id: string,\n predicate: (event: SessionEvent, eventIndex: number, ts: string) => boolean,\n opts?: { limit?: number | undefined; signal?: AbortSignal | undefined },\n ): Promise<Array<{ event: SessionEvent; eventIndex: number; ts: string }>> {\n return searchSessionEvents({\n file: this.sessionPath(id, '.jsonl'),\n secretScrubber: this.secretScrubber,\n predicate,\n limit: opts?.limit,\n signal: opts?.signal,\n });\n }\n\n async list(limit = 20): Promise<SessionSummary[]> {\n try {\n // Try the index first; fall back to directory scan if the index is\n // missing, empty, or unreadable.\n const indexed = await this.readIndex();\n if (indexed.length > 0) {\n // `readIndex()` already sorted the array by startedAt DESC, id\n // ASC, so we just slice the prefix.\n return this.scrubSummaries(indexed.slice(0, limit));\n }\n // Index unavailable \u2014 fall back to a directory scan. Prefer summary\n // sidecars and only backfill full JSONL-derived summaries for the page\n // we are about to return.\n return this.scrubSummaries(await this.listFromDirectoryScan(limit));\n } catch {\n return [];\n }\n }\n\n /**\n * List sessions matching filter criteria, using the cached index.\n * Filters are applied BEFORE sorting and slicing, so the caller gets\n * exactly `limit` matching sessions \u2014 not a slice of a larger fetch.\n *\n * This avoids the DefaultSessionReader pattern of fetching 1000 sessions\n * then linear-filtering: the index is already in memory (readIndex\n * caches it), and the filter runs over the cached array without any\n * additional disk I/O.\n */\n async listFiltered(criteria: {\n since?: string | undefined;\n until?: string | undefined;\n provider?: string | undefined;\n model?: string | undefined;\n minTokens?: number | undefined;\n titleContains?: string | undefined;\n limit?: number | undefined;\n }): Promise<SessionSummary[]> {\n const limit = criteria.limit ?? 100;\n try {\n const indexed = await this.readIndex();\n if (indexed.length === 0) {\n // No index \u2014 fall back to list() + in-process filter.\n const raw = await this.list(Math.max(limit, 100));\n return raw.filter((s) => matchesSessionFilter(s, criteria)).slice(0, limit);\n }\n const filtered = this.scrubSummaries(indexed).filter((s) =>\n matchesSessionFilter(s, criteria),\n );\n // Filtering preserves the index's existing newest-first order.\n return filtered.slice(0, limit);\n } catch {\n return [];\n }\n }\n\n // \u00E2\u201D\u20AC\u00E2\u201D\u20AC Session index (_index.jsonl) \u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\u00E2\u201D\u20AC\n //\n // One JSON line per closed session, appended atomically on close().\n // When a session is deleted, a tombstone {action:\"delete\",id:\"...\"} is\n // appended. On read, tombstones filter out matching session entries.\n // This keeps listing O(lines-in-index) instead of O(files-on-disk).\n //\n // The index auto-compacts every N appends to prevent unbounded growth\n // from tombstones and duplicate entries (resume cycles).\n\n private indexAppendCount = 0;\n private static readonly COMPACT_EVERY = 30;\n\n /** Append a session summary to the index, propagating persistence failures. */\n private async appendToIndexStrict(summary: SessionSummary): Promise<void> {\n await ensureDir(this.dir);\n // Serialize the append (and any compaction it triggers) under the index\n // file lock. The lock is per-FILE, so it also guards against a SECOND\n // wstack process in the same project appending/compacting concurrently \u2014\n // without it, a compact() rewrite racing an append() silently drops the\n // appended line (the source-of-truth .jsonl survives, but the listing\n // cache loses the entry until rebuildIndex()).\n let shouldCompact = false;\n await withFileLock(this.indexFile, async () => {\n // Invalidate before appending so an invalidation failure cannot leave a\n // successfully-updated index paired with a stale persisted shard view.\n await this.invalidateShardManifestBySessionId(summary.id);\n const line = JSON.stringify(summary) + '\\n';\n await fsp.appendFile(this.indexFile, line, 'utf8');\n this._indexCache = null;\n this.indexAppendCount++;\n // Auto-compact periodically to remove tombstones and duplicates.\n // compactIndexInner() is called WHILE the lock is held \u2014 it must not\n // re-acquire it (withFileLock is not reentrant) or it would deadlock.\n if (this.indexAppendCount >= DefaultSessionStore.COMPACT_EVERY) {\n shouldCompact = true;\n this.indexAppendCount = 0;\n }\n });\n if (shouldCompact) {\n await withFileLock(this.indexFile, () => this.compactIndexInner());\n }\n }\n\n /** Best-effort index append used by writer close. */\n private async appendToIndex(summary: SessionSummary): Promise<void> {\n // Note: storage.write for this operation is emitted by FileSessionWriter.doClose()\n // so it can include the traceId. Do NOT emit here to avoid duplicates.\n await this.appendToIndexStrict(summary).catch(() => {\n // best-effort \u2014 error surfaced via the storage.write event in doClose()\n });\n }\n\n /** Append a tombstone entry for a deleted session. */\n private async writeTombstone(id: string): Promise<void> {\n try {\n await ensureDir(this.dir);\n await withFileLock(this.indexFile, async () => {\n const line = JSON.stringify({ action: 'delete', id }) + '\\n';\n await fsp.appendFile(this.indexFile, line, 'utf8');\n this._indexCache = null;\n await this.invalidateShardManifestBySessionId(id);\n this.indexAppendCount++;\n });\n } catch {\n // best-effort\n }\n }\n\n /**\n * Compact the index: read all entries, drop tombstones, deduplicate\n * (keep latest per session), and rewrite atomically. Acquires the index\n * file lock so a concurrent append (this process or another wstack in the\n * same project) can't be overwritten by the rewrite.\n */\n private async compactIndex(): Promise<void> {\n const t0 = Date.now();\n let outcome: 'success' | 'failure' = 'success';\n let errorMsg: string | undefined;\n try {\n await withFileLock(this.indexFile, () => this.compactIndexInner());\n } catch (err) {\n outcome = 'failure';\n errorMsg = toErrorMessage(err);\n } finally {\n // Compact is internal \u00E2\u20AC\u201D use 'session' as the session ID placeholder.\n emitSessionStoreWrite(\n this.events,\n '~compact~',\n this.indexFile,\n 'compact',\n outcome,\n Date.now() - t0,\n undefined,\n errorMsg,\n );\n }\n }\n\n /**\n * Lock-free compaction body. The caller MUST already hold the index file\n * lock (via withFileLock(this.indexFile, ...)). Uses atomicWrite for the\n * rewrite so the temp file gets a random suffix (no collision between two\n * compactions) and the Windows transient-EPERM rename retry.\n */\n private async compactIndexInner(): Promise<void> {\n const entries = await this.readIndex();\n if (entries.length === 0) return;\n const lines = entries.map((s) => JSON.stringify(s)).join('\\n') + '\\n';\n await atomicWrite(this.indexFile, lines, { mode: 0o600 });\n this._indexCache = null;\n }\n\n /**\n * Read the index file and return deduplicated session summaries.\n * Entries with a matching tombstone are filtered out.\n * Returns empty array when the index doesn't exist or is corrupt.\n */\n private async readIndex(): Promise<readonly SessionSummary[]> {\n let stat: { mtimeMs: number; size: number; ino: number; birthtimeMs: number };\n try {\n const s = await fsp.stat(this.indexFile);\n stat = { mtimeMs: s.mtimeMs, size: s.size, ino: s.ino, birthtimeMs: s.birthtimeMs };\n } catch {\n this._indexCache = null;\n return [];\n }\n\n if (\n this._indexCache !== null &&\n this._indexCache.mtimeMs === stat.mtimeMs &&\n this._indexCache.size === stat.size &&\n this._indexCache.ino === stat.ino &&\n this._indexCache.birthtimeMs === stat.birthtimeMs\n ) {\n return this._indexCache.summaries;\n }\n\n const cached = this._indexCache;\n const sameFile =\n cached !== null && cached.ino === stat.ino && cached.birthtimeMs === stat.birthtimeMs;\n if (cached && sameFile && stat.size > cached.size) {\n const appended = await readFileRange(this.indexFile, cached.size, stat.size);\n if (appended !== null) {\n applySessionIndexLines(appended.raw, cached.byId, cached.deleted);\n const summaries = Array.from(cached.byId.values()).sort(compareSessionSummaries);\n this._indexCache = {\n ...stat,\n size: appended.end,\n summaries,\n byId: cached.byId,\n deleted: cached.deleted,\n };\n return summaries;\n }\n }\n\n let raw: string;\n try {\n raw = await fsp.readFile(this.indexFile, 'utf8');\n } catch {\n this._indexCache = null;\n return [];\n }\n const deleted = new Set<string>();\n const byId = new Map<string, SessionSummary>();\n applySessionIndexLines(raw, byId, deleted);\n const summaries = Array.from(byId.values());\n // Sort once when the index is (re)loaded so `list()` callers can\n // take a prefix without re-sorting the whole array per request.\n // Sort key mirrors the original `list()` comparator:\n // startedAt DESC, then id ASC for tie-breaks.\n summaries.sort(compareSessionSummaries);\n this._indexCache = { ...stat, summaries, byId, deleted };\n return summaries;\n }\n\n /**\n * Rebuild the index from disk by scanning all sessions and writing a\n * fresh _index.jsonl. Useful after manual cleanup or index corruption.\n */\n async rebuildIndex(): Promise<number> {\n const ids = await this.collectSessionIds(this.dir);\n /* v8 ignore next -- summaryFor() never rejects for a collected id (its .jsonl exists) */\n const summaries = await Promise.all(\n ids.map((id) => this.summaryFor(id).catch(() => null)),\n ); /* best-effort */\n const valid = summaries.filter((s): s is SessionSummary => s !== null);\n // Atomic rewrite under the index lock so it can't clobber a concurrent\n // append (or be clobbered by a concurrent compaction). atomicWrite gives\n // a random temp suffix (no collision with compactIndexInner's temp) and\n // the Windows transient-EPERM rename retry. The expensive disk scan above\n // runs OUTSIDE the lock to avoid holding it for the whole rebuild.\n const lines = valid.map((s) => JSON.stringify(s)).join('\\n') + '\\n';\n await withFileLock(this.indexFile, async () => {\n await atomicWrite(this.indexFile, lines, { mode: 0o600 });\n this._indexCache = null;\n });\n return valid.length;\n }\n\n private async listFromDirectoryScan(limit: number): Promise<SessionSummary[]> {\n const shardKeys = await this.collectShardKeys();\n const shardEntries = await mapWithConcurrency(\n shardKeys,\n DefaultSessionStore.LIST_SCAN_CONCURRENCY,\n async (shardKey) => await this.readOrBuildShardManifest(shardKey),\n );\n\n const out: DirectorySummaryCandidate[] = [];\n for (const entry of shardEntries) {\n for (const summary of entry.summaries) {\n out.push({ summary, needsBackfill: false });\n }\n }\n out.sort((a, b) => compareSessionSummaries(a.summary, b.summary));\n\n const selected = out.slice(0, limit);\n const summaries = await mapWithConcurrency(\n selected,\n Math.min(DefaultSessionStore.LIST_SCAN_CONCURRENCY, Math.max(1, limit)),\n async (candidate): Promise<SessionSummary | null> => candidate.summary,\n );\n return summaries.filter((s): s is SessionSummary => s !== null);\n }\n\n private async collectShardKeys(): Promise<string[]> {\n let entries: Dirent[];\n try {\n entries = await fsp.readdir(this.dir, { withFileTypes: true });\n } catch {\n return [''];\n }\n\n const shardKeys = [''];\n for (const entry of entries) {\n if (shouldSkipSessionDirectoryEntry(entry.name)) continue;\n if (entry.isDirectory()) shardKeys.push(entry.name);\n }\n return shardKeys;\n }\n\n private async readOrBuildShardManifest(shardKey: string): Promise<ShardManifestEntry> {\n const cached = this.shardManifestCache.get(shardKey);\n if (cached) return cached;\n\n const manifestPath = this.shardManifestPath(shardKey);\n return withFileLock(manifestPath, async () => {\n const lockedCached = this.shardManifestCache.get(shardKey);\n if (lockedCached) return lockedCached;\n const entry = await readOrBuildShardManifestEntry({\n shardKey,\n manifestPath,\n concurrency: DefaultSessionStore.LIST_SCAN_CONCURRENCY,\n collectSessionFilesInShard: (key) => this.collectSessionFilesInShard(key),\n readSummaryManifest: (id) => this.readSummaryManifest(id),\n summaryHeaderFor: (ref) => this.summaryHeaderFor(ref),\n summaryFor: (id) => this.summaryFor(id),\n });\n this.shardManifestCache.set(shardKey, entry);\n return entry;\n });\n }\n\n private async collectSessionFilesInShard(shardKey: string): Promise<SessionFileRef[]> {\n const dir = shardKey ? path.join(this.dir, shardKey) : this.dir;\n const entries = await this.collectSessionFiles(dir, shardKey);\n return shardKey\n ? entries.filter((entry) => entry.id.startsWith(`${shardKey}/`))\n : entries.filter((entry) => !entry.id.includes('/'));\n }\n\n private async collectSessionFiles(\n dir: string,\n prefix = '',\n depth = 0,\n ): Promise<SessionFileRef[]> {\n return collectSessionFilesFromDirectory(dir, prefix, depth);\n }\n\n /** Recursively collect session IDs from date-shard subdirectories.\n * IDs include the date-prefix path (e.g. \"2026-06-06/17-46-57Z_\u00E2\u20AC\u00A6\").\n * Skips `.jsonl`/`.summary.json` root files, dot-files, and\n * sub-directories that belong to fleet/subagent sessions. */\n private async collectSessionIds(dir: string, prefix = '', depth = 0): Promise<string[]> {\n return collectSessionIdsFromDirectory(dir, prefix, depth);\n }\n\n private async summaryFor(id: string): Promise<SessionSummary> {\n const manifest = this.sessionPath(id, '.summary.json');\n const t0 = Date.now();\n let outcome: 'success' | 'failure' = 'success';\n let errorMsg: string | undefined;\n const fromManifest = await this.readSummaryManifest(id, t0);\n if (fromManifest) return fromManifest;\n\n try {\n const full = this.sessionPath(id, '.jsonl');\n const stat = await fsp.stat(full);\n const summary = await this.summarize(id, stat.mtime.toISOString());\n await atomicWrite(manifest, JSON.stringify(summary), { mode: 0o600 }).catch((err) => {\n const msg = toErrorMessage(err);\n emitSessionStoreError(this.events, id, manifest, 'summary_fallback', msg, true);\n this.logWarn('Session manifest write failed', {\n event: 'session_store.manifest_write_failed',\n sessionId: id,\n message: msg,\n });\n });\n outcome = 'failure';\n errorMsg = 'summary fallback \u00E2\u20AC\u201D manifest rebuilt';\n emitSessionStoreRead(this.events, id, manifest, 'summary', outcome, Date.now() - t0, errorMsg);\n return summary;\n } catch (err) {\n outcome = 'failure';\n errorMsg = toErrorMessage(err);\n emitSessionStoreRead(this.events, id, manifest, 'summary', outcome, Date.now() - t0, errorMsg);\n return {\n id,\n title: '(damaged)',\n startedAt: new Date().toISOString(),\n model: 'unknown',\n provider: 'unknown',\n tokenTotal: 0,\n };\n }\n }\n\n private async readSummaryManifest(\n id: string,\n startTime = Date.now(),\n ): Promise<SessionSummary | null> {\n const manifest = this.sessionPath(id, '.summary.json');\n try {\n const raw = await fsp.readFile(manifest, 'utf8');\n emitSessionStoreRead(\n this.events,\n id,\n manifest,\n 'summary',\n 'success',\n Date.now() - startTime,\n );\n return JSON.parse(raw) as SessionSummary;\n } catch {\n return null;\n }\n }\n\n private async summaryHeaderFor(ref: SessionFileRef): Promise<SessionSummary | null> {\n return readSessionSummaryHeader(ref, this.secretScrubber);\n }\n\n /**\n * Delete a session and all associated files: JSONL, summary, plan/todos\n * sidecars, and the session directory (fleet.json, shared/, subagents/).\n *\n * Individual file deletions are best-effort (logged as structured warnings),\n * but a tombstone is always written so readIndex() filters this session out.\n * If the session directory itself can't be removed, the error is surfaced\n * to the caller so prune() can report it.\n */\n private async deleteSession(id: string): Promise<void> {\n const jsonlPath = this.sessionPath(id, '.jsonl');\n await deleteSessionArtifacts({ rootDir: this.dir, id, jsonlPath });\n\n // Write an index tombstone so readIndex() filters this session out.\n await this.writeTombstone(id);\n }\n\n async delete(id: string): Promise<void> {\n await assertSessionCanBeDeleted(id, this.isSessionInUse);\n await this.deleteSession(id);\n }\n\n async rename(id: string, name: string): Promise<SessionSummary> {\n const trimmed = sessionContentText(this.secretScrubber.scrub(name));\n const manifest = this.sessionPath(id, '.summary.json');\n const jsonlPath = this.sessionPath(id, '.jsonl');\n // Refuse to name a session that has no JSONL on disk. `summaryFor`\n // would otherwise synthesize a '(damaged)' summary and persist it,\n // creating a phantom entry. ENOENT \u2192 throw a typed error so callers\n // can surface \"session not found\" cleanly.\n try {\n await fsp.stat(jsonlPath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code === 'ENOENT') {\n throw new Error(`Session not found: ${id}`);\n }\n throw err;\n }\n\n const t0 = Date.now();\n let outcome: 'success' | 'failure' = 'success';\n let errorMsg: string | undefined;\n let updated: SessionSummary;\n try {\n updated = await withFileLock(manifest, async () => {\n const summary = (await this.readSummaryManifest(id)) ?? (await this.summaryFor(id));\n const { name: _drop, ...rest } = summary;\n const next: SessionSummary = trimmed ? { ...rest, name: trimmed } : rest;\n await atomicWrite(manifest, JSON.stringify(next), { mode: 0o600 });\n try {\n await this.appendToIndexStrict(next);\n } catch (err) {\n // Keep the sidecar and index in agreement when the index append fails.\n await atomicWrite(manifest, JSON.stringify(summary), { mode: 0o600 });\n throw err;\n }\n return next;\n });\n } catch (err) {\n outcome = 'failure';\n errorMsg = toErrorMessage(err);\n emitSessionStoreError(this.events, id, manifest, 'rename', errorMsg, false);\n throw err;\n } finally {\n emitSessionStoreWrite(\n this.events,\n id,\n manifest,\n 'rename',\n outcome,\n Date.now() - t0,\n undefined,\n errorMsg,\n );\n }\n\n // appendToIndexStrict() already invalidated the index and both shard\n // manifest caches while persisting the same updated summary.\n this.clearLoadCache(id);\n return updated;\n }\n\n async prune(maxAgeDays = 30): Promise<number> {\n const deleted = await pruneSessionFiles(this.dir, maxAgeDays, (id) => this.deleteSession(id));\n if (deleted > 0) {\n // Compact the index to remove tombstones for deleted sessions.\n /* v8 ignore next -- best-effort: compactIndex swallows its own errors */\n await this.compactIndex().catch(() => undefined); /* best-effort */\n }\n return deleted;\n }\n\n async clearHistory(id: string): Promise<void> {\n await this.ensureShardDir(id);\n const file = this.sessionPath(id, '.jsonl');\n const meta = this.sessionPath(id, '.summary.json');\n const record = `${JSON.stringify({\n type: 'session_start',\n ts: new Date().toISOString(),\n id,\n model: 'unknown',\n provider: 'unknown',\n })}\\n`;\n await atomicWrite(file, record);\n await fsp.unlink(meta).catch(() => undefined);\n // Invalidate the parsed-session cache so the cleared `SessionData`\n // graph cannot survive in `SessionLoadCache` (50 entries / 64 MiB).\n // Without this, a `replaceMessages([])` on a previous hot session can\n // keep an unbounded body graph reachable for the process lifetime.\n this.clearLoadCache(id);\n }\n\n private async summarize(id: string, mtime: string): Promise<SessionSummary> {\n return summarizeSessionFile({\n id,\n file: this.sessionPath(id, '.jsonl'),\n mtime,\n secretScrubber: this.secretScrubber,\n });\n }\n}\n", "import type { SecretScrubber } from '../types/secret-scrubber.js';\n\ninterface Pattern {\n type: string;\n regex: RegExp;\n}\n\nconst PATTERNS: Pattern[] = [\n // Anchored at the start where possible so partial matches inside larger\n // strings don't trigger false positives.\n {\n type: 'anthropic_key',\n regex: /(?<![A-Za-z0-9])sk-ant-api\\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g,\n },\n { type: 'openai_key', regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g },\n { type: 'github_pat', regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g },\n { type: 'github_pat_v2', regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g },\n { type: 'aws_access_key', regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g },\n { type: 'gcp_key', regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g },\n { type: 'slack_token', regex: /(?<![A-Za-z0-9-])xox[abpos]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g },\n {\n type: 'stripe_key',\n regex: /(?<![A-Za-z0-9])sk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g,\n },\n {\n type: 'twilio_sid', regex: /(?<![A-Za-z0-9])AC[a-f0-9]{32}(?![A-Za-z0-9])/g,\n },\n {\n type: 'telegram_bot_token',\n // Telegram tokens are of the form bot<digits>:<alphanum> in URL paths\n regex: /\\/bot\\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,\n },\n {\n type: 'jwt',\n // Anchored: look for literal \"eyJ\" which is unambiguous for JWT header\n regex:\n /(?<![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,\n },\n {\n type: 'private_key',\n // Anchored: start must be BEGIN, end must be END with no extra dashes after END\n regex:\n /(?:^|\\n)-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----[\\s\\S]*?-----END[^-]*-----(?:\\n|$)/g,\n },\n { type: 'mongodb_uri', regex: /mongodb(?:\\+srv)?:\\/\\/[^\\s\"'`]+/g },\n { type: 'postgres_uri', regex: /postgres(?:ql)?:\\/\\/[^\\s\"'`]+/g },\n { type: 'mysql_uri', regex: /mysql:\\/\\/[^\\s\"'`]+/g },\n { type: 'redis_uri', regex: /redis:\\/\\/[^\\s\"'`]+/g },\n // AI/ML provider keys \u2014 modern LLM services with well-known prefixes\n {\n type: 'huggingface_token',\n // HuggingFace tokens: hf_ followed by 34 alphanumeric chars\n regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g,\n },\n {\n type: 'replicate_token',\n // Replicate tokens: r8_ followed by 40+ alphanumeric chars\n regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,\n },\n {\n type: 'perplexity_key',\n // Perplexity API keys: pplx- followed by 40+ alphanumeric chars\n regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,\n },\n {\n type: 'groq_key',\n // Groq API keys: gsk_ followed by 40+ alphanumeric chars\n regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,\n },\n {\n type: 'bearer_token',\n // Anchored with alternation instead of negative lookahead \u2014 avoids V8\n // backtracking risk on adversarial input. Bounded at 512 chars.\n // Min 12 chars: some OAuth providers issue shorter-lived tokens (< 20\n // chars). A 12-char base64 string has ~71 bits of entropy \u2014 above the\n // threshold where random strings are unlikely to produce false matches.\n // The trailing boundary is a NON-consuming lookahead: two adjacent bearer\n // tokens sharing a single delimiter (`Bearer a\u2026 Bearer b\u2026`) must both be\n // redacted. A consuming trailing delimiter would eat the separator the\n // next match needs for its leading anchor, leaking the second token.\n regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g,\n },\n {\n type: 'high_entropy_env',\n // Anchored with alternation instead of lookbehind to avoid backtracking.\n // Value bounded at 512 chars.\n // The trailing boundary is a NON-consuming lookahead so two secrets\n // separated by a single delimiter (one space OR one newline, e.g.\n // `printenv` / `.env` dumps: `API_KEY=\u2026 \\n SESSION_TOKEN=\u2026`) are BOTH\n // redacted. A consuming trailing `\\s` would swallow the separator the\n // next match needs for its leading anchor, so every other secret would\n // leak in plaintext.\n // The leading delimiter is CAPTURED (group 1) and re-emitted by the\n // replacement so the separator between adjacent secrets is preserved\n // rather than collapsed. Capture groups are therefore: 1=leading\n // delimiter, 2=key name, 3=value.\n regex: /(^|\\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\\s*[:=]\\s*['\"]?([A-Za-z0-9_/+=-]{20,512})['\"]?(?=\\s|$)/g,\n },\n];\n\n/**\n * `high_entropy_env` is the one pattern that needs special replacement logic\n * (it preserves the key name), so it runs in its own pass. Every other pattern\n * is folded into a single combined regex. Derive the split by type rather than\n * by hard-coded indices so adding/removing a pattern can't silently drop one.\n */\nconst SIMPLE_PATTERNS = PATTERNS.filter((p) => p.type !== 'high_entropy_env');\n\n/**\n * Combined single-pass regex for all simple patterns. Each alternative is a\n * capturing group so the callback can determine which original pattern fired\n * (only one group is non-undefined at match time). Order matches SIMPLE_PATTERNS\n * (longer/more-specific prefixes first). Relies on each simple pattern source\n * containing no internal capturing groups \u2014 only `(?:...)` and lookarounds.\n */\nconst COMBINED_REGEX = new RegExp(SIMPLE_PATTERNS.map((p) => `(${p.regex.source})`).join('|'), 'g');\n\n/** Separate pattern for high_entropy_env (different replacement logic). */\nconst HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === 'high_entropy_env')!.regex;\n\n/**\n * Replacements for the combined patterns, parallel to SIMPLE_PATTERNS. The\n * combined-regex callback indexes into this with the matched group's position.\n */\nconst COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);\n\n/**\n * Per-chunk cap. Splits long inputs into 64 KB chunks to keep scrub() memory\n * bounded. Real scrub() inputs (LLM responses, tool outputs) are typically\n * much smaller; this cap handles edge cases without impacting normal usage.\n */\nconst SCRUB_CHUNK_BYTES = 64 * 1024;\n\n/**\n * Overlap window used to nudge a chunk boundary onto a safe separator so a\n * secret straddling the 64 KB cut isn't split in half (which would leave\n * neither half matching, leaking the secret verbatim).\n *\n * Sized above the longest BOUNDED credential pattern: `high_entropy_env`\n * caps its value at 512 chars (+ key name + quotes \u2248 560) and `bearer_token`\n * at 512; every prefix-keyed pattern is far shorter. Because all of these\n * patterns are whitespace-free, the first whitespace at/after the nominal cut\n * is guaranteed to sit *past the end* of any such secret \u2014 so snapping the\n * boundary forward to it keeps every bounded secret wholly inside one chunk.\n * 1 KB gives comfortable headroom over the 560-char worst case.\n */\nconst SCRUB_OVERLAP_BYTES = 1024;\n\n/**\n * Quick pre-scan: check if the text contains any substring that MUST be\n * present for a credential pattern to match. If none are found, the text\n * is guaranteed clean \u2014 skip all regex passes (2 total: 16-pattern combined + high_entropy_env).\n *\n * Each anchor is the shortest unique substring from the corresponding pattern.\n * V8's `String.includes()` is hand-tuned C++ \u2014 O(n) with near-zero overhead\n * for typical tool-output lengths (100\u20135000 chars). A single combined regex\n * via `text.search()` is consistently slower for this many alternatives.\n */\nfunction hasCredentialAnchors(text: string): boolean {\n return (\n text.includes('-----BEGIN') || // Private keys (most unique \u2192 cheap reject)\n text.includes('sk-') || // Anthropic + OpenAI keys\n text.includes('sk_') || // Stripe live/test keys\n text.includes('ghp_') || // GitHub PAT v1\n text.includes('github_pat_') || // GitHub PAT v2\n text.includes('eyJ') || // JWT\n text.includes('AKIA') || // AWS access key\n text.includes('AIza') || // GCP service key\n text.includes('xox') || // Slack token (xoxa/xoxb/xoxp/xoxo/xoxs)\n text.includes('Bearer ') || // Bearer token (space suffix reduces false positives)\n text.includes('/bot') || // Telegram bot token (URL path pattern)\n text.includes('hf_') || // HuggingFace token\n text.includes('r8_') || // Replicate token\n text.includes('pplx-') || // Perplexity API key\n text.includes('gsk_') || // Groq API key\n text.includes('_KEY=') || // High-entropy env vars: API_KEY=, SECRET_KEY=, ...\n text.includes('_TOKEN=') || // ACCESS_TOKEN=, AUTH_TOKEN=, ...\n text.includes('_SECRET=') || // API_SECRET=, CLIENT_SECRET=, ...\n text.includes('_PASSWORD=') || // DB_PASSWORD=, ROOT_PASSWORD=, ...\n text.includes('mongodb://') ||\n text.includes('mongodb+srv://') ||\n text.includes('postgres://') ||\n text.includes('postgresql://') ||\n text.includes('mysql://') ||\n text.includes('redis://') ||\n // JSON-style credential keys: tool outputs often serialise objects as\n // raw JSON strings rather than parsed objects. The value may not start\n // with a recognisable prefix (sk-, ghp_, etc.), so we anchor on common\n // key names. The `\"` prefix avoids matching prose that happens to mention\n // these words \u2014 JSON serialisation always quotes string keys.\n text.includes('\"apiKey\"') ||\n text.includes('\"api_key\"') ||\n text.includes('\"token\"') ||\n text.includes('\"secret\"') ||\n text.includes('\"password\"') ||\n text.includes('\"authorization\"') ||\n text.includes('\"bearer\"') ||\n text.includes('\"private_key\"') ||\n text.includes('\"access_token\"') ||\n text.includes('\"refresh_token\"') ||\n text.includes('\"client_secret\"')\n );\n}\n\nexport class DefaultSecretScrubber implements SecretScrubber {\n scrub(text: string): string {\n if (!text) return text;\n\n // Fast path: if no credential anchor substrings exist in the text,\n // none of the 17 regex patterns can match. Skip all regex work.\n // This covers the vast majority of tool outputs (~95% of calls on\n // typical sessions are file paths, status messages, diffs, etc.).\n if (!hasCredentialAnchors(text)) return text;\n\n // For oversize inputs, scrub in fixed chunks to keep memory bounded.\n // The boundary is snapped FORWARD to the next whitespace within an\n // overlap window so a secret straddling the nominal 64 KB cut is never\n // split in half. Every bounded credential pattern is whitespace-free, so\n // the next whitespace at/after the cut necessarily falls past the end of\n // any such secret \u2014 guaranteeing it stays wholly inside the current chunk.\n if (text.length <= SCRUB_CHUNK_BYTES) {\n return this.scrubOne(text);\n }\n const out: string[] = [];\n let i = 0;\n while (i < text.length) {\n let end = Math.min(i + SCRUB_CHUNK_BYTES, text.length);\n if (end < text.length) {\n // Look for the first whitespace at/after the nominal cut, bounded by\n // the overlap window. Extending forward (not backward) ensures any\n // secret that began before `end` finishes before the new boundary.\n const limit = Math.min(end + SCRUB_OVERLAP_BYTES, text.length);\n let safe = -1;\n for (let j = end; j < limit; j++) {\n const ch = text.charCodeAt(j);\n // space, \\t, \\n, \\r\n if (ch === 32 || ch === 9 || ch === 10 || ch === 13) {\n safe = j;\n break;\n }\n }\n // Snap onto the whitespace if found; otherwise fall back to the hard\n // cut (an unbroken >1 KB run with no whitespace can't be a bounded\n // secret anyway \u2014 those are all \u2264 ~560 chars and whitespace-free).\n end = safe === -1 ? end : safe + 1;\n }\n out.push(this.scrubOne(text.slice(i, end)));\n i = end;\n }\n return out.join('');\n }\n\n private scrubOne(text: string): string {\n // Redundant guard: if we reached scrubOne via the chunked path, the\n // chunk may have been small enough to anchor-skip independently.\n if (!hasCredentialAnchors(text)) return text;\n\n // Pass 1: combined single-pass regex for all simple patterns. Each\n // alternative is a capturing group; only the group that matched is\n // non-undefined. The trailing offset/string args replace() appends are\n // always defined, so the matched group (which precedes them) is found first.\n let out = text.replace(\n COMBINED_REGEX,\n (match, ...groups) => {\n // groups[i] corresponds to SIMPLE_PATTERNS[i]; find which one fired.\n const idx = groups.findIndex((g) => g !== undefined);\n if (idx < 0) return match;\n const replacement = COMBINED_REPLACEMENTS[idx];\n return replacement !== undefined ? replacement : match;\n },\n );\n\n // Pass 2: high_entropy_env needs special handling \u2014 preserve the key name.\n // Groups: 1=leading delimiter (re-emitted so adjacent-secret separators\n // aren't collapsed), 2=key name, 3=value (redacted).\n out = out.replace(HIGH_ENTROPY_REGEX, (_match, lead, key, _value) => {\n return `${lead}${key}=[REDACTED:high_entropy_env]`;\n });\n\n return out;\n }\n\n /**\n * Recursively scrub every string value in an object/array graph. Secrets can\n * appear under any key \u2014 a URL query param, an `authorization` header, an\n * arbitrarily-named nested field \u2014 so we don't gate recursion on key names.\n * The per-string `scrub()` fast-path (anchor pre-scan) keeps this cheap: any\n * value without a credential anchor returns immediately without regex work.\n */\n scrubObject<T>(obj: T): T {\n const seen = new WeakSet();\n const visit = (v: unknown): unknown => {\n if (typeof v === 'string') return this.scrub(v);\n if (v === null || typeof v !== 'object') return v;\n if (seen.has(v as object)) return v;\n seen.add(v as object);\n if (Array.isArray(v)) return v.map(visit);\n const out: Record<string, unknown> = {};\n for (const [k, val] of Object.entries(v as Record<string, unknown>)) {\n out[k] = visit(val);\n }\n return out;\n };\n return visit(obj) as T;\n }\n}\n", "import type { FileHandle } from 'node:fs/promises';\nimport {\n type AtomicWriteOptions,\n createPersistencePrimitives,\n type FileLockOptions,\n} from '@wrongstack/persistence';\nimport { FsError } from '../types/errors.js';\n\nexport type { AtomicWriteOptions, FileLockOptions } from '@wrongstack/persistence';\n\nconst primitives = createPersistencePrimitives({\n createLockTimeoutError: ({ targetPath, timeoutMs }) =>\n new FsError({\n message: `Timed out waiting for file lock: ${targetPath}`,\n code: 'FS_ATOMIC_WRITE_FAILED',\n path: targetPath,\n context: { timeoutMs },\n }),\n});\n\nexport const atomicWrite: (\n targetPath: string,\n content: string | Uint8Array,\n opts?: AtomicWriteOptions,\n) => Promise<void> = primitives.atomicWrite;\n/**\n * Atomic replace that streams its new contents instead of taking them as one\n * buffer. Use when the replacement is large enough that materializing it would\n * itself be the memory problem (log rotation, tail compaction).\n */\nexport const atomicReplaceWithWriter: <T>(\n targetPath: string,\n write: (handle: FileHandle) => Promise<T>,\n opts?: AtomicWriteOptions,\n) => Promise<T> = primitives.atomicReplaceWithWriter;\nexport const ensureDir: (dir: string) => Promise<void> = primitives.ensureDir;\nexport const withFileLock: <T>(\n targetPath: string,\n fn: () => Promise<T>,\n opts?: FileLockOptions,\n) => Promise<T> = primitives.withFileLock;\n", "/**\n * Converts an unknown error value to a human-readable string.\n * Used in 40+ files across the codebase to normalize error messaging.\n */\nexport function toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n", "import { toErrorMessage } from '../utils/error.js';\n\n/**\n * WrongStack error hierarchy.\n *\n * Every error thrown by the framework is a `WrongStackError` with a\n * machine-readable `code`, a `subsystem` tag, and a `severity` level.\n * This lets consumers (CLI, TUI, plugins, tests) branch on structured\n * data instead of parsing error messages.\n */\n\n// \u2500\u2500 Error codes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Machine-readable error codes as frozen constants.\n *\n * Use `ERROR_CODES.X` instead of raw string literals for:\n * - IDE autocomplete and compile-time validation\n * - Safe refactoring (rename updates all usages)\n * - Plugin extensibility (extend the object to add custom codes)\n *\n * The `ErrorCode` type is derived from this object, so adding a new\n * code here automatically updates the type without extra changes.\n */\nexport const ERROR_CODES = {\n // Provider\n PROVIDER_RATE_LIMITED: 'PROVIDER_RATE_LIMITED',\n PROVIDER_AUTH_FAILED: 'PROVIDER_AUTH_FAILED',\n PROVIDER_OVERLOADED: 'PROVIDER_OVERLOADED',\n PROVIDER_INVALID_REQUEST: 'PROVIDER_INVALID_REQUEST',\n PROVIDER_SERVER_ERROR: 'PROVIDER_SERVER_ERROR',\n PROVIDER_NETWORK_ERROR: 'PROVIDER_NETWORK_ERROR',\n PROVIDER_CONTEXT_OVERFLOW: 'PROVIDER_CONTEXT_OVERFLOW',\n // Tool\n TOOL_NOT_FOUND: 'TOOL_NOT_FOUND',\n TOOL_PERMISSION_DENIED: 'TOOL_PERMISSION_DENIED',\n TOOL_EXECUTION_FAILED: 'TOOL_EXECUTION_FAILED',\n TOOL_TIMEOUT: 'TOOL_TIMEOUT',\n TOOL_INPUT_INVALID: 'TOOL_INPUT_INVALID',\n // Config\n CONFIG_INVALID: 'CONFIG_INVALID',\n CONFIG_NOT_FOUND: 'CONFIG_NOT_FOUND',\n CONFIG_PARSE_FAILED: 'CONFIG_PARSE_FAILED',\n CONFIG_MIGRATION_NEEDED: 'CONFIG_MIGRATION_NEEDED',\n // Plugin\n PLUGIN_LOAD_FAILED: 'PLUGIN_LOAD_FAILED',\n PLUGIN_API_MISMATCH: 'PLUGIN_API_MISMATCH',\n PLUGIN_MISSING_DEPENDENCY: 'PLUGIN_MISSING_DEPENDENCY',\n // Agent\n AGENT_ITERATION_LIMIT: 'AGENT_ITERATION_LIMIT',\n AGENT_CONTEXT_OVERFLOW: 'AGENT_CONTEXT_OVERFLOW',\n AGENT_ABORTED: 'AGENT_ABORTED',\n AGENT_RUN_FAILED: 'AGENT_RUN_FAILED',\n // Session\n SESSION_NOT_FOUND: 'SESSION_NOT_FOUND',\n SESSION_CORRUPTED: 'SESSION_CORRUPTED',\n SESSION_WRITE_FAILED: 'SESSION_WRITE_FAILED',\n // Container / Registry\n CONTAINER_TOKEN_ALREADY_BOUND: 'CONTAINER_TOKEN_ALREADY_BOUND',\n CONTAINER_TOKEN_NOT_BOUND: 'CONTAINER_TOKEN_NOT_BOUND',\n CONTAINER_CIRCULAR_DEPENDENCY: 'CONTAINER_CIRCULAR_DEPENDENCY',\n REGISTRY_DUPLICATE: 'REGISTRY_DUPLICATE',\n REGISTRY_NOT_FOUND: 'REGISTRY_NOT_FOUND',\n REGISTRY_INVALID: 'REGISTRY_INVALID',\n // File system\n FS_READ_FAILED: 'FS_READ_FAILED',\n FS_WRITE_FAILED: 'FS_WRITE_FAILED',\n FS_MKDIR_FAILED: 'FS_MKDIR_FAILED',\n FS_DELETE_FAILED: 'FS_DELETE_FAILED',\n FS_ATOMIC_WRITE_FAILED: 'FS_ATOMIC_WRITE_FAILED',\n // SDD (Spec-Driven Development)\n SDD_VALIDATION_FAILED: 'SDD_VALIDATION_FAILED',\n SDD_PARSE_FAILED: 'SDD_PARSE_FAILED',\n SDD_INVALID_STATE: 'SDD_INVALID_STATE',\n SDD_NOT_READY: 'SDD_NOT_READY',\n // General\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n PARSE_FAILED: 'PARSE_FAILED',\n UNKNOWN: 'UNKNOWN',\n} as const;\n\n/**\n * Union type derived from `ERROR_CODES`. Using `typeof ERROR_CODES[keyof typeof ERROR_CODES]`\n * instead of a string literal union means TypeScript auto-updates the type whenever\n * a new code is added to `ERROR_CODES` \u2014 no need to keep two lists in sync.\n */\nexport type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];\n\nexport type ErrorSubsystem =\n | 'provider'\n | 'tool'\n | 'config'\n | 'plugin'\n | 'agent'\n | 'session'\n | 'sdd'\n | 'container'\n | 'fs'\n | 'general';\nexport type ErrorSeverity = 'fatal' | 'error' | 'warning';\n\n// \u2500\u2500 Base error class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class WrongStackError extends Error {\n readonly code: ErrorCode;\n readonly subsystem: ErrorSubsystem;\n readonly severity: ErrorSeverity;\n readonly recoverable: boolean;\n readonly context?: Record<string, unknown> | undefined;\n\n constructor(opts: {\n message: string;\n code: ErrorCode;\n subsystem: ErrorSubsystem;\n severity?: ErrorSeverity | undefined;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super(opts.message, { cause: opts.cause });\n this.name = 'WrongStackError';\n this.code = opts.code;\n this.subsystem = opts.subsystem;\n this.severity = opts.severity ?? 'error';\n this.recoverable = opts.recoverable ?? false;\n this.context = opts.context;\n }\n\n /**\n * Render a one-line user-facing description.\n * Subclasses should override for domain-specific formatting.\n */\n describe(): string {\n const ctx = this.context ? ` ${formatContext(this.context)}` : '';\n return `${this.code}: ${this.message}${ctx}`;\n }\n}\n\nfunction formatContext(ctx: Record<string, unknown>): string {\n const parts = Object.entries(ctx)\n .filter(([, v]) => v !== undefined)\n .slice(0, 3)\n .map(([k, v]) => `${k}=${String(v)}`);\n return parts.length > 0 ? `[${parts.join(' ')}]` : '';\n}\n\n// \u2500\u2500 Specific error classes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Tool execution errors \u2014 thrown by ToolExecutor and individual tools.\n */\nexport class ToolError extends WrongStackError {\n readonly toolName: string;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n | 'TOOL_NOT_FOUND'\n | 'TOOL_PERMISSION_DENIED'\n | 'TOOL_EXECUTION_FAILED'\n | 'TOOL_TIMEOUT'\n | 'TOOL_INPUT_INVALID'\n >;\n toolName: string;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'tool',\n recoverable: opts.recoverable,\n context: { tool: opts.toolName, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ToolError';\n this.toolName = opts.toolName;\n }\n}\n\n/**\n * Config loading / validation errors.\n */\nexport class ConfigError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'CONFIG_INVALID' | 'CONFIG_NOT_FOUND' | 'CONFIG_PARSE_FAILED' | 'CONFIG_MIGRATION_NEEDED'\n >;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'config',\n severity: 'fatal',\n recoverable: false,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'ConfigError';\n }\n}\n\n/**\n * Plugin loading / lifecycle errors.\n */\nexport class PluginError extends WrongStackError {\n readonly pluginName: string;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'PLUGIN_LOAD_FAILED' | 'PLUGIN_API_MISMATCH' | 'PLUGIN_MISSING_DEPENDENCY'\n >;\n pluginName: string;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'plugin',\n severity: 'error',\n recoverable: opts.code === ERROR_CODES.PLUGIN_MISSING_DEPENDENCY,\n context: { plugin: opts.pluginName, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'PluginError';\n this.pluginName = opts.pluginName;\n }\n}\n\n/**\n * Agent runtime errors \u2014 thrown by Agent.run when a non-WrongStackError\n * escapes the inner loop, so callers always see a structured error.\n */\nexport class AgentError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'AGENT_ITERATION_LIMIT' | 'AGENT_CONTEXT_OVERFLOW' | 'AGENT_ABORTED' | 'AGENT_RUN_FAILED'\n >;\n recoverable?: boolean | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'agent',\n severity: opts.code === ERROR_CODES.AGENT_ABORTED ? 'warning' : 'error',\n recoverable: opts.recoverable ?? opts.code === ERROR_CODES.AGENT_ITERATION_LIMIT,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'AgentError';\n }\n}\n\n/**\n * Wrap an arbitrary thrown value into a `WrongStackError` so the caller\n * always gets a structured error. Pass-throughs WrongStackError instances\n * unchanged; raw `Error`s and primitives get an `AGENT_RUN_FAILED` wrapper\n * with the original preserved as `cause`.\n */\nexport function toWrongStackError(\n err: unknown,\n code: Extract<ErrorCode, 'AGENT_RUN_FAILED' | 'AGENT_ABORTED' | 'UNKNOWN'> = ERROR_CODES.AGENT_RUN_FAILED,\n): WrongStackError {\n if (err instanceof WrongStackError) return err;\n const message = toErrorMessage(err);\n return new AgentError({\n message,\n code: code === 'UNKNOWN' ? ERROR_CODES.AGENT_RUN_FAILED : code,\n cause: err,\n });\n}\n\n/**\n * Session storage errors.\n */\nexport class SessionError extends WrongStackError {\n readonly sessionId?: string | undefined;\n\n constructor(opts: {\n message: string;\n code: Extract<ErrorCode, 'SESSION_NOT_FOUND' | 'SESSION_CORRUPTED' | 'SESSION_WRITE_FAILED'>;\n sessionId?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'session',\n severity: opts.code === ERROR_CODES.SESSION_WRITE_FAILED ? 'error' : 'warning',\n recoverable: opts.code !== ERROR_CODES.SESSION_CORRUPTED,\n context: { sessionId: opts.sessionId, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'SessionError';\n this.sessionId = opts.sessionId;\n }\n}\n\n/**\n * SDD (Spec-Driven Development) errors \u2014 spec validation, parsing, and\n * state machine violations in the AISpecBuilder, TaskFlow, and TaskTracker.\n */\nexport class SddError extends WrongStackError {\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'SDD_VALIDATION_FAILED' | 'SDD_PARSE_FAILED' | 'SDD_INVALID_STATE' | 'SDD_NOT_READY'\n >;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'sdd',\n severity: opts.code === ERROR_CODES.SDD_PARSE_FAILED ? 'warning' : 'error',\n recoverable: opts.code === ERROR_CODES.SDD_NOT_READY,\n context: opts.context,\n cause: opts.cause,\n });\n this.name = 'SddError';\n }\n}\n\n/**\n * File system operation errors.\n */\nexport class FsError extends WrongStackError {\n readonly path?: string | undefined;\n\n constructor(opts: {\n message: string;\n code: Extract<\n ErrorCode,\n 'FS_READ_FAILED' | 'FS_WRITE_FAILED' | 'FS_MKDIR_FAILED' | 'FS_DELETE_FAILED' | 'FS_ATOMIC_WRITE_FAILED'\n >;\n path?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: opts.code,\n subsystem: 'fs',\n severity: 'error',\n recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,\n context: { path: opts.path, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'FsError';\n this.path = opts.path;\n }\n}\n\n/**\n * HTTP fetch error \u2014 thrown when a network request returns a non-OK status.\n * Carries the response status so {@link classifyToolError} can branch on it\n * (429 \u2192 transient, 404 \u2192 not_found, 401 \u2192 permission) without duck-typing\n * the error via `'response' in err`.\n *\n * P3 #18 (before-release.md): the previous `'response' in err` check caught\n * any Error with a `response` property, including custom errors, proxy\n * objects, or mocked errors in tests. `instanceof FetchError` is reliable.\n *\n * Tools and providers that make HTTP requests and need the executor to\n * classify their failures should throw `new FetchError({ status, message })`\n * instead of a bare `Error` with an ad-hoc `response` field.\n */\nexport class FetchError extends WrongStackError {\n readonly status: number;\n\n constructor(opts: {\n message: string;\n status: number;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n severity: 'error',\n recoverable: opts.status === 429 || opts.status >= 500,\n context: { status: opts.status, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'FetchError';\n this.status = opts.status;\n }\n}\n\n/**\n * Tool input validation error \u2014 thrown when a tool's input fails a validation\n * check that the JSON Schema cannot express (e.g. `old_string === new_string`\n * in edit, or a cross-field invariant). Use this instead of a bare\n * `throw new Error('...validation...')` so {@link classifyToolError} can\n * match on `instanceof` rather than a locale-dependent message substring.\n *\n * P2 #6 (before-release.md): the previous `err.message.includes('validation')`\n * check misclassified any error whose message happened to contain \"validation\"\n * (e.g. a third-party \"input validation timeout\") as a VALIDATION error.\n *\n * Named `ToolValidationError` (not `ValidationError`) to avoid colliding with\n * the existing `ValidationError` interface exported by json-schema-validate.ts\n * (a validation-result shape, not an Error subclass).\n */\nexport class ToolValidationError extends WrongStackError {\n constructor(opts: {\n message: string;\n /** Field path or tool name that failed validation, for diagnostics. */\n field?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n severity: 'error',\n recoverable: false,\n context: { field: opts.field, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ToolValidationError';\n }\n}\n\n/**\n * Response / payload parse error \u2014 thrown when an upstream HTTP response,\n * file, or data structure is well-formed at the transport layer (HTTP 200,\n * valid JSON) but is missing required fields or has an unexpected shape.\n *\n * Distinct from `ConfigError(CONFIG_PARSE_FAILED)` (which is specifically\n * for config-file parsing) and `FetchError` (which covers HTTP non-OK\n * responses). `ParseError` fills the gap: the request succeeded but the\n * response body couldn't be interpreted.\n *\n * Common sites: OAuth token responses missing `access_token`, device-code\n * responses missing `device_code`, registry responses with unexpected\n * schemas.\n */\nexport class ParseError extends WrongStackError {\n readonly source?: string | undefined;\n\n constructor(opts: {\n message: string;\n /**\n * What was being parsed \u2014 e.g. `'oauth-token-response'`,\n * `'device-code-response'`. Lets consumers distinguish parse failures\n * from different upstream APIs without parsing the message.\n */\n source?: string | undefined;\n context?: Record<string, unknown> | undefined;\n cause?: unknown | undefined;\n }) {\n super({\n message: opts.message,\n code: ERROR_CODES.PARSE_FAILED,\n subsystem: 'general',\n severity: 'error',\n recoverable: false,\n context: { source: opts.source, ...opts.context },\n cause: opts.cause,\n });\n this.name = 'ParseError';\n this.source = opts.source;\n }\n}\n\n// \u2500\u2500 Type guards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function isWrongStackError(err: unknown): err is WrongStackError {\n return err instanceof WrongStackError;\n}\n\nexport function isToolError(err: unknown): err is ToolError {\n return err instanceof ToolError;\n}\n\nexport function isConfigError(err: unknown): err is ConfigError {\n return err instanceof ConfigError;\n}\n\nexport function isPluginError(err: unknown): err is PluginError {\n return err instanceof PluginError;\n}\n\nexport function isSessionError(err: unknown): err is SessionError {\n return err instanceof SessionError;\n}\n\nexport function isAgentError(err: unknown): err is AgentError {\n return err instanceof AgentError;\n}\n\nexport function isFsError(err: unknown): err is FsError {\n return err instanceof FsError;\n}\n\nexport function isToolValidationError(err: unknown): err is ToolValidationError {\n return err instanceof ToolValidationError;\n}\n\nexport function isFetchError(err: unknown): err is FetchError {\n return err instanceof FetchError;\n}\n\nexport function isParseError(err: unknown): err is ParseError {\n return err instanceof ParseError;\n}\n\nexport function isSddError(err: unknown): err is SddError {\n return err instanceof SddError;\n}\n", "/**\n * Build a sanitized child-process environment.\n *\n * The bash/exec tools and MCP stdio transports execute LLM-generated or\n * configured commands. The parent process carries provider API keys\n * (ANTHROPIC_API_KEY, OPENAI_API_KEY, ...), VCS tokens (GITHUB_TOKEN),\n * and cloud credentials. Forwarding those to a child is an exfiltration\n * vector even with `permission: 'confirm'` \u2014 a compromised MCP server\n * or a cleverly composed shell pipeline can leak secrets.\n *\n * Strategy: copy a small, explicit allowlist of variables that real builds\n * need, then copy anything else that does NOT look secret-bearing. This\n * preserves user-friendly behavior (locale, terminal, npm config) while\n * blocking the obvious leak channels. Two value-side guards back up the\n * name-based filter:\n * - any value carrying an embedded URI credential (`scheme://user:pass@host`,\n * e.g. `DATABASE_URL`/`REDIS_URL`/`*_DSN`) is dropped (WS-01);\n * - `NODE_OPTIONS` is forwarded but with module-preload directives\n * (`--require`/`--import`/`--loader`) stripped so a parent-set value can't\n * inject code into node children (WS-02).\n *\n * Override with `WRONGSTACK_CHILD_ENV_PASSTHROUGH=1` to forward the full\n * parent environment unchanged (opt-in for advanced users who understand\n * the risk).\n */\n\nconst ALLOWED_KEYS = new Set<string>([\n 'PATH',\n 'HOME',\n 'USER',\n 'USERNAME',\n 'LOGNAME',\n 'SHELL',\n 'LANG',\n 'LC_ALL',\n 'LC_CTYPE',\n 'TERM',\n 'TZ',\n 'TMPDIR',\n 'TEMP',\n 'TMP',\n 'PWD',\n 'OLDPWD',\n 'COMSPEC',\n 'SYSTEMROOT',\n 'SYSTEMDRIVE',\n 'WINDIR',\n 'PROGRAMFILES',\n 'PROGRAMFILES(X86)',\n 'PROGRAMDATA',\n 'APPDATA',\n 'LOCALAPPDATA',\n 'USERPROFILE',\n 'PUBLIC',\n 'PATHEXT',\n]);\n\n// Substring match against env-var names (case-insensitive). Bias toward\n// false-positives \u2014 a missing var is recoverable, an exfiltrated key is not.\n// Only consulted for vars NOT on the curated allowlist; PWD/PASSWD-style\n// false positives there are avoided by checking allowlist first.\nconst SECRET_NAME_PARTS = [\n 'TOKEN',\n 'SECRET',\n 'PASSWORD',\n 'PASSWD',\n 'AUTH',\n 'CRED',\n 'BEARER',\n 'COOKIE',\n 'PRIVATE',\n];\n\nfunction looksSecret(name: string): boolean {\n const upper = name.toUpperCase();\n for (const p of SECRET_NAME_PARTS) {\n if (upper.includes(p)) return true;\n }\n // KEY is tricky \u2014 PUBLIC_KEY is fine to forward but most _KEY vars are\n // secrets. Require word boundary so KEYBOARD_LAYOUT etc. are not flagged.\n if (/(?:^|_)KEY(?:$|_|S$)/i.test(upper)) return true;\n if (/API[_-]?KEY/i.test(upper)) return true;\n if (/ACCESS[_-]?KEY/i.test(upper)) return true;\n if (/SESSION[_-]?ID/i.test(upper) === false && /SESSION/i.test(upper)) {\n // SESSION_ID is metadata (we set our own); other SESSION_* often holds\n // session cookies. Be conservative.\n return true;\n }\n return false;\n}\n\n/**\n * Value-side secret detection (WS-01). The name-based `looksSecret` filter\n * misses connection-string variables whose NAME is innocuous but whose VALUE\n * embeds a password \u2014 e.g. `DATABASE_URL=postgres://user:pass@host`,\n * `REDIS_URL=redis://:pass@host`, `MONGO_URI`, `AMQP_URL`, `*_DSN`. Forwarding\n * these to a child (bash/exec/MCP server) leaks the embedded credential.\n *\n * Matches a URI userinfo component that contains a password, i.e.\n * `scheme://[user]:<password>@host`. Deliberately precise: a credential-free\n * URL (`https://api.example.com`, `https://user@host` with no password) is NOT\n * matched, so non-secret `*_URL` knobs (registries, endpoints) still forward.\n */\nfunction valueHasEmbeddedCredential(value: string): boolean {\n // scheme:// then optional user, a ':' , a non-empty password, then '@'.\n // Userinfo chars stop at '/', whitespace, ':' (separator) and '@'.\n return /\\b[a-z][a-z0-9+.-]*:\\/\\/[^/\\s:@]*:[^/\\s@]+@/i.test(value);\n}\n\n/**\n * Code-injection directives that turn `NODE_OPTIONS` into an RCE channel by\n * preloading an arbitrary module into every node child process (WS-02).\n */\nconst NODE_OPTIONS_INJECTION_FLAG =\n /^(?:--require|-r|--import|--loader|--experimental-loader)$/;\nconst NODE_OPTIONS_INJECTION_FLAG_EQ =\n /^(?:--require|-r|--import|--loader|--experimental-loader)=/;\n\n/**\n * Strip module-preload directives from a `NODE_OPTIONS` value while preserving\n * benign flags (`--no-warnings`, `--max-old-space-size=\u2026`, etc.). Handles both\n * the `--require=./x.js` and space-separated `--require ./x.js` forms. Returns\n * the sanitized string (possibly empty).\n */\nexport function sanitizeNodeOptions(value: string): string {\n const tokens = value.split(/\\s+/).filter(Boolean);\n const kept: string[] = [];\n for (let i = 0; i < tokens.length; i++) {\n const tok = tokens[i] as string;\n if (NODE_OPTIONS_INJECTION_FLAG_EQ.test(tok)) continue; // --require=./x\n if (NODE_OPTIONS_INJECTION_FLAG.test(tok)) {\n i++; // also drop the following path token (--require ./x)\n continue;\n }\n kept.push(tok);\n }\n return kept.join(' ');\n}\n\nexport interface BuildChildEnvOptions {\n /** Session ID to inject as WRONGSTACK_SESSION_ID. */\n sessionId?: string | undefined;\n /** Additional env vars to merge (takes priority over filtered parent env). */\n extra?: NodeJS.ProcessEnv | undefined;\n}\n\n/**\n * Commit identity applied to every git-touching child process via the\n * `GIT_AUTHOR_*` / `GIT_COMMITTER_*` env vars. Env-var identity outranks\n * every `git config` layer (repo/global/system), so commits made by any\n * tool (git tool, bash/exec, worktree manager, plugins) carry this\n * name/email without touching the user's git config \u2014 hand-made commits\n * in a normal terminal are unaffected.\n */\nexport interface GitIdentity {\n name?: string | undefined;\n email?: string | undefined;\n}\n\nlet gitIdentity: GitIdentity | null = null;\n\n/**\n * Set (or clear with `null`) the git commit identity injected by\n * `buildChildEnv()`. Wired at boot from the user-level `config.git.identity`\n * (the config loader strips `git` from repo-committed in-project configs \u2014\n * identity spoofing must not be repo-controllable) and re-applied at runtime\n * by the `/gitid` slash command.\n */\nexport function configureChildEnvGitIdentity(identity: GitIdentity | null | undefined): void {\n const name = identity?.name?.trim();\n const email = identity?.email?.trim();\n gitIdentity = name || email ? { name: name || undefined, email: email || undefined } : null;\n}\n\n/** Current configured git identity, or null when none is set. */\nexport function getChildEnvGitIdentity(): Readonly<GitIdentity> | null {\n return gitIdentity;\n}\n\n/**\n * Build a filtered child-process environment suitable for bash, exec, and\n * MCP server subprocesses. Strips API keys, tokens, and other credentials\n * while preserving system/tooling variables.\n */\nexport function buildChildEnv(optsOrSessionId?: BuildChildEnvOptions | string): NodeJS.ProcessEnv {\n const opts: BuildChildEnvOptions =\n typeof optsOrSessionId === 'string'\n ? { sessionId: optsOrSessionId }\n : (optsOrSessionId ?? {});\n\n // WRONGSTACK_CHILD_ENV_PASSTHROUGH may NOT be set via config file.\n // It is a privileged override that opt-outs the entire credential filter\n // and must only be set by the operator's shell environment (real env var,\n // not something a config file injects into process.env). Config-file\n // sources do NOT go through process.env \u2014 only the actual shell environment\n // does \u2014 so checking Object.prototype.hasOwnProperty.call(process.env, ...)\n // is sufficient to exclude config-driven values.\n const hasOwn = Object.hasOwn(process.env, 'WRONGSTACK_CHILD_ENV_PASSTHROUGH');\n const legacyHasOwn = Object.hasOwn(process.env, 'WRONGSTACK_BASH_ENV_PASSTHROUGH');\n const passthrough = (hasOwn && process.env['WRONGSTACK_CHILD_ENV_PASSTHROUGH'] === '1')\n || (legacyHasOwn && process.env['WRONGSTACK_BASH_ENV_PASSTHROUGH'] === '1');\n if (passthrough && !process.env['CI']) {\n console.warn(\n '[agent] WARNING: WRONGSTACK_*_ENV_PASSTHROUGH=1 is active \u2014\\n' +\n ' all parent env vars (including API keys) forwarded to child processes.\\n' +\n ' Do not use on shared or multi-tenant systems.'\n );\n }\n const out: NodeJS.ProcessEnv = {};\n\n // The CLI entry defaults NODE_ENV=production (so React/Ink resolve their\n // production builds \u2014 see cli-main) and marks the injection with this\n // flag. The injected value must NOT reach children: NODE_ENV=production\n // makes `pnpm install` skip devDependencies and flips test-runner\n // behavior. Strip both vars whenever the flag says wrongstack set them \u2014\n // a NODE_ENV genuinely exported by the operator's shell (flag absent)\n // is forwarded unchanged. Applies in passthrough mode too: passthrough\n // means \"the operator's real environment\", which this value is not.\n const nodeEnvDefaulted = process.env['WRONGSTACK_NODE_ENV_DEFAULTED'] === '1';\n\n for (const [k, v] of Object.entries(process.env)) {\n if (v === undefined) continue;\n if (nodeEnvDefaulted && (k === 'NODE_ENV' || k === 'WRONGSTACK_NODE_ENV_DEFAULTED')) continue;\n if (passthrough) {\n out[k] = v;\n continue;\n }\n const upper = k.toUpperCase();\n // 0. Strip any value with an embedded URI credential (user:pass@host),\n // regardless of the variable name (WS-01). Applied before the allowlist\n // so even a \"system\" name carrying a connection string is caught.\n if (valueHasEmbeddedCredential(v)) continue;\n // 1. Forward names on the explicit allowlist \u2014 these are well-known\n // non-secret system variables (PATH, HOME, LANG, ...).\n if (ALLOWED_KEYS.has(upper)) {\n out[k] = v;\n continue;\n }\n // 2. Strip anything that looks like a secret.\n if (looksSecret(upper)) continue;\n // NODE_OPTIONS is forwarded (builds rely on flags like --no-warnings) but\n // module-preload directives (--require/--import/--loader) are stripped \u2014\n // they would let a parent-set NODE_OPTIONS inject code into every node\n // child (WS-02 defense-in-depth).\n if (upper === 'NODE_OPTIONS') {\n const sanitized = sanitizeNodeOptions(v);\n if (sanitized) out[k] = sanitized;\n continue;\n }\n // 3. Forward tooling-prefixed vars that builds commonly need, unless\n // they already failed the secret check above.\n if (\n upper.startsWith('NODE_') ||\n upper.startsWith('NPM_') ||\n upper.startsWith('PNPM_') ||\n upper.startsWith('YARN_') ||\n upper.startsWith('GIT_') ||\n upper.startsWith('CI') ||\n upper.startsWith('XDG_') ||\n // Our own non-secret knobs (WRONGSTACK_HOME, WRONGSTACK_SESSION_ID, \u2026).\n // Secrets never live in WRONGSTACK_* env vars (they're in the encrypted\n // vault). Forwarding keeps child wstack processes \u2014 e.g. ones spawned\n // by the test suite \u2014 inside the same redirected global root.\n upper.startsWith('WRONGSTACK_') ||\n upper === 'EDITOR' ||\n upper === 'VISUAL' ||\n upper === 'PAGER'\n ) {\n out[k] = v;\n }\n }\n\n // Configured commit identity. Applied in passthrough mode too \u2014 it is the\n // operator's explicit intent, not a parent-env leak. Placed BEFORE the\n // extras merge so a caller-provided GIT_* override still wins.\n if (gitIdentity) {\n if (gitIdentity.name) {\n out['GIT_AUTHOR_NAME'] = gitIdentity.name;\n out['GIT_COMMITTER_NAME'] = gitIdentity.name;\n }\n if (gitIdentity.email) {\n out['GIT_AUTHOR_EMAIL'] = gitIdentity.email;\n out['GIT_COMMITTER_EMAIL'] = gitIdentity.email;\n }\n }\n\n // Merge explicit extras AFTER filtering. Callers MUST treat `opts.extra`\n // as a small, user-authored allowlist (e.g. MCP server tokens, LSP env\n // overrides from config). Do NOT pass `process.env` or any object derived\n // from it \u2014 that would defeat the parent-env scrub above. The secret\n // filter is intentionally skipped here so legitimate secret-bearing\n // tokens the user explicitly configured can still reach the child.\n if (opts.extra) {\n Object.assign(out, opts.extra);\n }\n\n if (opts.sessionId) out['WRONGSTACK_SESSION_ID'] = opts.sessionId;\n return out;\n}\n", "/**\n * TTY detection helpers \u2014 the single source of truth for \"is this process\n * running against a real terminal?\". Replaces ad-hoc `process.stdin.isTTY`\n * / `process.stdout.isTTY` checks scattered across the codebase so that:\n *\n * 1. test code can mock a single module instead of stubbing `isTTY` on\n * every ReadStream/WriteStream the test happens to touch;\n * 2. a future TTY-detection source (an env var override, a Windows\n * ConPTY workaround, \u2026) lands in one place;\n * 3. `isInteractive()` encodes the rule the project already used inline\n * (\"both streams are TTYs AND we're not running under CI\") in one\n * testable helper instead of the same 3-condition check in two\n * different files.\n *\n * Scope: detection only. Raw-mode control (`setRawMode`), resize\n * subscriptions, and write-injection belong to a future, larger TTY\n * abstraction; this module is the smallest pull that gives us a\n * testable seam and dedups 20+ call sites.\n */\n\nconst hasStdout = (): boolean => typeof process !== 'undefined' && !!process.stdout;\nconst hasStdin = (): boolean => typeof process !== 'undefined' && !!process.stdin;\n\n/** True when `process.stdout` is attached to a terminal (not a pipe/file). */\nexport function isStdoutTTY(): boolean {\n return hasStdout() && Boolean(process.stdout.isTTY);\n}\n\n/** True when `process.stdin` is attached to a terminal (not a pipe/file). */\nexport function isStdinTTY(): boolean {\n return hasStdin() && Boolean(process.stdin.isTTY);\n}\n\n/**\n * True when the current process is an interactive session: both stdin and\n * stdout are TTYs. Callers that also need a \"not a single-shot invocation\"\n * or \"not under CI\" check should layer that on top \u2014 keeping this helper\n * minimal preserves the original inline checks it replaces.\n */\nexport function isInteractive(): boolean {\n return isStdinTTY() && isStdoutTTY();\n}\n\n/** Current terminal size in characters, with a 24\u00D780 fallback for non-TTYs. */\nexport function getTermSize(): { rows: number; cols: number } {\n if (!hasStdout()) return { rows: 24, cols: 80 };\n return {\n rows: process.stdout.rows ?? 24,\n cols: process.stdout.columns ?? 80,\n };\n}\n\n/**\n * Subscribe to terminal resize events. `cb` is called with the new size each\n * time the underlying stream emits `resize`. Returns a cleanup function the\n * caller MUST call on dispose to remove the listener \u2014 leaving a stale\n * `resize` listener on a disposed component leaks the closure (and the\n * component itself, transitively) until the process exits.\n *\n * The stream argument defaults to `process.stdout`. Pass an explicit\n * `NodeJS.WriteStream` when the caller already owns one (e.g. a status line\n * that targets an injected `out` for testability). For non-TTY streams no\n * listener is registered and the returned cleanup is a no-op.\n */\nexport function onResize(\n cb: (size: { rows: number; cols: number }) => void,\n stream: NodeJS.WriteStream = process.stdout,\n): () => void {\n if (!stream || typeof stream.on !== 'function') return () => {};\n const handler = (): void => {\n cb({\n rows: stream.rows ?? 24,\n cols: stream.columns ?? 80,\n });\n };\n stream.on('resize', handler);\n return () => {\n stream.off('resize', handler);\n };\n}\n\n/**\n * Toggle raw mode on a TTY stdin stream. Returns `true` when the toggle was\n * applied, `false` when the stream is null, not a TTY, or doesn't expose\n * `setRawMode` (pipes, file descriptors, Windows ConPTY edge cases). Callers\n * that need to restore the previous mode should snapshot `input.isRaw`\n * BEFORE the call and pass the value to a second call to flip back.\n *\n * Use this helper to drop the now-redundant\n * `if (input.isTTY) input.setRawMode(...)` ceremony at every call site.\n */\nexport function setRawMode(input: NodeJS.ReadStream, mode: boolean): boolean {\n if (input?.isTTY !== true) return false;\n if (typeof input.setRawMode !== 'function') return false;\n input.setRawMode(mode);\n return true;\n}\n\n/**\n * Bracket installed by the interactive input reader while a `readline`\n * prompt is on screen. Out-of-band terminal writes \u2014 logger WARN/INFO\n * lines, async activity from the Telegram bridge, etc. \u2014 go to the same\n * physical terminal as the half-typed prompt but readline has no idea they\n * happened, so it never repaints. The result is the classic corruption the\n * user sees: every async line strands the in-progress draft as a fresh\n * scrollback row (sometimes with its cursor underline).\n *\n * The guard closes that gap. `suspend()` wipes the draft row so the message\n * prints clean; `resume()` repaints the prompt + draft (cursor preserved).\n * When no prompt is active the guard is `null` and writes pass straight\n * through \u2014 so agent-turn output (spinner, renderer) is untouched.\n */\nexport interface OutputLineGuard {\n /** Clear the current input row right before an out-of-band write. */\n suspend(): void;\n /** Repaint the prompt + in-progress draft right after the write. */\n resume(): void;\n}\n\nlet activeOutputGuard: OutputLineGuard | null = null;\n\n/**\n * Register (or clear, with `null`) the guard that brackets out-of-band\n * writes. Installed by {@link writeOut}/{@link writeErr} consumers \u2014 in\n * practice the CLI's readline input reader \u2014 only while a prompt is live.\n * Idempotent; the most recent caller wins.\n */\nexport function setOutputLineGuard(guard: OutputLineGuard | null): void {\n activeOutputGuard = guard;\n}\n\n/**\n * Stream-agnostic write primitive. Returns `false` when the stream is\n * missing or doesn't expose `write` so callers can degrade silently under\n * hostile host environments (closed pipe, mock injects `null`, test\n * replaces the stream with a stub).\n *\n * When an {@link OutputLineGuard} is installed (a readline prompt is on\n * screen) the write is bracketed by `suspend()`/`resume()` so the user's\n * half-typed input survives the interruption instead of being stranded in\n * scrollback. The guard's own redraw uses raw stream writes \u2014 never\n * `writeOut`/`writeErr` \u2014 so there is no re-entrancy here.\n *\n * **Not exported in the public API.** Exposed only inside `term.ts` for\n * `writeOut` / `writeErr` to share a single implementation. If a caller\n * needs to write to an arbitrary stream, they should call `writeOut` (or\n * `writeErr`) with an explicit `stream` argument \u2014 the named functions\n * are the public surface so the \"this is the standard error stream\"\n * intent stays visible at every call site.\n */\nfunction writeTo(\n s: string,\n stream: NodeJS.WriteStream | undefined,\n): boolean {\n if (!stream || typeof stream.write !== 'function') return false;\n const guard = activeOutputGuard;\n if (!guard) {\n stream.write(s);\n return true;\n }\n // A prompt is live \u2014 wipe the draft row, emit the message, repaint.\n guard.suspend();\n stream.write(s);\n guard.resume();\n return true;\n}\n\n/**\n * Write `s` to `stream` (defaults to `process.stdout`). Returns `false`\n * when the stream is missing or doesn't expose `write` so callers can\n * degrade silently under hostile host environments (closed pipe, mock\n * injects `null`, test replaces the stream with a stub).\n *\n * Why a helper:\n * 1. **Single seam for output capture in tests** \u2014 stub `writeOut` once\n * and assert on what the rest of the codebase intended to print,\n * without spying on `process.stdout.write` (which is brittle and\n * leaks across parallel test files).\n * 2. **Stream swap without grep** \u2014 routing the CLI's output to a\n * logger or `out.log` becomes a one-line change at process boot.\n * 3. **Defensive default** \u2014 closes the \"what if `process.stdout` is\n * `null`\" gap that currently exists at ~50 call sites that just\n * call `process.stdout.write(s)` and crash on certain Windows\n * redirect invocations.\n *\n * Call-site migration is staged: this commit introduces the helper, a\n * follow-up commit replaces the 50+ `process.stdout.write(...)` sites\n * with `writeOut(...)`. Until that migration lands, both forms coexist\n * and `writeOut` is the preferred form for new code.\n */\nexport function writeOut(\n s: string,\n stream: NodeJS.WriteStream = process.stdout,\n): boolean {\n return writeTo(s, stream);\n}\n\n/**\n * Symmetric partner of `writeOut` for the standard error stream. Same shape,\n * same defensive contract, same single-seam-for-tests story \u2014 just defaults to\n * `process.stderr` instead of `process.stdout`.\n *\n * Use this in code paths that emit error/diagnostic/warning text. Keeping\n * these two helpers split (rather than a single `writeTo(s, stream)`) means\n * the call site reads as a clear intent signal: \"I am writing an error\" vs.\n * \"I am writing a result\" \u2014 which matters for callers that decide between\n * stdout/stderr routing (e.g. `--quiet` flags, log-level filtering,\n * structured-log rewriters that fork on stream).\n *\n * Stderr writes from the core logger (see `infrastructure/logger.ts`) and from\n * the TUI guard (see `tui/run-tui.ts`) used to call `process.stderr.write`\n * directly. Routing them through this helper lets tests stub the stream at\n * one boundary and lets future logging middleware (e.g. a JSON-line rewriter)\n * swap the destination for the entire process in one place.\n */\nexport function writeErr(\n s: string,\n stream: NodeJS.WriteStream = process.stderr,\n): boolean {\n return writeTo(s, stream);\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// TerminalCapability \u2014 startup capability profile\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Color depth the terminal can render.\n *\n * 0 = no color (dumb, redirected, `TERM=dumb`)\n * 1 = 16 colors (basic ANSI)\n * 2 = 256 colors (ANSI 256 + bright variants)\n * 3 = 16.7M / 24-bit truecolor\n */\nexport type ColorDepth = 0 | 1 | 2 | 3;\n\n/**\n * Mouse tracking protocol the terminal speaks.\n *\n * 'none' \u2014 no mouse reporting\n * 'x10' \u2014 basic button press/release, capped at 223 columns\n * 'urxvt'\u2014 URXVT extension, supports >223 cols\n * 'sgr' \u2014 SGR extended mode, modern standard, no column cap\n */\nexport type MouseProtocol = 'none' | 'x10' | 'urxvt' | 'sgr';\n\n/**\n * A snapshot of the terminal's capabilities and identity, computed once at\n * startup. Call {@link detectTerminal} once and pass the result through your\n * app \u2014 never re-detect mid-session.\n *\n * Query once \u2192 adapt once. Re-querying on every render is wrong because\n * `$TERM` is static for the process lifetime and stdout.isTTY can only go\n * from true\u2192false (never the reverse), so a mid-session change means the\n * process was started in a terminal and then something went wrong.\n */\nexport interface TerminalCapability {\n /** True when both stdin and stdout are attached to a terminal. */\n isRealTTY: boolean;\n /**\n * Whether the terminal speaks color.\n *\n * Uses the industry-standard precedence:\n * `FORCE_COLOR=0` \u2192 0 (disabled)\n * `FORCE_COLOR` \u2192 3 (force truecolor)\n * `NO_COLOR=\u2026` \u2192 0 (opted out)\n * `COLORTERM=truecolor|24bit` \u2192 3\n * `TERM=\u2026truecolor|24bit` \u2192 3 (some emulators advertise it)\n * `TERM=\u2026256color` \u2192 2\n * fallback \u2192 1\n *\n * Note: `TERM=dumb` always produces 0 even if `COLORTERM=truecolor` is set \u2014\n * `dumb` terminals are non-interactive by definition.\n */\n colorDepth: ColorDepth;\n /**\n * Whether `stdout` can be written to. Determined once at startup from\n * `stdout?.isTTY ?? false`. Even when `isRealTTY` is true, `stdout` may be\n * writable=false (e.g. the terminal was closed mid-session).\n */\n stdoutWritable: boolean;\n /**\n * Best mouse protocol the terminal speaks. Progressive enhancement:\n * attempt SGR first, fall back to URXVT, then X10, then 'none'.\n *\n * The caller (typically the App component) decides whether to enable\n * tracking at all \u2014 this only reports what the terminal CAN understand.\n */\n mouseProtocol: MouseProtocol;\n /**\n * Whether the terminal title can be set via OSC 0 / OSC 2.\n * True when stdout is a TTY and `$TERM` is not `dumb`.\n */\n canSetTitle: boolean;\n /**\n * Whether the terminal understands the tmux DCS passthrough prefix.\n * Detected by seeing `$TERM=tmux*`. When true, escape sequences should be\n * wrapped: `\\x1bPtmux;\\x1b${seq}\\x1b\\\\`.\n */\n isTmux: boolean;\n /**\n * Whether the terminal is on Windows using the legacy conhost backend\n * (cmd.exe, PowerShell non-ConPTY) rather than ConPTY (Windows Terminal,\n * VS Code Integrated Terminal). Used to apply Windows-specific raw-mode\n * handoff logic.\n *\n * Detected by: platform === 'win32' AND `!process.stdout.getColorDepth?.()`\n * (ConPTY exposes color depth; conhost does not). Also false on non-Windows.\n */\n isWindowsConhost: boolean;\n}\n\n/**\n * Parse `process.env.TERM` into a color-depth guess.\n *\n * We intentionally do NOT use the `supports-color` npm package here because\n * (a) it adds a dependency, (b) it resolves at module-import time, and (c) we\n * need to factor in `FORCE_COLOR` / `NO_COLOR` which may be set after import.\n * This pure function is trivial to test and predictable regardless of import\n * order.\n */\nfunction parseColorDepth(env: { FORCE_COLOR?: string; NO_COLOR?: string; COLORTERM?: string; TERM?: string; }): ColorDepth {\n // `FORCE_COLOR=0` is an explicit opt-out, even when `COLORTERM=truecolor`.\n if (env.FORCE_COLOR === '0') return 0;\n // `FORCE_COLOR` with no value or any truthy value forces truecolor.\n if (env.FORCE_COLOR !== undefined) return 3;\n // Explicit user opt-out.\n if (typeof env.NO_COLOR === 'string' && env.NO_COLOR !== '') return 0;\n // Explicit terminal advertisement.\n const colorterm = (env.COLORTERM ?? '').toLowerCase();\n if (colorterm === 'truecolor' || colorterm === '24bit') return 3;\n // TERM strings that advertise rich color.\n const term = (env.TERM ?? '').toLowerCase();\n if (term.includes('truecolor') || term.includes('24bit')) return 3;\n if (term.includes('256color')) return 2;\n // TERM=dumb = no interactive capability at all.\n if (term === 'dumb') return 0;\n // Default to 16-color \u2014 the safest floor. Modern terminals all override this.\n return 1;\n}\n\n/**\n * Detect the best mouse protocol the terminal speaks.\n *\n * Uses the `$TERM` string as a proxy since Node.js has no runtime query for\n * mouse capability (DECSET 1000/1002/1003 responses are not standardized for\n * programmatic use). The approximation is:\n * - `tmux` + `screen` = SGR (they proxy it)\n * - `xterm*`, `rxvt*`, `konsole`, `gnome*` = SGR (post-2013)\n * - `linux`, `vt100`, `dumb` = none\n * - Everything else = URXVT / X10 (try SGR, fall back gracefully in mouse.ts)\n *\n * This is advisory only. The App component gates mouse tracking behind an\n * explicit user/setting opt-in; false positives just mean the tracking enable\n * sequence is ignored harmlessly.\n */\nfunction parseMouseProtocol(term: string): MouseProtocol {\n const t = term.toLowerCase();\n // Terminals that reliably support SGR (1006) mode.\n if (\n t.startsWith('xterm') ||\n t.startsWith('tmux') ||\n t.startsWith('screen') ||\n t.includes('rxvt-unicode') ||\n t.includes('urxvt') ||\n t.includes('konsole') ||\n t.includes('gnome') ||\n t.includes('foot') ||\n t.includes('alacritty') ||\n t.includes('wezterm') ||\n t.includes('kitty') ||\n t.includes('vscode') ||\n t.includes('Apple_Terminal')\n ) {\n return 'sgr';\n }\n // Known mouse-capable but older terminals.\n if (t.startsWith('rxvt') || t.startsWith('linux') || t.includes('Eterm')) {\n return 'urxvt';\n }\n // vt100 / dumb \u2014 no mouse.\n if (t === 'vt100' || t === 'dumb') return 'none';\n // Default: assume SGR is safe to try; mouse.ts falls back silently.\n return 'sgr';\n}\n\n/**\n * Detect the terminal's capabilities and identity at startup.\n *\n * Call once, early in process boot, before any event-loop async has had a\n * chance to corrupt the read of `process.stdout.isTTY`. Store the result and\n * pass it through the app \u2014 do NOT call this on every render.\n *\n * All `env` lookups default gracefully to safe values when the env var is\n * absent or empty, so the return value is deterministic regardless of what\n * the host's environment looks like.\n */\nexport function detectTerminal(\n opts: {\n stdin?: NodeJS.ReadStream | null;\n stdout?: NodeJS.WriteStream | null;\n env?: typeof process.env;\n } = {},\n): TerminalCapability {\n const stdin = opts.stdin ?? process.stdin;\n const stdout = opts.stdout ?? process.stdout;\n const env = opts.env ?? process.env;\n\n const isRealTTY = (stdin?.isTTY ?? false) && (stdout?.isTTY ?? false);\n const stdoutWritable = isRealTTY && typeof stdout?.write === 'function';\n const term = env.TERM ?? '';\n const isTmux = term.toLowerCase().startsWith('tmux');\n const isWindowsConhost = isRealTTY\n && process.platform === 'win32'\n && typeof (stdout as NodeJS.WriteStream & { getColorDepth?: unknown }).getColorDepth !== 'function';\n\n return {\n isRealTTY,\n colorDepth: isRealTTY ? parseColorDepth(env) : 0,\n stdoutWritable,\n mouseProtocol: parseMouseProtocol(term),\n canSetTitle: isRealTTY && term !== 'dumb',\n isTmux,\n isWindowsConhost,\n };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// TerminalLifecycle \u2014 raw-mode lifecycle manager\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Lifecycle owner for the TTY stdin raw-mode state machine.\n *\n * Raw mode is the most dangerous primitive in TUI code. Getting it wrong leaves\n * the user's terminal in a broken state (line-buffered, no echo) that requires\n * closing and reopening the terminal to recover. This class enforces a strict\n * acquire \u2192 hold \u2192 release lifecycle:\n *\n * - `acquire()` may be called only once (idempotent \u2014 subsequent calls no-op).\n * - `release()` restores the previous mode exactly once.\n * - `release()` is called automatically on process `exit` and `beforeExit`.\n * - Any signal handler (SIGINT, SIGTERM, SIGHUP) that calls `release()` clears\n * its own registration so double-signals don't double-restore.\n *\n * Use this instead of calling `setRawMode()` directly at every call site. The\n * single instance lives at module scope in `run-tui.ts`; components that need\n * to signal \"TUI is shutting down\" call `terminalLifecycle.requestExit()` rather\n * than calling `release()` directly.\n */\nexport class TerminalLifecycle {\n private _active = false;\n\n /**\n * The stdin stream we took raw mode on. Set by `acquire()`; undefined before\n * first call. Used by `release()` and by the ConPTY race-closer in\n * `acquire()`.\n */\n private _stdin: NodeJS.ReadStream | undefined;\n\n /**\n * The `isRaw` snapshot captured before the first `acquire()` call, so\n * `release()` restores to the pre-TUI state rather than blindly disabling\n * raw mode. Null when `acquire()` has never been called.\n */\n private _wasRaw: boolean | null = null;\n\n /**\n * Whether stdin was paused before the TUI took ownership. Readline closes\n * its interface by pausing the shared process stream, so raw mode alone is\n * not enough to make Ink receive bytes during the boot-prompt -> TUI handoff.\n */\n private _wasPaused: boolean | null = null;\n\n /**\n * Request the process to exit. Set the flag, trigger any registered\n * `onRequestExit` callback (typically Ink's `unmount()`), and arm a\n * deadline timer. When the timer fires the process is hard-exited.\n *\n * Safe to call multiple times: subsequent calls no-op after the first.\n */\n requestExit: (exitCode?: number) => void;\n\n /**\n * Callback invoked when `requestExit` is called. Registered by the Ink\n * mount point so unmount() runs before the deadline timer fires.\n */\n onRequestExit: (() => void | Promise<void>) | null = null;\n\n constructor() {\n // Build the requestExit closure here so subclasses / tests can override\n // `requestExit` before calling `acquire()`.\n let exitCode = 0;\n let requested = false;\n this.requestExit = (code = 0) => {\n exitCode = code;\n if (requested) return;\n requested = true;\n // Let the Ink unmount handler run (if registered).\n try {\n this.onRequestExit?.();\n } catch {\n // unmount handler threw \u2014 proceed to hard exit.\n }\n // Hard-exit deadline. `unref()` so the timer doesn't keep the event\n // loop alive if everything else has settled.\n setTimeout(() => process.exit(exitCode), 5_000).unref();\n };\n }\n\n /** True while raw mode is held. */\n get active(): boolean {\n return this._active;\n }\n\n /**\n * Acquire raw mode on `stdin`. Idempotent \u2014 calling twice is safe; only the\n * first call has any effect.\n *\n * On Windows ConPTY, calls `setRawMode` twice: once immediately, then again\n * after the next event-loop tick. This closes the ConPTY race where\n * `readline`'s cleanup (registered before this class is constructed) can\n * restore the original cooked mode after we set raw mode but before Ink's\n * render loop takes over the stdin fd. The double-call ensures the last\n * writer wins.\n *\n * @param stdin - The stdin stream (defaults to `process.stdin`).\n * @returns `true` if raw mode was acquired; `false` if stdin is not a TTY\n * or already shut down.\n */\n acquire(stdin: NodeJS.ReadStream = process.stdin): boolean {\n if (this._active) return false;\n\n // Guard: must be a real TTY, must have setRawMode.\n if (stdin?.isTTY !== true) return false;\n if (typeof stdin.setRawMode !== 'function') return false;\n\n // Snapshot the pre-TUI raw state so release() restores correctly.\n this._wasRaw = stdin.isRaw ?? false;\n this._wasPaused = stdin.isPaused();\n this._stdin = stdin;\n\n stdin.setRawMode(true);\n // A preceding readline prompt normally leaves process.stdin paused.\n // Explicitly start the stream before Ink installs its input listener;\n // otherwise the UI can render perfectly while every key appears dead.\n stdin.resume();\n this._active = true;\n\n // Windows ConPTY double-acquire: schedule a second setRawMode on the next\n // tick so we win the race against readline's \"restore original mode\".\n // Safe on non-Windows (no-op: process.platform !== 'win32').\n if (process.platform === 'win32') {\n setImmediate(() => {\n if (this._active && this._stdin?.isTTY) {\n this._stdin.setRawMode?.(true);\n }\n });\n }\n\n return true;\n }\n\n /**\n * Release raw mode and restore the terminal to the state it was in before\n * `acquire()` was called. Idempotent \u2014 subsequent calls are no-ops.\n *\n * Call this on: SIGINT, SIGTERM, SIGHUP, SIGBREAK, and `process.exit`.\n *\n * \u26A0 **Windows caveat**: `setRawMode(false)` on ConPTY restores the original\n * console mode captured when the process started \u2014 not the mode captured by\n * `acquire()`. On ConPTY, the original mode was already raw (ConPTY starts\n * raw), so this call is typically a no-op on Windows Terminal. On conhost.exe\n * it may restore cooked mode, which is correct for that backend.\n */\n release(): void {\n if (!this._active) return;\n this._active = false;\n\n const stdin = this._stdin;\n if (stdin?.isTTY === true) {\n // Restore to pre-TUI state rather than blindly disabling raw mode.\n stdin.setRawMode?.(this._wasRaw ?? false);\n if (this._wasPaused) stdin.pause();\n }\n this._stdin = undefined;\n this._wasRaw = null;\n this._wasPaused = null;\n }\n\n /**\n * Synchronously reset all terminal state written by the TUI layer:\n * raw mode, SGR attributes, cursor visibility, and mouse tracking.\n *\n * Intended for the final `process.on('exit')` handler where async is\n * impossible. Uses `\\x1b[0m` (SGR reset), `\\x1b[?25h` (cursor show),\n * and `\\x1b[?9l` (mouse off) \u2014 all safe to emit even if the terminal does\n * not understand them (unknown CSI sequences are silently ignored).\n *\n * Safe to call multiple times from multiple exit paths because each write\n * is a constant-time synchronous stream write.\n *\n * @param stdout - Stream to reset (defaults to `process.stdout`).\n */\n reset(stdout: NodeJS.WriteStream = process.stdout): void {\n if (typeof stdout?.write !== 'function') return;\n // SGR reset \u2014 clears bold, color, underline, etc.\n stdout.write('\\x1b[0m');\n // Cursor show (in case Ink's unmount left it hidden).\n stdout.write('\\x1b[?25h');\n // Mouse tracking off (all three DECRST sequences \u2014 safe no-ops if never set).\n stdout.write('\\x1b[?1003l\\x1b[?1002l\\x1b[?1000l');\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// safeEscape \u2014 guarded escape-sequence emitter\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Result of emitting an escape sequence. Distinguishes the three distinct\n * failure modes so callers can degrade gracefully.\n */\nexport interface EscapeEmitResult {\n /** `true` when the sequence was written to the stream. */\n ok: boolean;\n /**\n * Human-readable reason when `ok` is `false`. One of:\n * `'not_a_tty'` \u2014 stdout is not a terminal (pipe / redirect / CI)\n * `'unwritable'` \u2014 stdout.write is missing or threw\n * `'empty'` \u2014 `sequence` was the empty string\n */\n reason: 'not_a_tty' | 'unwritable' | 'empty';\n}\n\n/**\n * A terminal escape sequence with its optional string terminator.\n *\n * OSC (title, color palette) sequences MUST be terminated with `BEL (\\x07)`\n * or `ST (\\x1b\\\\)` or the terminal stays in command mode and corrupts\n * subsequent output. CSI sequences (colors, cursor movement) are\n * self-terminating and do not need a terminator byte.\n */\nexport interface EscapeSequence {\n /** The raw sequence, e.g. `\\x1b[38;2;255;0;0m`. */\n raw: string;\n /**\n * Optional terminator byte required by OSC/DCS families.\n * `undefined` for CSI sequences; `\\x07` (BEL) for OSC; `\\x1b\\\\` (ST) for DCS.\n */\n terminator?: string;\n}\n\n/**\n * Shared OSC/DCS terminator bytes.\n *\n * BEL (`\\x07`) is understood by iTerm2, Konsole, mintty, foot, and\n * Windows Terminal. ST (`\\x1b\\\\`) is the strict DEC standard equivalent.\n * Prefer BEL for compatibility; fall back to ST if the terminal's response\n * to BEL is observed to be garbled.\n */\nexport const ESCAPE_TERMINATOR = Object.freeze({\n BEL: '\\x07', // OSC 0 / OSC 2 (title)\n ST: '\\x1b\\\\', // DCS / strict OSC\n});\n\n/**\n * Emit a terminal escape sequence safely.\n *\n * Rules enforced:\n * 1. Empty string \u2192 returns `ok: false, reason: 'empty'`, no write.\n * 2. Not a TTY \u2192 returns `ok: false, reason: 'not_a_tty'`, no write.\n * 3. Stream write throws \u2192 returns `ok: false, reason: 'unwritable'`.\n * 4. OSC/DCS sequences MUST have a terminator byte (guarded assertion).\n * A missing terminator on an OSC sequence corrupts output on terminals\n * that don't understand the sequence \u2014 this function refuses to emit it.\n *\n * @param sequence - The sequence to emit. CSI sequences need no terminator.\n * OSC/DCS sequences must carry `terminator: '\\x07'` or `'\\x1b\\\\'`.\n * @param stdout - Stream to write to (defaults to `process.stdout`).\n * @returns Result indicating success or the specific failure mode.\n */\nexport function safeEmit(\n sequence: EscapeSequence | string,\n stdout: NodeJS.WriteStream = process.stdout,\n): EscapeEmitResult {\n const raw = typeof sequence === 'string' ? sequence : sequence.raw;\n const term = typeof sequence === 'string' ? undefined : sequence.terminator;\n\n if (!raw) {\n return { ok: false, reason: 'empty' };\n }\n if (stdout?.isTTY !== true) {\n return { ok: false, reason: 'not_a_tty' };\n }\n\n // For OSC/DCS sequences (which include `[` or `]` in the body), verify a\n // terminator is present. CSI sequences start with `\\x1b[` and are\n // self-terminating; we treat any sequence that is not a known OSC/DCS\n // as CSI and allow through. OSC starts with `\\x1b]`; DCS with `\\x1bP`.\n if (raw.startsWith('\\x1b]') || raw.startsWith('\\x1bP')) {\n // Assertion: terminator must be provided. Belt-and-suspenders against\n // callers that forget to append `\\x07` \u2014 this prevents corrupted output.\n if (!term) {\n // Defensive: append BEL rather than crashing. This is the only case\n // where we mutate the sequence. The assert-then-fallback pattern is\n // intentional: we catch programmer error in development but degrade\n // safely in production rather than throwing.\n void term; // suppress unused-variable warning\n const safe = `${raw}\\x07`;\n try {\n stdout.write(safe);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' }; // reason field is ignored when ok=true\n }\n try {\n stdout.write(`${raw}${term}`);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' };\n }\n\n // CSI sequence \u2014 self-terminating.\n try {\n stdout.write(raw);\n } catch {\n return { ok: false, reason: 'unwritable' };\n }\n return { ok: true, reason: 'empty' };\n}\n\n/**\n * Build an OSC 0 / OSC 2 (window/tab title) sequence with a safe terminator.\n *\n * The terminator defaults to `BEL (\\x07)` for broad terminal compatibility.\n * Use `terminator: ESCAPE_TERMINATOR.ST` for stricter terminals that interpret\n * BEL as a beep.\n *\n * @param title - The title string. Embedded BEL bytes are stripped.\n * @param terminator - Defaults to `ESCAPE_TERMINATOR.BEL`.\n */\nexport function buildTitleSequence(\n title: string,\n terminator: string = ESCAPE_TERMINATOR.BEL,\n): EscapeSequence {\n return {\n raw: `\\x1b]0;${title.replace(/\\x07/g, '')}`,\n terminator,\n };\n}\n\n/**\n * Build an SGR (Select Graphic Rendition) color/style sequence.\n *\n * @param codes - SGR parameter list, e.g. `[31]` (red foreground),\n * `[1;32]` (bold green), `[38;2;255;128;0]` (truecolor orange).\n */\nexport function buildSgrSequence(...codes: number[]): EscapeSequence {\n return { raw: `\\x1b[${codes.join(';')}m` };\n}\n\n/**\n * Emit a title sequence. Convenience wrapper around `safeEmit` + `buildTitleSequence`.\n *\n * @param title - The title string.\n * @param stdout - Target stream.\n */\nexport function setTitle(\n title: string,\n stdout: NodeJS.WriteStream = process.stdout,\n): boolean {\n return safeEmit(buildTitleSequence(title), stdout).ok;\n}\n", "import { isStdoutTTY } from './term.js';\n\nconst isColorTty = (): boolean => {\n if (envFlag(process.env.NO_COLOR)) return false;\n if (envFlag(process.env.FORCE_COLOR)) return true;\n return isStdoutTTY();\n};\n\nfunction envFlag(value: string | undefined): boolean {\n if (value === undefined) return false;\n if (value.trim() === '') return false;\n return !/^(0|false|no|off)$/i.test(value.trim());\n}\n\nconst COLOR = isColorTty();\n\nconst wrap =\n (open: string, close: string) =>\n (s: string): string =>\n COLOR ? `\\x1b[${open}m${s}\\x1b[${close}m` : s;\n\nexport const color = {\n reset: wrap('0', '0'),\n bold: wrap('1', '22'),\n dim: wrap('2', '22'),\n italic: wrap('3', '23'),\n underline: wrap('4', '24'),\n red: wrap('31', '39'),\n green: wrap('32', '39'),\n yellow: wrap('33', '39'),\n blue: wrap('34', '39'),\n magenta: wrap('35', '39'),\n cyan: wrap('36', '39'),\n gray: wrap('90', '39'),\n amber: wrap('38;5;214', '39'),\n pink: wrap('38;5;205', '39'),\n bgRed: wrap('41', '49'),\n bgGreen: wrap('42', '49'),\n};\n\nexport function stripAnsi(s: string): string {\n return s.replace(/\\x1b\\[[0-9;]*[A-Za-z]/g, '');\n}\n", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * Minimal paths object needed for config backup \u2014 just the global root\n * from which we derive the backup history directory.\n */\nexport interface ConfigBackupPaths {\n globalRoot: string;\n}\n\n/**\n * Backup directory for config file history.\n * Every config write creates a timestamped snapshot here so the user can\n * recover from accidental changes. Backups are never cleaned up automatically.\n */\nexport function configHistoryDir(globalRoot: string): string {\n return path.join(globalRoot, 'config-history');\n}\n\n/**\n * Derive a human-readable slug for a config file path relative to the\n * ~/.wrongstack root. Examples:\n * config.json \u2192 config\n * profiles/default/config.json \u2192 profiles-default-config\n */\nfunction configSlug(absolutePath: string, globalRoot: string): string {\n const rel = path.relative(globalRoot, absolutePath);\n const normalized = rel.replace(/\\\\/g, '/').replace(/\\.json$/i, '');\n return normalized.replace(/\\//g, '-');\n}\n\n/**\n * Before overwriting a config file, save its current content to the\n * config-history directory with a timestamp. Best-effort: failures are\n * silently ignored so they never block the config write itself.\n */\nexport async function backupConfigFile(\n filePath: string,\n paths: ConfigBackupPaths,\n): Promise<void> {\n let currentContent: string;\n try {\n currentContent = await fs.readFile(filePath, 'utf8');\n if (!currentContent.trim()) return;\n } catch {\n return; // ENOENT or other error \u2014 no current content to back up\n }\n\n const now = new Date();\n const ts = now.toISOString()\n .replace(/[:.]/g, '-')\n .replace(/Z$/, '');\n const slug = configSlug(filePath, paths.globalRoot);\n const backupDir = configHistoryDir(paths.globalRoot);\n const backupFile = path.join(backupDir, `${slug}-${ts}.json`);\n\n try {\n await fs.mkdir(backupDir, { recursive: true });\n await fs.writeFile(backupFile, currentContent, { mode: 0o600, encoding: 'utf8' });\n } catch {\n // best-effort \u2014 never block the config write for a backup failure\n }\n}\n", "/** Assert a value is neither null nor undefined. Throws if it is.\n * Useful after optional chaining and indexed access when the\n * control flow guarantees the value exists but TypeScript can't\n * prove it (e.g. after a check on a related field). */\nexport function expectDefined<T>(value: T | null | undefined, label?: string): T {\n if (value === null || value === undefined) {\n const err = new Error(label ? `Expected ${label} to be defined` : 'Expected value to be defined');\n err.name = 'ExpectDefinedError';\n throw err;\n }\n return value;\n}\n", "import type { ContentBlock, ToolResultBlock, ToolUseBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\nimport { expectDefined } from './expect-defined.js';\nexport interface MessageRepairReport {\n changed: boolean;\n removedToolUses: string[];\n removedToolResults: string[];\n removedMessages: number;\n}\n\nexport interface MessageRepairResult {\n messages: Message[];\n report: MessageRepairReport;\n}\n\n/**\n * Repair provider-level tool-call adjacency invariants.\n *\n * Anthropic requires every assistant `tool_use` block to have a matching\n * `tool_result` block in the immediately following user message. Manual\n * context surgery (summary/prune) can cut through the middle of such an\n * exchange. This function removes only the now-orphaned protocol blocks,\n * preserving surrounding text/images/thinking blocks where possible.\n */\nexport function repairToolUseAdjacency(messages: Message[]): MessageRepairResult {\n const removedToolUses: string[] = [];\n const removedToolResults: string[] = [];\n let removedMessages = 0;\n let changed = false;\n const out: Message[] = [];\n\n for (let i = 0; i < messages.length; i++) {\n const original = expectDefined(messages[i]);\n let msg = original;\n\n if (hasToolUse(msg)) {\n const nextIds = toolResultIds(messages[i + 1]);\n const filtered = mapContent(msg, (blocks) => {\n const next: ContentBlock[] = [];\n for (const block of blocks) {\n if (block.type === 'tool_use' && !nextIds.has(block.id)) {\n removedToolUses.push(block.id);\n changed = true;\n continue;\n }\n next.push(block);\n }\n return next;\n });\n msg = filtered ?? msg;\n }\n\n if (hasToolResult(msg)) {\n const allowed = toolUseIds(out[out.length - 1]);\n const filtered = mapContent(msg, (blocks) => {\n const next: ContentBlock[] = [];\n for (const block of blocks) {\n if (block.type === 'tool_result' && !allowed.has(block.tool_use_id)) {\n removedToolResults.push(block.tool_use_id);\n changed = true;\n continue;\n }\n next.push(block);\n }\n return next;\n });\n msg = filtered ?? msg;\n }\n\n if (isEmptyMessage(msg)) {\n removedMessages++;\n changed = true;\n continue;\n }\n out.push(msg);\n }\n\n return {\n messages: changed ? out : messages,\n report: { changed, removedToolUses, removedToolResults, removedMessages },\n };\n}\n\nfunction hasToolUse(msg: Message | undefined): boolean {\n return contentBlocks(msg).some((b): b is ToolUseBlock => b.type === 'tool_use');\n}\n\nfunction hasToolResult(msg: Message | undefined): boolean {\n return contentBlocks(msg).some((b): b is ToolResultBlock => b.type === 'tool_result');\n}\n\nfunction toolUseIds(msg: Message | undefined): Set<string> {\n const ids = new Set<string>();\n if (msg?.role !== 'assistant') return ids;\n for (const block of contentBlocks(msg)) {\n if (block.type === 'tool_use') ids.add(block.id);\n }\n return ids;\n}\n\nfunction toolResultIds(msg: Message | undefined): Set<string> {\n const ids = new Set<string>();\n if (msg?.role !== 'user') return ids;\n for (const block of contentBlocks(msg)) {\n if (block.type === 'tool_result') ids.add(block.tool_use_id);\n }\n return ids;\n}\n\nfunction contentBlocks(msg: Message | undefined): ContentBlock[] {\n return msg && Array.isArray(msg.content) ? msg.content : [];\n}\n\nfunction mapContent(msg: Message, fn: (blocks: ContentBlock[]) => ContentBlock[]): Message | null {\n if (!Array.isArray(msg.content)) return msg;\n const next = fn(msg.content);\n if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {\n return msg;\n }\n return { ...msg, content: next };\n}\n\n/**\n * True when a message content payload carries meaningful information for\n * the provider: non-whitespace text, a tool call/result, thinking with\n * text or a signature, or any other block type.\n *\n * False for empty strings/arrays and for arrays whose only blocks are\n * empty or whitespace-only text \u2014 the shape persisted when a stream is\n * interrupted before the first meaningful delta (issue #271). Strict\n * providers reject such assistant turns, so repair and replay paths must\n * treat them as empty.\n */\nexport function hasMeaningfulContent(content: Message['content']): boolean {\n if (typeof content === 'string') return content.trim().length > 0;\n for (const block of content) {\n if (block.type === 'text') {\n if (block.text.trim().length > 0) return true;\n continue;\n }\n if (block.type === 'thinking') {\n // Signature-only thinking blocks are valid and required for replay;\n // blocks with neither text nor signature are provider-rejected noise.\n if (block.thinking.trim().length > 0 || block.signature) return true;\n continue;\n }\n // tool_use, tool_result, image, redacted_thinking, \u2026 \u2014 always meaningful.\n return true;\n }\n return false;\n}\n\nfunction isEmptyMessage(msg: Message): boolean {\n return !hasMeaningfulContent(msg.content);\n}\n", "import type { CacheTtl, ReasoningEffort } from '../provider.js';\n\nexport interface ModelRuntimeReasoningConfig {\n /**\n * Whether to send explicit reasoning enable/disable.\n * - 'auto' \u2192 do not send explicit fields; provider/model default wins\n * - 'on' \u2192 send `reasoning.enabled = true`\n * - 'off' \u2192 send `reasoning.enabled = false` only when the model supports disable\n */\n mode?: 'auto' | 'on' | 'off' | undefined;\n /** Reasoning effort. Only sent when the model advertises `effortSupported`. */\n effort?: ReasoningEffort | undefined;\n /** Preserve thinking across turns. Only sent when `preserveThinking !== 'unsupported'`. */\n preserve?: boolean | undefined;\n}\n\n/**\n * Runtime prompt-cache controls mapped into `Request.cache`. Currently only the\n * Anthropic TTL toggle (5m vs 1h) is exposed; other providers ignore it.\n */\nexport interface ModelRuntimeCacheConfig {\n ttl?: CacheTtl | undefined;\n /**\n * Opt-in explicit Gemini context caching. When true, the Google provider\n * creates a server-side `cachedContents` resource for the stable system\n * prefix (system instruction + tool defs) and references it by name instead\n * of resending it every turn. Default false: Gemini's automatic *implicit*\n * caching already covers a byte-stable prefix with no setup. Ignored by every\n * non-Google provider. Any failure in the create flow falls back to the\n * normal inline request, so enabling it can never break a request.\n */\n geminiExplicit?: boolean | undefined;\n}\n\n/**\n * Shared runtime controls applied to every provider request, regardless of host\n * (REPL / TUI / WebUI). The CLI installs a single request-pipeline middleware\n * that reads these and mutates the outgoing `Request`.\n */\nexport interface ModelRuntimeConfig {\n reasoning?: ModelRuntimeReasoningConfig | undefined;\n cache?: ModelRuntimeCacheConfig | undefined;\n /**\n * Generic generation parameters mapped directly onto `Request` fields.\n * Only sent when the active model's `Capabilities` advertise support.\n */\n parameters?: ModelRuntimeParametersConfig | undefined;\n}\n\n/**\n * Selects which built-in/base system identity prompt file is used when building\n * the host system prompt. Surfaces keep `default` unless explicitly told to use\n * the lite or pro prompt.\n */\nexport interface SystemPromptConfig {\n variant?: 'default' | 'lite' | 'pro' | undefined;\n}\n\n/**\n * Generic generation parameters the user can set per-session / per-project.\n * Each field maps to a `Request` field of the same name and is gated by the\n * corresponding `Capabilities` flag so unsupported models don't receive\n * parameters they'd reject.\n */\nexport interface ModelRuntimeParametersConfig {\n /** Top-K sampling (Anthropic, Gemini). Gated by `capabilities.topK`. */\n topK?: number | undefined;\n /** Frequency penalty (OpenAI, Gemini). Gated by `capabilities.frequencyPenalty`. */\n frequencyPenalty?: number | undefined;\n /** Presence penalty (OpenAI, Gemini). Gated by `capabilities.presencePenalty`. */\n presencePenalty?: number | undefined;\n /** Random seed (OpenAI, Gemini). Gated by `capabilities.seed`. */\n seed?: number | undefined;\n /** End-user identifier for abuse monitoring. */\n user?: string | undefined;\n /** Log probabilities (OpenAI, Gemini). Gated by `capabilities.logprobs`. */\n logprobs?: boolean | undefined;\n /** Number of top logprobs to return (OpenAI). Only when `logprobs` is true. */\n topLogprobs?: number | undefined;\n}\n\n/**\n * HQ client connection settings. Same-machine clients can auto-discover the\n * local HQ auth file; remote clients use this config-backed URL/token pair.\n */\nexport interface HqClientConfig {\n /** Enable HQ publishing. Env WRONGSTACK_HQ_ENABLED still overrides at runtime. */\n enabled?: boolean | undefined;\n /** HQ HTTP base URL, e.g. http://host:3499. */\n url?: string | undefined;\n /** Client token for /ws/client. Stored encrypted by SecretVault when persisted. */\n token?: string | undefined;\n /** Optional HQ data dir for same-machine auth.json discovery. */\n dataDir?: string | undefined;\n /** Send raw content previews to HQ instead of redacted previews. */\n rawContent?: boolean | undefined;\n /** Override project display name in HQ. */\n projectAlias?: string | undefined;\n}\n\n/**\n * Token-saving mode tier levels. Controls how aggressively the system prompt\n * is compacted to reduce per-request token consumption.\n *\n * - 'off' \u2014 Full prompt, all tools, complete guidance (no reduction)\n * - 'minimal' \u2014 TIER1 tools (13, including codebase index lifecycle), stripped guidance\n * - 'light' \u2014 Same Tier 1 tool surface, common patterns, minimal guidance\n * - 'medium' \u2014 TIER1 + TIER2 development tools, some guidance (default when `true`)\n * - 'aggressive' \u2014 Maximum savings before tools become unusable (~4-5k tokens saved)\n */\n/**\n * Prompt token-saving tiers. `'auto'` is an INPUT-only sentinel meaning \"pick a\n * concrete tier from the model's context window\" \u2014 it is resolved to one of the\n * concrete tiers by {@link resolveTokenSavingTier} before reaching the prompt\n * builder (which never sees `'auto'`; if it somehow does, it behaves as `'off'`,\n * i.e. the full prompt \u2014 the safe fallback).\n */\nexport type TokenSavingTier = 'off' | 'auto' | 'minimal' | 'light' | 'medium' | 'aggressive';\n\n/** Concrete tiers the prompt builder actually consumes ('auto' excluded). */\nexport type ConcreteTokenSavingTier = Exclude<TokenSavingTier, 'auto'>;\n\n/**\n * Normalize a TokenSavingTier value, handling backward-compatible boolean inputs.\n * - `true` \u2192 'medium' (existing behavior)\n * - `false` \u2192 'off'\n * - `'auto'` \u2192 `'off'` \u2014 the `'auto'` sentinel is window-dependent, so EVERY\n * consumer that isn't the prompt builder (tool selection, lazy-load gate, TUI\n * display) must treat it as the safe no-op `'off'`: it must NOT reduce the\n * registered tool set or enable lazy loading on its own. Only the prompt\n * builder expands `'auto'` \u2014 via {@link resolveTokenSavingTier} \u2014 and only for\n * the (cache-stable) prompt prose. This keeps auto-tiering capability-neutral.\n * - other valid strings are returned as-is; `undefined`/invalid \u2192 'off'\n */\nexport function normalizeTokenSavingTier(val?: TokenSavingTier | boolean): ConcreteTokenSavingTier {\n if (val === undefined) return 'off';\n if (typeof val === 'boolean') return val ? 'medium' : 'off';\n const validTiers = new Set<ConcreteTokenSavingTier>([\n 'off',\n 'minimal',\n 'light',\n 'medium',\n 'aggressive',\n ]);\n // 'auto' is deliberately absent \u2192 collapses to 'off' for non-prompt consumers.\n return validTiers.has(val as ConcreteTokenSavingTier) ? (val as ConcreteTokenSavingTier) : 'off';\n}\n\n/**\n * Resolve the effective (concrete) token-saving tier for the **prompt builder**,\n * expanding the `'auto'` sentinel from the model's context window. This is\n * **cache-safe**: the window is stable for a session, so it resolves to the same\n * tier every turn \u2014 the system-prompt prefix stays byte-stable and the provider\n * prompt cache is never busted by a shifting tier (unlike a per-turn\n * pressure-driven tier). It re-resolves only on `/model` switch, which busts the\n * cache anyway.\n *\n * Modern threshold mapping \u2014 the system prompt (~12K tokens) is roughly the same\n * size regardless of context window; paying full price for it on every turn when\n * there is abundant headroom is wasteful. The tiers apply minimal trimming first,\n * escalating only when the window is genuinely tight:\n * - `< 32k` \u2192 `'medium'` (tiny window: identity + tool prose is a big fraction)\n * - `< 128k` \u2192 `'light'` (tight window: GPT-4 base class models)\n * - `>= 128k` \u2192 `'minimal'` (modern models: GPT-4o, Sonnet, DeepSeek, Qwen 3, Gemini 1M, Qwen 1M)\n * - unknown window \u2192 `'off'` (never guess a lean prompt without evidence)\n *\n * Explicit concrete tiers (a user who set `'medium'`, `'off'`, \u2026) are always\n * respected verbatim \u2014 `'auto'` is the only value that consults the window.\n */\nexport function resolveTokenSavingTier(\n val: TokenSavingTier | boolean | undefined,\n maxContext: number | undefined,\n): ConcreteTokenSavingTier {\n if (val === 'auto') {\n if (typeof maxContext !== 'number' || !Number.isFinite(maxContext) || maxContext <= 0) {\n return 'off';\n }\n if (maxContext < 32_000) return 'medium';\n if (maxContext < 128_000) return 'light';\n return 'minimal';\n }\n return normalizeTokenSavingTier(val);\n}\n\n// FleetChatVerbosity, FLEET_CHAT_VERBOSITY_VALUES, and resolveFleetChatVerbosity\n// were moved to the dependency-free leaf module ./fleet-chat.ts to avoid a\n// type-level import cycle with ./autonomy.ts. They are surfaced through the\n// config barrel (../config.ts) which re-exports ./config/fleet-chat.js directly.\n\nexport const DEFAULT_TUI_THINKING_WORD = 'thinking';\nexport const MAX_TUI_THINKING_WORD_LENGTH = 16;\n\n/**\n * Normalize the configurable statusline word shown while the TUI is working.\n * The value must be a single short word; invalid values fall back to the default.\n */\nexport function normalizeTuiThinkingWord(value: unknown): string {\n if (typeof value !== 'string') return DEFAULT_TUI_THINKING_WORD;\n const word = value.trim();\n if (word.length === 0 || word.length > MAX_TUI_THINKING_WORD_LENGTH) {\n return DEFAULT_TUI_THINKING_WORD;\n }\n if (!/^[\\p{L}\\p{N}_-]+$/u.test(word)) return DEFAULT_TUI_THINKING_WORD;\n return word;\n}\n", "/**\n * Deep merge utility \u2014 safely merges nested objects with configurable\n * conflict resolution, array merging, and prototype-pollution guarding.\n *\n * Used by:\n * - config-loader (config layer merging with primitive-array concatenation)\n * - secret-vault (config patching)\n * - json-path (json_merge tool with prefer-base / prefer-patch semantics)\n *\n * @module utils/deep-merge\n */\n\n// ---------------------------------------------------------------------------\n// Prototype-pollution guard \u2014 shared set of forbidden __proto__ keys\n// ---------------------------------------------------------------------------\n\nexport const FORBIDDEN_PROTO_KEYS = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n '__defineGetter__',\n '__defineSetter__',\n '__lookupGetter__',\n '__lookupSetter__',\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** True when every element is a primitive or null (no nested objects/arrays). */\nexport function isPrimitiveArray(a: unknown[]): boolean {\n return a.every((v) => v === null || (typeof v !== 'object' && typeof v !== 'function'));\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface DeepMergeOptions {\n /**\n * Which side wins on collision for scalars and arrays.\n *\n * - `'prefer-patch'` (default): patch value replaces base value.\n * - `'prefer-base'`: base value is kept, patch value is ignored.\n */\n conflictResolution?: 'prefer-base' | 'prefer-patch';\n\n /**\n * How to handle array values.\n *\n * - `'replace'` (default): patch array replaces base array entirely.\n * - `'concat-primitives'`: when both values are primitive arrays,\n * they are concatenated and deduped (via Set). Non-primitive\n * arrays still replace the base wholesale.\n */\n arrayMode?: 'replace' | 'concat-primitives';\n\n /**\n * Skip prototype-pollution keys (`__proto__`, `constructor`, etc.).\n * Enabled by default. Only disable when you control both inputs\n * and the keyset (e.g. when merging trusted JSON schemas).\n */\n protectProto?: boolean;\n\n /**\n * Optional callback fired when a non-primitive (object) array is\n * replaced wholesale (only relevant with `arrayMode: 'concat-primitives'`).\n * Receives the key name, existing array length, and patch array length.\n * Used by config-loader for debug logging.\n */\n onNonPrimitiveArrayReplace?: (\n key: string,\n existingLen: number,\n patchLen: number,\n ) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively merge `patch` into `base`, returning a new object.\n *\n * - Nested plain objects are merged recursively.\n * - Arrays are handled per `options.arrayMode`.\n * - Scalar collisions are resolved per `options.conflictResolution`.\n * - `null` and non-object values in `patch` replace the base value\n * (unless `conflictResolution` is `'prefer-base'`).\n * - Keys in `base` that are absent from `patch` are preserved.\n * - `FORBIDDEN_PROTO_KEYS` are skipped in the patch (unless\n * `options.protectProto` is set to `false`).\n *\n * The function is generic over `T extends Record<string, unknown>` for\n * callers that pass typed config objects, but the runtime signature\n * also accepts `unknown` inputs (used by the json-path plugin).\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n base: T,\n patch: Record<string, unknown>,\n options?: DeepMergeOptions,\n): T;\n\nexport function deepMerge(\n base: unknown,\n patch: unknown,\n options?: DeepMergeOptions,\n): unknown;\n\nexport function deepMerge(\n base: unknown,\n patch: unknown,\n options: DeepMergeOptions = {},\n): unknown {\n const {\n conflictResolution = 'prefer-patch',\n arrayMode = 'replace',\n protectProto = true,\n onNonPrimitiveArrayReplace,\n } = options;\n\n // Non-object / null handling \u2014 delegate to conflict resolution.\n if (typeof base !== 'object' || base === null) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n if (typeof patch !== 'object' || patch === null) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // Arrays \u2014 handled *before* the object merge so array-of-objects\n // aren't accidentally treated as plain records.\n if (Array.isArray(base) && Array.isArray(patch)) {\n if (\n arrayMode === 'concat-primitives' &&\n isPrimitiveArray(base) &&\n isPrimitiveArray(patch)\n ) {\n return [...new Set([...base, ...patch])];\n }\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // If only one side is an array, treat as scalar collision.\n if (Array.isArray(base) || Array.isArray(patch)) {\n return conflictResolution === 'prefer-patch' ? patch : base;\n }\n\n // Plain object merge.\n const baseObj = base as Record<string, unknown>;\n const patchObj = patch as Record<string, unknown>;\n const out: Record<string, unknown> = { ...baseObj };\n\n for (const [k, v] of Object.entries(patchObj)) {\n if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;\n\n const existing = out[k];\n if (\n v !== null &&\n typeof v === 'object' &&\n !Array.isArray(v) &&\n existing !== null &&\n typeof existing === 'object' &&\n !Array.isArray(existing)\n ) {\n // Recursive merge for nested plain objects.\n out[k] = deepMerge(existing, v, options);\n } else if (Array.isArray(v) && Array.isArray(existing)) {\n // Delegate to top-level array handling so arrayMode\n // (e.g. 'concat-primitives') applies to nested arrays too.\n // Fire debug hook when a non-primitive array replaces an existing\n // array (for non-primitive arrays, concat-primitives is a no-op and\n // the result is always a wholesale replacement).\n if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {\n onNonPrimitiveArrayReplace(k, existing.length, v.length);\n }\n out[k] = deepMerge(existing, v, options);\n } else if (v !== undefined) {\n // Fire debug hook when a non-primitive (object) array replaces an\n // existing value in concat-primitives mode.\n if (\n onNonPrimitiveArrayReplace &&\n Array.isArray(v) &&\n !isPrimitiveArray(v)\n ) {\n const existingLen = Array.isArray(existing) ? existing.length : 0;\n onNonPrimitiveArrayReplace(k, existingLen, v.length);\n }\n out[k] = v;\n }\n // When v === undefined, leave the existing value untouched\n // (this matches config-loader's behaviour: undefined in patch\n // means \"don't change this key\").\n }\n\n return out;\n}\n", "import { readFileSync, statSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Cache of resolved instruction text, keyed by the relative path. Bundled\n * instruction files are immutable for the process lifetime, so re-reading them\n * on every call (some callers do so per LLM request) is wasted disk I/O.\n */\nconst textCache = new Map<string, string>();\n\n/** Resolved once \u2014 the candidate roots only depend on this module's location. */\nlet rootCandidates: string[] | undefined;\n\nexport function readBundledInstructionText(relativePath: string): string {\n const cached = textCache.get(relativePath);\n if (cached !== undefined) return cached;\n\n let resolved = '';\n for (const root of instructionRootCandidates()) {\n try {\n resolved = readFileSync(path.join(root, relativePath), 'utf8').trimEnd();\n break;\n } catch {\n // try next candidate\n }\n }\n textCache.set(relativePath, resolved);\n return resolved;\n}\n\nexport function renderInstructionTemplate(\n template: string,\n values: Record<string, string>,\n): string {\n return template.replace(/\\{\\{\\s*([a-zA-Z0-9_.-]+)\\s*\\}\\}/g, (match, key: string) =>\n Object.hasOwn(values, key) ? (values[key] ?? '') : match,\n );\n}\n\nfunction instructionRootCandidates(): string[] {\n if (rootCandidates !== undefined) return rootCandidates;\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, '../../instructions'),\n path.resolve(here, '../instructions'),\n path.resolve(here, 'instructions'),\n ];\n rootCandidates = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));\n return rootCandidates;\n}\n\nfunction isDirectory(candidate: string): boolean {\n try {\n return statSync(candidate).isDirectory();\n } catch {\n return false;\n }\n}\n", "/**\n * Process-liveness probing.\n *\n * This predicate was copy-pasted across six subsystems (session registry, HQ\n * auth store, director state, fleet notifier, recovery lock, mailbox lock)\n * with four different semantics. The dangerous variant was `catch { return\n * false }`: on POSIX, `process.kill(pid, 0)` throws EPERM when the process\n * EXISTS but belongs to another user (or runs elevated), so a bare catch\n * reports a live process as dead. Callers then \"reclaim\" resources that are\n * still owned \u2014 the director-state lock would be stolen from a running\n * director, and live sessions were pruned from the registry.\n *\n * Everything that asks \"is this pid alive?\" must go through here.\n */\n\n/**\n * True iff `pid` names a live process.\n *\n * POSIX: `process.kill(pid, 0)` succeeds for our own processes, throws EPERM\n * for processes we may not signal (alive, just not ours), and throws ESRCH\n * once the pid is gone.\n * Windows (Node 22+): the same call succeeds when the process exists and\n * throws otherwise.\n *\n * Deliberately fails CLOSED on an unrecognized error code \u2014 reporting \"dead\"\n * for an unknown failure is the conservative answer for the pruning callers,\n * which is why lock owners that must not be displaced (see\n * `single-instance-mailbox`) layer a second, platform-specific check on top.\n */\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false;\n if (pid === process.pid) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n /* v8 ignore next -- platform/permission-specific: EPERM means alive but owned by another user */\n if (code === 'EPERM') return true;\n return false;\n }\n}\n", "import { randomBytes } from 'node:crypto';\n\n/**\n * Crockford base32 alphabet (excludes I, L, O, U to avoid ambiguity).\n */\nconst ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\nconst ENCODING_LEN = ENCODING.length;\nconst TIME_LEN = 10;\nconst RANDOM_LEN = 16;\n\nfunction encodeTime(now: number, len: number): string {\n let mod: number;\n let str = '';\n for (let i = len - 1; i >= 0; i--) {\n mod = now % ENCODING_LEN;\n str = ENCODING[mod] + str;\n now = (now - mod) / ENCODING_LEN;\n }\n return str;\n}\n\nfunction encodeRandom(len: number): string {\n const bytes = randomBytes(len);\n let str = '';\n for (let i = 0; i < len; i++) {\n str += ENCODING[(bytes[i] as number) % ENCODING_LEN];\n }\n return str;\n}\n\n/**\n * Generate a ULID \u2014 a 26-char Crockford-base32 identifier whose first 10 chars\n * encode the millisecond timestamp (so IDs sort lexicographically by creation\n * time) followed by 16 chars of randomness. Zero runtime dependencies.\n *\n * The codebase convention is \"IDs are ULIDs\"; use this for any new store key.\n */\nexport function ulid(seedTime: number = Date.now()): string {\n return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);\n}\n\n/** True for a well-formed 26-char Crockford-base32 ULID. */\nexport function isUlid(value: string): boolean {\n if (value.length !== TIME_LEN + RANDOM_LEN) return false;\n for (const ch of value) {\n if (!ENCODING.includes(ch)) return false;\n }\n return true;\n}\n", "/**\n * Compile a user-supplied regex with conservative bounds against ReDoS.\n *\n * Duplicated from @wrongstack/tools/_regex.ts to avoid a circular\n * dependency (tools depends on core, not vice versa). Keep both copies\n * in sync if the heuristics change.\n *\n * V8's regex engine is backtracking-based and cannot interrupt a\n * synchronous match \u2014 a pattern like `(a+)+$` against a sufficiently\n * long line will pin a worker for seconds.\n */\n\nconst MAX_PATTERN_LEN = 512;\n\n// Heuristics for catastrophic-backtracking constructs.\nconst DANGEROUS_PATTERNS: ReadonlyArray<RegExp> = [\n /(\\([^)]*[+*][^)]*\\))[+*]/, // (a+)+, (.*)+, etc\n /(\\(\\?:[^)]*[+*][^)]*\\))[+*]/, // same, with non-capturing group\n];\n\nexport interface CompileResult {\n ok: true;\n regex: RegExp;\n}\n\nexport interface CompileFail {\n ok: false;\n reason: string;\n}\n\nexport function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail {\n if (typeof pattern !== 'string') {\n return { ok: false, reason: 'pattern must be a string' };\n }\n if (pattern.length === 0) {\n return { ok: false, reason: 'pattern is empty' };\n }\n if (pattern.length > MAX_PATTERN_LEN) {\n return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };\n }\n for (const rx of DANGEROUS_PATTERNS) {\n if (rx.test(pattern)) {\n return {\n ok: false,\n reason:\n 'pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers',\n };\n }\n }\n try {\n return { ok: true, regex: new RegExp(pattern, flags) };\n } catch (err) {\n return {\n ok: false,\n reason: err instanceof Error ? err.message : 'invalid regex',\n };\n }\n}\n", "import { toErrorMessage } from './error.js';\n\nexport interface SafeParseResult<T> {\n ok: boolean;\n value?: T | undefined;\n error?: string | undefined;\n}\n\nexport function safeParse<T = unknown>(input: string, maxBytes = 5_000_000): SafeParseResult<T> {\n if (Buffer.byteLength(input, 'utf8') > maxBytes) {\n return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };\n }\n try {\n return { ok: true, value: JSON.parse(input) as T };\n } catch (err) {\n return {\n ok: false,\n error: toErrorMessage(err),\n };\n }\n}\n\nexport function safeStringify(value: unknown, pretty = false): string {\n const seen = new WeakSet();\n const replacer = (_k: string, v: unknown): unknown => {\n if (typeof v === 'bigint') return v.toString();\n if (v instanceof Error) {\n return { name: v.name, message: v.message, stack: v.stack };\n }\n if (typeof v === 'object' && v !== null) {\n if (seen.has(v as object)) return '[Circular]';\n seen.add(v as object);\n }\n return v;\n };\n try {\n return JSON.stringify(value, replacer, pretty ? 2 : undefined) ?? 'null';\n } catch (err) {\n return JSON.stringify({\n __serialization_error: toErrorMessage(err),\n });\n }\n}\n\n/**\n * Attempt to parse JSON5-style input and return a valid JSON string.\n * Handles trailing commas, line/block comments, and unquoted keys\n * that are common in provider output.\n *\n * Returns the sanitized string if it parses successfully as JSON,\n * or `null` if the input cannot be made valid. Callers use this to\n * decide whether to proceed with the parsed result or fall back to\n * raw handling.\n */\nexport function sanitizeJsonString(s: string): string | null {\n let out = s.trim();\n\n // Stage 1: strip line and block comments outside JSON string values.\n out = stripJsonComments(out);\n\n // Stage 2: strip trailing commas before } or ]\n out = out.replace(/,(\\s*[}\\]])/g, '$1');\n\n // Stage 3: escape literal control characters that appear *inside* string\n // values. Models frequently emit raw newlines/tabs inside a code payload\n // (e.g. edit's old_string/new_string) instead of the required \\n / \\t, which\n // makes JSON.parse throw. This is the single most common malformed-args case.\n out = escapeControlCharsInStrings(out);\n\n // Stage 4: attempt full parse; return null if it fails so callers can\n // distinguish \"already valid JSON\" from \"unrecoverable\".\n try {\n JSON.parse(out);\n return out;\n } catch {\n return null; // stripped but still not valid JSON; caller handles it\n }\n}\n\n/**\n * Strip a Markdown code-fence wrapper from a payload.\n *\n * Models occasionally return tool-call arguments wrapped in ```json fences\n * (or embedded in prose around one) instead of bare JSON. Returns the inner\n * content when the input starts with a fence (closing fence optional, so a\n * truncated stream still unwraps) or contains one complete fenced block;\n * returns null when no fence is present. Callers should only invoke this\n * after a direct parse failed, so fences inside legitimate string values are\n * never touched.\n */\nexport function stripCodeFences(s: string): string | null {\n const trimmed = s.trim();\n // Whole-payload fence: ```lang? \u2026 ```? (closer optional for truncation)\n const opener = /^```[\\w+-]*[ \\t]*\\r?\\n?/.exec(trimmed);\n if (opener) {\n const inner = trimmed.slice(opener[0].length).replace(/(\\r?\\n)?[ \\t]*```[ \\t]*$/, '');\n return inner.trim();\n }\n // Fence embedded in prose: extract the first complete fenced block.\n const embedded = /```[\\w+-]*[ \\t]*\\r?\\n([\\s\\S]*?)\\r?\\n[ \\t]*```/.exec(trimmed);\n if (embedded) return (embedded[1] ?? '').trim();\n return null;\n}\n\n/**\n * Walk the string tracking whether we are inside a JSON string literal and\n * replace raw control characters (U+0000\u2013U+001F) that appear inside strings\n * with their valid JSON escape sequences. Characters outside strings are left\n * untouched (insignificant whitespace stays as-is). Already-escaped sequences\n * are not double-escaped because we only act on *literal* control bytes.\n */\nfunction escapeControlCharsInStrings(s: string): string {\n let inString = false;\n let out = '';\n for (let i = 0; i < s.length; i++) {\n const c = s.charAt(i);\n if (c === '\"' && (i === 0 || s[i - 1] !== '\\\\')) {\n inString = !inString;\n out += c;\n continue;\n }\n const code = c.charCodeAt(0);\n if (inString && code < 0x20) {\n switch (c) {\n case '\\n':\n out += '\\\\n';\n break;\n case '\\r':\n out += '\\\\r';\n break;\n case '\\t':\n out += '\\\\t';\n break;\n case '\\b':\n out += '\\\\b';\n break;\n case '\\f':\n out += '\\\\f';\n break;\n default:\n out += `\\\\u${code.toString(16).padStart(4, '0')}`;\n }\n continue;\n }\n out += c;\n }\n return out;\n}\n\nfunction stripJsonComments(s: string): string {\n let inString = false;\n let escaped = false;\n const chars: string[] = [];\n let i = 0;\n\n while (i < s.length) {\n const c = s.charAt(i);\n\n if (inString) {\n chars.push(c);\n if (escaped) {\n escaped = false;\n } else if (c === '\\\\') {\n escaped = true;\n } else if (c === '\"') {\n inString = false;\n }\n i++;\n continue;\n }\n\n if (c === '\"') {\n inString = true;\n chars.push(c);\n i++;\n continue;\n }\n\n if (c === '/' && s.charAt(i + 1) === '/') {\n while (i < s.length && s.charAt(i) !== '\\n') i++;\n continue;\n }\n\n if (c === '/' && s.charAt(i + 1) === '*') {\n const end = s.indexOf('*/', i + 2);\n if (end === -1) {\n // Preserve an unterminated opener so the final JSON.parse rejects it.\n chars.push(s.slice(i));\n break;\n }\n i = end + 2;\n continue;\n }\n\n chars.push(c);\n i++;\n }\n\n return chars.join('');\n}\n", "import * as path from 'node:path';\nimport { ERROR_CODES, FsError } from '../types/errors.js';\n\n/**\n * Resolve `<dir>/<sessionId><suffix>` for per-session sidecar files\n * (annotations, audit chain, replay log, the session JSONL itself).\n *\n * Modern session ids are date-sharded (\"2026-06-11/sess_<ULID>\"),\n * so a forward slash is a legitimate shard separator \u2014 NOT traversal.\n * Escape attempts are blocked two ways: an explicit ban on `..` and\n * backslashes, plus a resolved-path containment check that rejects any\n * id whose resolved target leaves `dir`. Character bans alone are how\n * several stores ended up throwing on every modern session id.\n */\nexport function sessionScopedPath(dir: string, sessionId: string, suffix: string): string {\n if (!sessionId || sessionId.includes('\\\\') || sessionId.includes('..')) {\n throw invalid(sessionId);\n }\n const resolved = path.resolve(dir, `${sessionId}${suffix}`);\n const rel = path.relative(path.resolve(dir), resolved);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw invalid(sessionId);\n }\n return resolved;\n}\n\nfunction invalid(sessionId: string): FsError {\n return new FsError({\n message: `Invalid sessionId: ${sessionId}`,\n code: ERROR_CODES.FS_DELETE_FAILED,\n path: sessionId,\n context: { reason: 'path_traversal' },\n });\n}\n", "/**\n * Turn an arbitrary string into a filesystem- and URL-safe lowercase slug.\n *\n * Collapses every run of non-alphanumeric characters into a single hyphen,\n * trims leading/trailing hyphens, and caps the length. Returns `fallback`\n * when the input slugifies to the empty string.\n *\n * Used as the stable dedup + registry key for prompts. (Distinct from the\n * project-folder slug in `wstack-paths.ts`, which has its own `'project'`\n * fallback and shorter cap.)\n */\nexport function slugify(name: string, fallback = 'prompt', maxLen = 64): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, maxLen)\n .replace(/-+$/g, '') || fallback\n );\n}\n", "import { createHash } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\n/**\n * Path layout. All developer-level state lives in ~/.wrongstack/.\n * Per-project state is keyed by the canonical project root under\n * ~/.wrongstack/projects/<slug>/. Linked Git worktrees resolve to their main\n * checkout so coordination and durable state are shared across worktrees.\n *\n * The ONLY thing inside the project tree is the optional\n * .wrongstack/AGENTS.md (committed) and .wrongstack/skills/ (committed).\n */\n\nexport interface WstackPaths {\n /** ~/.wrongstack \u2014 global root. */\n globalRoot: string;\n /** Active profile name resolved from the bootstrap config. */\n profileName: string;\n /** ~/.wrongstack/profiles/<activeProfile> \u2014 active user-profile root. */\n profileDir: string;\n /** Absolute project root. */\n projectRoot: string;\n /** Home directory (~) \u2014 base for foreign tool state (~/.codex, ~/.agents, \u2026). */\n homeDir: string;\n /**\n * ~/.wrongstack/profiles/<activeProfile> \u2014 directory for profile-scoped\n * user state (mode.json, statusline.json, custom-context-modes.json, \u2026).\n */\n configDir: string;\n /** ~/.wrongstack/config.json \u2014 bootstrap config (activeProfile + version). */\n globalConfig: string;\n /**\n * ~/.wrongstack/profiles \u2014 directory containing per-profile configs.\n * Each profile has its own subdirectory with a config.json.\n */\n profilesDir: string;\n /**\n * Resolve the config path for a named profile.\n * Returns ~/.wrongstack/profiles/<name>/config.json.\n */\n profileConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/statusline.json */\n profileStatuslineConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/mode.json */\n profileModeConfig: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/provider-status.json */\n profileProviderStatus: (name: string) => string;\n /** Resolve ~/.wrongstack/profiles/<name>/update-cache.json */\n profileUpdateCache: (name: string) => string;\n /** ~/.wrongstack/.key \u2014 32 random bytes, mode 0600, AES-GCM key for the secret vault. */\n secretsKey: string;\n /** ~/.wrongstack/profiles/<activeProfile>/memory.md \u2014 profile memory. */\n globalMemory: string;\n /** ~/.wrongstack/profiles/<activeProfile>/skills \u2014 profile skills. */\n globalSkills: string;\n /** ~/.claude/skills \u2014 user-global skills from foreign coding agents (Claude Code, Codex, \u2026). Read-only. */\n globalClaudeSkills: string;\n /** ~/.wrongstack/profiles/<activeProfile>/design-kits \u2014 profile Design Studio kits. */\n globalDesignKits: string;\n /** ~/.wrongstack/profiles/<activeProfile>/prompts \u2014 profile prompt library. */\n globalPrompts: string;\n /** ~/.wrongstack/profiles/<activeProfile>/instructions \u2014 profile instruction overrides. */\n globalInstructions: string;\n /** ~/.wrongstack/profiles/<activeProfile>/prompt-usage.json \u2014 prompt usage. */\n promptUsage: string;\n /** ~/.wrongstack/cache \u2014 fetched data (models.dev, etc.). */\n cacheDir: string;\n /** ~/.wrongstack/cache/models.dev.json */\n modelsCache: string;\n /** ~/.wrongstack/cache/models-overlay.json \u2014 cached curated overlay. */\n modelsOverlayCache: string;\n /**\n * Per-project codebase symbol index (SQLite). Lives under the global project\n * dir \u2014 NOT inside the repo \u2014 so it never clutters the working tree or needs\n * gitignoring. `~/.wrongstack/projects/<hash>/codebase-index`.\n */\n projectCodebaseIndex: string;\n /** ~/.wrongstack/profiles/<activeProfile>/history \u2014 REPL line history. */\n historyFile: string;\n /** ~/.wrongstack/logs/wrongstack.log */\n logFile: string;\n /** ~/.wrongstack/projects/<hash> */\n projectDir: string;\n /** ~/.wrongstack/projects/<hash>/memory.md */\n projectMemory: string;\n /** ~/.wrongstack/projects/<hash>/sessions */\n projectSessions: string;\n /** ~/.wrongstack/projects/<hash>/trust.json */\n projectTrust: string;\n /** ~/.wrongstack/projects/<hash>/meta.json */\n projectMeta: string;\n /** ~/.wrongstack/projects/<hash>/config.local.json \u2014 optional override */\n projectLocalConfig: string;\n /** <project>/.wrongstack/config.json \u2014 per-project settings (safe fields only).\n * This lives inside the project root so it can be gitignored or shared. */\n inProjectConfig: string;\n /** <project>/.wrongstack/AGENTS.md \u2014 committed project memory. */\n inProjectAgentsFile: string;\n /** <project>/.wrongstack/skills \u2014 committed project skills. */\n inProjectSkills: string;\n /** <project>/.claude/skills \u2014 project skills authored for foreign coding agents (Claude Code, \u2026). Read-only. */\n inProjectClaudeSkills: string;\n /** <project>/.wrongstack/prompts \u2014 committed project prompt library. */\n inProjectPrompts: string;\n /** <project>/.wrongstack/instructions \u2014 committed project instruction overrides. */\n inProjectInstructions: string;\n /** <project>/.wrongstack/design-kits \u2014 committed project Design Studio kits. */\n inProjectDesignKits: string;\n /** <project>/.wrongstack/worktrees \u2014 git worktrees for per-phase isolation (gitignored). */\n inProjectWorktrees: string;\n /** Stable hash for the canonical project root (shared by linked Git worktrees). */\n projectHash: string;\n /** Human-readable canonical project slug, shared by linked Git worktrees. */\n projectSlug: string;\n /** ~/.wrongstack/projects/<hash>/goal.json \u2014 goal persistence */\n projectGoal: string;\n /** ~/.wrongstack/projects/<hash>/input-history.json \u2014 TUI prompt input history */\n projectInputHistory: string;\n /** ~/.wrongstack/projects/<hash>/specs \u2014 SDD spec files */\n projectSpecs: string;\n /** ~/.wrongstack/projects/<hash>/task-graphs \u2014 SDD task graphs */\n projectTaskGraphs: string;\n /** ~/.wrongstack/projects/<hash>/sdd-session.json \u2014 SDD session state */\n projectSddSession: string;\n /** ~/.wrongstack/projects/<hash>/plan.json \u2014 plan persistence */\n projectPlan: string;\n /** ~/.wrongstack/projects/<hash>/autophase \u2014 Goal phase-graph JSON files (dir name kept for backward compat) */\n projectAutophase: string;\n /** ~/.wrongstack/projects/<hash>/sdd-boards \u2014 live SDD board snapshots + JSONL event logs */\n projectSddBoards: string;\n /** ~/.wrongstack/profiles/<activeProfile>/sync.json \u2014 CloudSync configuration */\n syncConfig: string;\n /** ~/.wrongstack/config-history \u2014 timestamped backups on every config write */\n configHistoryDir: string;\n /** Function to get the status.json path for a project given its hash. */\n projectStatus: (projectHash: string) => string;\n}\n\n/**\n * Resolve the stable project identity root used by global WrongStack state.\n *\n * A linked Git worktree has its own checkout path and a `.git` *file* that\n * points into `<main>/.git/worktrees/<name>`. Its `commondir` points back to\n * the main checkout's `.git` directory. Treating the linked checkout path as\n * the project identity would split one repository into multiple session,\n * registry, and mailbox directories \u2014 agents in different worktrees would be\n * unable to see or message each other.\n *\n * Project-local paths still use the caller's actual checkout. Only global\n * state identity is canonicalized. Non-Git projects, normal checkouts, Git\n * submodules, and separate-git-dir layouts keep their existing identity.\n */\nexport function canonicalProjectRoot(absRoot: string): string {\n const checkoutRoot = path.resolve(absRoot);\n const dotGit = path.join(checkoutRoot, '.git');\n\n try {\n if (!fs.statSync(dotGit).isFile()) return checkoutRoot;\n\n const gitDirLine = fs.readFileSync(dotGit, 'utf8').trim();\n const match = /^gitdir:\\s*(.+)$/i.exec(gitDirLine);\n if (!match?.[1]) return checkoutRoot;\n\n const gitDir = path.resolve(checkoutRoot, match[1].trim());\n const commonDirFile = path.join(gitDir, 'commondir');\n if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;\n\n const commonDir = path.resolve(gitDir, fs.readFileSync(commonDirFile, 'utf8').trim());\n // Linked worktrees created by Git share the main checkout's `.git` dir.\n // A submodule or --separate-git-dir layout may point elsewhere; do not\n // guess a working-tree root for those shapes.\n if (path.basename(commonDir).toLowerCase() !== '.git') return checkoutRoot;\n return path.dirname(commonDir);\n } catch {\n // Missing/malformed administrative files must never make path resolution\n // fail. Falling back preserves the pre-canonicalization behavior.\n return checkoutRoot;\n }\n}\n\nexport function projectHash(absRoot: string): string {\n return createHash('sha256').update(canonicalProjectRoot(absRoot)).digest('hex').slice(0, 12);\n}\n\n/**\n * Human-readable project directory name: slugified folder name + short hash\n * suffix for uniqueness. e.g. `wrongstack-a1b2c3` instead of `3024e5e6fa58`.\n */\nexport function projectSlug(absRoot: string): string {\n const identityRoot = canonicalProjectRoot(absRoot);\n const base = slugify(path.basename(identityRoot));\n const hash = createHash('sha256').update(identityRoot).digest('hex').slice(0, 6);\n return `${base}-${hash}`;\n}\n\n/** Turn a folder name into a filesystem-safe lowercase slug. */\nfunction slugify(name: string): string {\n return (\n name\n .toLowerCase()\n // Collapse any run of non-alphanumeric chars into a single hyphen.\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40) || 'project'\n );\n}\n\nexport interface WstackPathOptions {\n userHome?: string | undefined;\n projectRoot: string;\n /** Override the global root (e.g. for tests). Default: `${userHome}/.wrongstack`. */\n globalRoot?: string | undefined;\n /** Explicit profile override. Otherwise read from the root bootstrap config. */\n profileName?: string | undefined;\n}\n\n/** Make an untrusted profile name safe for use as one path segment. */\nexport function safeProfileName(name: string | undefined): string {\n const safe = (name ?? '').replace(/[/\\\\:]/g, '_').replace(/\\.\\./g, '_').trim();\n return safe || 'default';\n}\n\n/**\n * Resolve the selected profile from ~/.wrongstack/config.json. The root file\n * is bootstrap metadata only; a missing/corrupt bootstrap selects `default`.\n */\nexport function activeProfileName(globalRoot: string): string {\n try {\n const parsed = JSON.parse(fs.readFileSync(path.join(globalRoot, 'config.json'), 'utf8')) as {\n activeProfile?: unknown;\n };\n return safeProfileName(typeof parsed.activeProfile === 'string' ? parsed.activeProfile : undefined);\n } catch {\n return 'default';\n }\n}\n\n/**\n * The global `~/.wrongstack` root, honoring the `WRONGSTACK_HOME` env\n * override. The override exists so tests (and sandboxed runs) can redirect\n * ALL global state \u2014 config, secrets, logs, projects/, mailboxes \u2014 away from\n * the real user home. Before it existed, `pnpm test` booted runtimes against\n * the real `~/.wrongstack`: it read the user's real config.json (starting a\n * second live Telegram poller), appended to the real wrongstack.log, and left\n * ~20k orphaned fixture dirs under projects/.\n *\n * Every code path that wants the global dir must come through here (or\n * through `resolveWstackPaths`) instead of `path.join(os.homedir(), '.wrongstack')`.\n */\nexport function wstackGlobalRoot(): string {\n const fromEnv = process.env['WRONGSTACK_HOME'];\n if (fromEnv && fromEnv.trim().length > 0) return path.resolve(fromEnv);\n return path.join(os.homedir(), '.wrongstack');\n}\n\nexport function resolveWstackPaths(opts: WstackPathOptions): WstackPaths {\n // Precedence: explicit globalRoot > explicit userHome (callers/tests that\n // pass one expect paths under it) > WRONGSTACK_HOME env > real home dir.\n const globalRoot =\n opts.globalRoot ?? (opts.userHome ? path.join(opts.userHome, '.wrongstack') : wstackGlobalRoot());\n // Home dir for FOREIGN tool state (Claude Code's ~/.claude). Independent of\n // WRONGSTACK_HOME, which redirects only WrongStack state: a real user's\n // Claude skills live in their real home, but tests pass `userHome` to keep\n // both `.wrongstack` and `.claude` under a temp dir.\n const homeDir = opts.userHome ?? os.homedir();\n const profileName = safeProfileName(opts.profileName ?? activeProfileName(globalRoot));\n const profileDir = path.join(globalRoot, 'profiles', profileName);\n const hash = projectHash(opts.projectRoot);\n const slug = projectSlug(opts.projectRoot);\n const projectDir = path.join(globalRoot, 'projects', slug);\n return {\n globalRoot,\n profileName,\n profileDir,\n projectRoot: opts.projectRoot,\n homeDir,\n configDir: profileDir,\n globalConfig: path.join(globalRoot, 'config.json'),\n profilesDir: path.join(globalRoot, 'profiles'),\n profileConfig: (name: string) => {\n return path.join(globalRoot, 'profiles', safeProfileName(name), 'config.json');\n },\n profileStatuslineConfig: (name: string) => {\n return path.join(globalRoot, 'profiles', safeProfileName(name), 'statusline.json');\n },\n profileModeConfig: (name: string) => {\n return path.join(globalRoot, 'profiles', safeProfileName(name), 'mode.json');\n },\n profileProviderStatus: (name: string) => {\n return path.join(globalRoot, 'profiles', safeProfileName(name), 'provider-status.json');\n },\n profileUpdateCache: (name: string) => {\n return path.join(globalRoot, 'profiles', safeProfileName(name), 'update-cache.json');\n },\n secretsKey: path.join(globalRoot, '.key'),\n globalMemory: path.join(profileDir, 'memory.md'),\n globalSkills: path.join(profileDir, 'skills'),\n globalClaudeSkills: path.join(homeDir, '.claude', 'skills'),\n globalDesignKits: path.join(profileDir, 'design-kits'),\n globalPrompts: path.join(profileDir, 'prompts'),\n globalInstructions: path.join(profileDir, 'instructions'),\n promptUsage: path.join(profileDir, 'prompt-usage.json'),\n cacheDir: path.join(globalRoot, 'cache'),\n modelsCache: path.join(globalRoot, 'cache', 'models.dev.json'),\n modelsOverlayCache: path.join(globalRoot, 'cache', 'models-overlay.json'),\n historyFile: path.join(profileDir, 'history'),\n logFile: path.join(globalRoot, 'logs', 'wrongstack.log'),\n projectDir,\n projectCodebaseIndex: path.join(projectDir, 'codebase-index'),\n projectMemory: path.join(projectDir, 'memory.md'),\n projectSessions: path.join(projectDir, 'sessions'),\n projectTrust: path.join(projectDir, 'trust.json'),\n projectMeta: path.join(projectDir, 'meta.json'),\n projectLocalConfig: path.join(projectDir, 'config.local.json'),\n inProjectConfig: path.join(opts.projectRoot, '.wrongstack', 'config.json'),\n inProjectAgentsFile: path.join(opts.projectRoot, '.wrongstack', 'AGENTS.md'),\n inProjectSkills: path.join(opts.projectRoot, '.wrongstack', 'skills'),\n inProjectClaudeSkills: path.join(opts.projectRoot, '.claude', 'skills'),\n inProjectPrompts: path.join(opts.projectRoot, '.wrongstack', 'prompts'),\n inProjectInstructions: path.join(opts.projectRoot, '.wrongstack', 'instructions'),\n inProjectDesignKits: path.join(opts.projectRoot, '.wrongstack', 'design-kits'),\n inProjectWorktrees: path.join(opts.projectRoot, '.wrongstack', 'worktrees'),\n projectHash: hash,\n projectSlug: slug,\n projectGoal: path.join(projectDir, 'goal.json'),\n projectInputHistory: path.join(projectDir, 'input-history.json'),\n projectSpecs: path.join(projectDir, 'specs'),\n projectTaskGraphs: path.join(projectDir, 'task-graphs'),\n projectSddSession: path.join(projectDir, 'sdd-session.json'),\n projectPlan: path.join(projectDir, 'plan.json'),\n projectAutophase: path.join(projectDir, 'autophase'),\n projectSddBoards: path.join(projectDir, 'sdd-boards'),\n syncConfig: path.join(profileDir, 'sync.json'),\n configHistoryDir: path.join(globalRoot, 'config-history'),\n projectStatus: (projectHash: string) => path.join(globalRoot, 'projects', projectHash, 'status.json'),\n };\n}\n", "import { closeSync, createReadStream, fsyncSync, openSync, writeSync } from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { createInterface } from 'node:readline';\nimport type { EventBus } from '../kernel/events.js';\nimport type { SecretScrubber } from '../types/secret-scrubber.js';\nimport type {\n FileSnapshot,\n SessionEvent,\n SessionMetadata,\n SessionSummary,\n SessionWriter,\n WorkspaceCheckpointRef,\n} from '../types/session.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/index.js';\nimport type { SessionCheckpointCas } from './session-checkpoint-cas.js';\nimport { sessionContentPreview, userInputTitle } from './session-helpers.js';\nimport {\n findSessionCheckpointTruncatePlan,\n rewriteSessionToCheckpoint,\n} from './session-writer-truncate.js';\nimport { scrubSessionWriterEvent } from './session-writer-scrubber.js';\n\n/**\n * Append-mode JSONL session writer with batched writes, write serialization,\n * and enriched summary tracking.\n *\n * Extracted from session-store.ts to keep each module focused: this class\n * owns the per-session write path (append/flush/close/checkpoint/truncate),\n * while `DefaultSessionStore` owns the store-level read/list/index/delete path.\n */\nexport class FileSessionWriter implements SessionWriter {\n private closed = false;\n private closePromise: Promise<void> | null = null;\n private manifestFile: string;\n private summary: SessionSummary;\n private tokenIn = 0;\n private tokenOut = 0;\n private baseTokenTotal = 0;\n private readonly filePath: string;\n get transcriptPath(): string | undefined {\n return this.filePath || undefined;\n }\n /**\n * Lazy session_start/session_resumed init, shared by all appenders.\n * A single promise (not a boolean) so a second append racing the first\n * can't push its event into the buffer BEFORE the first append's event \u2014\n * every appender awaits the same init and resumes in FIFO call order.\n */\n private initPromise: Promise<void> | null = null;\n private ensureInit(): Promise<void> {\n if (!this.initPromise) this.initPromise = this.writeSessionStartLazy();\n return this.initPromise;\n }\n private readonly resumed: boolean;\n private appendFailCount = 0;\n private lastAppendWarnAt = 0;\n private readonly secretScrubber?: SecretScrubber | undefined;\n private readonly checkpointCas?: SessionCheckpointCas | undefined;\n /** Mutable slot for onAppend callback \u2014 constructor opts seed it, setOnAppend replaces it. */\n private _onAppend: ((event: SessionEvent) => void) | undefined;\n /** Implements SessionWriter.onAppend \u2014 public getter/setter for direct property access. */\n get onAppend(): ((event: SessionEvent) => void) | undefined {\n return this._onAppend;\n }\n set onAppend(cb: ((event: SessionEvent) => void) | undefined) {\n this._onAppend = cb;\n }\n /** Mutable slot for onAppendBatch callback. */\n private _onAppendBatch: ((events: SessionEvent[]) => void) | undefined;\n /** Implements SessionWriter.onAppendBatch \u2014 public getter/setter for direct property access. */\n get onAppendBatch(): ((events: SessionEvent[]) => void) | undefined {\n return this._onAppendBatch;\n }\n set onAppendBatch(cb: ((events: SessionEvent[]) => void) | undefined) {\n this._onAppendBatch = cb;\n }\n private readonly onCloseCb?: ((summary: SessionSummary) => void | Promise<void>) | undefined;\n private readonly resolveNameCb?:\n | (() => Promise<Pick<SessionSummary, 'name'> | null>)\n | undefined;\n /** Implements SessionWriter.traceId \u2014 propagated from ContextInit.traceId. */\n traceId: string | undefined;\n\n // \u2500\u2500 Write buffer \u2014 batches events to reduce per-event disk I/O \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n\n /**\n * Set or replace the onAppend callback. Used by telemetry bridges that\n * receive the writer as an already-constructed dependency.\n */\n setOnAppend(cb: ((event: SessionEvent) => void) | undefined): void {\n this._onAppend = cb;\n }\n\n /**\n * Set or replace the onAppendBatch callback.\n */\n setOnAppendBatch(cb: ((events: SessionEvent[]) => void) | undefined): void {\n this._onAppendBatch = cb;\n }\n\n //\n // Every append() pushes the scrubbed event into an in-memory buffer instead\n // of calling handle.appendFile() synchronously. The buffer flushes to disk\n // when it reaches FLUSH_SIZE events OR after FLUSH_INTERVAL_MS of inactivity.\n // This cuts the number of disk writes by ~95% without changing the on-disk\n // format \u2014 the JSONL is still one JSON object per line.\n private writeBuffer: SessionEvent[] = [];\n private writeBufferBytes = 0;\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private static readonly FLUSH_INTERVAL_MS = 500;\n private static readonly FLUSH_SIZE = 50;\n private static readonly WRITE_BUFFER_MAX_EVENTS = 2_000;\n private static readonly WRITE_BUFFER_MAX_BYTES = 16 * 1024 * 1024;\n private bufferOverflowCount = 0;\n private lastBufferOverflowWarnAt = 0;\n\n private eventBytes(event: SessionEvent): number {\n try {\n return Buffer.byteLength(JSON.stringify(event), 'utf8') + 1;\n } catch {\n return FileSessionWriter.WRITE_BUFFER_MAX_BYTES + 1;\n }\n }\n\n private pushWriteBuffer(event: SessionEvent): boolean {\n const bytes = this.eventBytes(event);\n if (\n this.writeBuffer.length >= FileSessionWriter.WRITE_BUFFER_MAX_EVENTS ||\n bytes > FileSessionWriter.WRITE_BUFFER_MAX_BYTES ||\n this.writeBufferBytes + bytes > FileSessionWriter.WRITE_BUFFER_MAX_BYTES\n ) {\n this.bufferOverflowCount++;\n const now = Date.now();\n if (now - this.lastBufferOverflowWarnAt > 5_000) {\n console.warn(\n JSON.stringify({\n level: 'error',\n event: 'session.buffer_overflow',\n sessionId: this.id,\n bufferedEvents: this.writeBuffer.length,\n bufferedBytes: this.writeBufferBytes,\n droppedEvents: this.bufferOverflowCount,\n timestamp: new Date().toISOString(),\n }),\n );\n this.bufferOverflowCount = 0;\n this.lastBufferOverflowWarnAt = now;\n }\n return false;\n }\n this.writeBuffer.push(event);\n this.writeBufferBytes += bytes;\n return true;\n }\n\n // \u2500\u2500 Write serialization \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // All disk writes are funneled through a FIFO promise chain. Without it,\n // a timer-driven flush racing an explicit flush()/close() issues two\n // concurrent appendFile() calls on the shared O_APPEND handle \u2014 the kernel\n // may complete them out of order (chronology breaks) or, for large\n // batches, interleave partial writes (torn JSONL lines). The write chain\n // serializes handle I/O; flushPromise coalesces concurrent flush requests\n // into one drain so callers cannot create an unbounded promise chain.\n private writeChain: Promise<void> = Promise.resolve();\n private flushPromise: Promise<void> | null = null;\n\n /** Enqueue a write on the FIFO chain. Resolves/rejects with that write. */\n private enqueueWrite(data: string): Promise<void> {\n const write = this.writeChain.then(async () => {\n // Lazy reopen: clearSession() closes the handle to allow a subsequent\n // clearHistory() atomicWrite (tmp + rename) on the same file. If the\n // handle is still closed when the next append arrives, reopen it.\n try {\n return await this.handle.appendFile(data, 'utf8');\n } catch (err: unknown) {\n const nodeErr = err as NodeJS.ErrnoException;\n if (nodeErr?.code === 'EBADF') {\n this.handle = await fsp.open(this.filePath, 'a', 0o600);\n return await this.handle.appendFile(data, 'utf8');\n }\n throw err;\n }\n });\n this.writeChain = write.then(\n () => undefined,\n () => undefined,\n );\n return write;\n }\n\n // \u2500\u2500 Enriched summary tracking \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n private iterationCount = 0;\n private toolCallCount = 0;\n private toolErrorCount = 0;\n private toolBreakdown: Record<string, number> = {};\n private fileChangeCount = 0;\n private compactionCount = 0;\n private messageCount = 0;\n private lastUserMessage: string | undefined;\n private lastActivityAt: string;\n private outcome: SessionSummary['outcome'] = undefined;\n\n private pendingFileSnapshots: Array<{\n path: string;\n action: 'created' | 'modified' | 'deleted';\n before: string | null;\n after: string | null;\n }> = [];\n private pendingFileSnapshotBytes = 0;\n private static readonly PENDING_FILE_SNAPSHOT_MAX_ENTRIES = 256;\n private static readonly PENDING_FILE_SNAPSHOT_MAX_BYTES = 16 * 1024 * 1024;\n /** Prompt whose tool work is currently executing. Set by writeCheckpoint. */\n private activePromptIndex: number | null = null;\n /** Tracks open tool_use IDs during the current run to serialize on close for resume. */\n private openToolUses = new Set<string>();\n\n /**\n * Buffer an event from a synchronous Context callback. ensureInit() starts\n * by pushing the lifecycle preamble synchronously, so session_start always\n * precedes observations even though these callbacks cannot await append().\n */\n private bufferSynchronousEvent(event: SessionEvent): void {\n if (this.closed) return;\n void this.ensureInit();\n this.observeForSummary(event);\n // Fire the onAppend callback so file_snapshot and file_observation events\n // also reach the HQ bridge without a disk read-back. Scrub file_snapshot\n // events through the shared scrubber (which now handles the file_snapshot\n // type) to avoid duplicating the scrubbing logic inline. The scrubbed\n // event is pushed to the JSONL buffer so secrets never persist at rest,\n // matching the async append() path contract.\n const appendEvent = event.type === 'file_snapshot'\n ? scrubSessionWriterEvent(event, this.secretScrubber)\n : event;\n try { this._onAppend?.(appendEvent); } catch { /* best-effort */ }\n this.pushWriteBuffer(appendEvent);\n if (this.writeBuffer.length >= FileSessionWriter.FLUSH_SIZE) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n void this.flushBuffer().catch(() => {\n // Retained at the head of writeBuffer for the boundary retry.\n });\n } else {\n this.scheduleFlush();\n }\n }\n\n recordFileChange(input: {\n path: string;\n action: 'created' | 'modified' | 'deleted';\n before: string | null;\n after: string | null;\n }): void {\n if (this.closed) return;\n if (this.activePromptIndex === null) {\n // Compatibility path for embedders that mutate before their first\n // checkpoint. writeCheckpoint()/close() will attach these changes to the\n // first available prompt index.\n const bytes =\n Buffer.byteLength(input.path, 'utf8') +\n (input.before ? Buffer.byteLength(input.before, 'utf8') : 0) +\n (input.after ? Buffer.byteLength(input.after, 'utf8') : 0);\n if (\n this.pendingFileSnapshots.length >= FileSessionWriter.PENDING_FILE_SNAPSHOT_MAX_ENTRIES ||\n bytes > FileSessionWriter.PENDING_FILE_SNAPSHOT_MAX_BYTES ||\n this.pendingFileSnapshotBytes + bytes > FileSessionWriter.PENDING_FILE_SNAPSHOT_MAX_BYTES\n ) {\n console.warn(\n JSON.stringify({\n level: 'error',\n event: 'session.file_snapshot_buffer_overflow',\n sessionId: this.id,\n bufferedFiles: this.pendingFileSnapshots.length,\n bufferedBytes: this.pendingFileSnapshotBytes,\n timestamp: new Date().toISOString(),\n }),\n );\n return;\n }\n this.pendingFileSnapshots.push(input);\n this.pendingFileSnapshotBytes += bytes;\n return;\n }\n\n // This method is intentionally synchronous because file tools call it\n // immediately after their atomic mutation. Put the reconstruct event in\n // the writer buffer before the tool returns; agent-tools flushes the buffer\n // together with the matching tool_result boundary.\n const event: SessionEvent = {\n type: 'file_snapshot',\n ts: new Date().toISOString(),\n promptIndex: this.activePromptIndex,\n files: [input],\n };\n this.bufferSynchronousEvent(event);\n }\n\n recordFileObservation(input: {\n path: string;\n hash: string;\n mtimeMs: number;\n source: 'user' | 'write';\n }): void {\n if (!input.path || !/^[a-f\\d]{64}$/i.test(input.hash) || !Number.isFinite(input.mtimeMs))\n return;\n this.bufferSynchronousEvent({\n type: 'file_observation',\n ts: new Date().toISOString(),\n path: input.path,\n hash: input.hash.toLowerCase(),\n mtimeMs: input.mtimeMs,\n source: input.source,\n });\n }\n\n recordSideEffect(input: {\n toolUseId: string;\n toolName: string;\n input: Record<string, unknown>;\n outcome?: string | undefined;\n risk: 'fs.write' | 'shell' | 'package' | 'network' | 'config';\n }): void {\n // Fire-and-forget \u2014 side-effect recording must never block tool execution.\n this.append({\n type: 'side_effect',\n ts: new Date().toISOString(),\n toolUseId: input.toolUseId,\n toolName: input.toolName,\n input: input.input,\n outcome: input.outcome,\n risk: input.risk,\n }).catch(() => {\n /* best-effort */\n });\n }\n\n constructor(\n public readonly id: string,\n private handle: fsp.FileHandle,\n private readonly startedAt: string,\n private readonly meta: Omit<SessionMetadata, 'startedAt'>,\n private readonly events?: EventBus | undefined,\n opts: {\n resumed?: boolean | undefined;\n dir?: string | undefined;\n filePath?: string | undefined;\n secretScrubber?: SecretScrubber | undefined;\n checkpointCas?: SessionCheckpointCas | undefined;\n /** Called synchronously after each event is scrubbed + observed, before it enters the write buffer. */\n onAppend?: ((event: SessionEvent) => void) | undefined;\n /** Batch variant called after all events in the batch have been scrubbed + observed. */\n onAppendBatch?: ((events: SessionEvent[]) => void) | undefined;\n /** Existing cumulative summary when reopening a persisted session. */\n initialSummary?: SessionSummary | undefined;\n /** Called on close() with the finalized summary for index/sidecar writes. */\n onClose?: ((summary: SessionSummary) => void | Promise<void>) | undefined;\n /** Reconcile an explicit name changed while this writer remained open. */\n resolveName?: (() => Promise<Pick<SessionSummary, 'name'> | null>) | undefined;\n } = {},\n traceId?: string | undefined,\n ) {\n this.resumed = opts.resumed ?? false;\n // id already contains a date-prefix shard (e.g. \"2026-06-06/sess_<ULID>\").\n // opts.dir is the shard directory \u2014 join with basename so the manifest\n // lives next to the JSONL file instead of creating a double-nested path.\n this.manifestFile = opts.dir ? path.join(opts.dir, `${path.basename(id)}.summary.json`) : '';\n this.filePath = opts.filePath ?? '';\n this.secretScrubber = opts.secretScrubber;\n this.checkpointCas = opts.checkpointCas;\n this._onAppend = opts.onAppend;\n this._onAppendBatch = opts.onAppendBatch;\n this.onCloseCb = opts.onClose;\n this.resolveNameCb = opts.resolveName;\n this.summary = opts.initialSummary\n ? { ...opts.initialSummary, id }\n : {\n id,\n title: '(empty session)',\n startedAt,\n model: meta.model ?? 'unknown',\n provider: meta.provider ?? 'unknown',\n tokenTotal: 0,\n };\n this.baseTokenTotal = this.summary.tokenTotal;\n this.iterationCount = this.summary.iterationCount ?? 0;\n this.toolCallCount = this.summary.toolCallCount ?? 0;\n this.toolErrorCount = this.summary.toolErrorCount ?? 0;\n this.toolBreakdown = { ...(this.summary.toolBreakdown ?? {}) };\n this.fileChangeCount = this.summary.fileChangeCount ?? 0;\n this.compactionCount = this.summary.compactionCount ?? 0;\n this.messageCount = this.summary.messageCount ?? 0;\n this.lastUserMessage = this.summary.lastUserMessage;\n this.lastActivityAt = this.summary.lastActivityAt ?? this.summary.endedAt ?? startedAt;\n this.outcome = this.resumed ? undefined : this.summary.outcome;\n // Propagated from ContextInit.traceId via SessionWriter.traceId so that\n // storage events carry the run-level trace ID without needing a Context\n // handle in every storage operation.\n this.traceId = traceId;\n }\n\n get pendingToolUses(): string[] {\n return Array.from(this.openToolUses);\n }\n\n private async writeSessionStartLazy(): Promise<void> {\n // Keep the lifecycle preamble in the same retryable buffer as every other\n // reconstruct event. Writing it through a separate one-shot append meant\n // an ENOSPC/transient handle failure could permanently lose session_start\n // while later events survived, leaving an unidentifiable transcript.\n this.pushWriteBuffer({\n type: this.resumed ? 'session_resumed' : 'session_start',\n ts: this.startedAt,\n id: this.id,\n model: this.meta.model ?? 'unknown',\n provider: this.meta.provider ?? 'unknown',\n });\n }\n\n async append(event: SessionEvent): Promise<void> {\n if (this.closed) return;\n await this.ensureInit();\n // Scrub before observing (the summary title is derived from user_input\n // content) and before buffering, so neither the JSONL nor the sidecar\n // ever holds a cleartext secret.\n const scrubbed = scrubSessionWriterEvent(event, this.secretScrubber);\n // observeForSummary MUST run synchronously here \u2014 the summary counters\n // (toolCallCount, tokenIn/Out, outcome) drive the .summary.json sidecar\n // and the session index. Deferring observation to flush time would leave\n // the summary stale if close() fires before the next timer tick.\n this.observeForSummary(scrubbed);\n // Fire the onAppend callback with the scrubbed event so the HQ bridge\n // can stream it without reading it back from disk. Synchronous and\n // best-effort \u2014 the callback must not throw.\n try { this._onAppend?.(scrubbed); } catch { /* best-effort */ }\n this.pushWriteBuffer(scrubbed);\n\n if (this.writeBuffer.length >= FileSessionWriter.FLUSH_SIZE) {\n // Buffer full \u2014 flush immediately. Cancel any pending timer so we\n // don't double-flush on the next tick.\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flushBuffer().catch(() => {\n // append() is intentionally best-effort. The failed batch remains at\n // the front of writeBuffer; an explicit boundary flush can surface the\n // error while ordinary audit appends do not abort the agent loop.\n });\n } else {\n this.scheduleFlush();\n }\n }\n\n async appendBatch(events: SessionEvent[]): Promise<void> {\n if (this.closed || events.length === 0) return;\n await this.ensureInit();\n const scrubbedBatch: SessionEvent[] = [];\n for (const event of events) {\n const scrubbed = scrubSessionWriterEvent(event, this.secretScrubber);\n this.observeForSummary(scrubbed);\n // Fire the per-event callback so subscribers to onAppend (rather than\n // onAppendBatch) also receive batch events individually.\n try { this._onAppend?.(scrubbed); } catch { /* best-effort */ }\n this.pushWriteBuffer(scrubbed);\n scrubbedBatch.push(scrubbed);\n }\n // Fire the batch callback with all scrubbed events so the HQ bridge\n // can stream them without reading them back from disk.\n try { this._onAppendBatch?.(scrubbedBatch); } catch { /* best-effort */ };\n if (this.writeBuffer.length >= FileSessionWriter.FLUSH_SIZE) {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flushBuffer().catch(() => {\n // Same best-effort append contract as append(); batch is retained.\n });\n } else {\n this.scheduleFlush();\n }\n }\n\n /**\n * Flush buffered events to disk immediately. Critical events\n * (user_input, llm_response) call this so they survive SIGKILL/crash\n * instead of sitting in the in-memory buffer for up to 500ms.\n *\n * Idempotent \u2014 cancels any pending timer, writes whatever has accumulated,\n * then asks the OS to synchronize the file data before resolving. Even an\n * empty-buffer flush synchronizes any earlier timer-driven append.\n */\n async flush(): Promise<void> {\n if (this.closed) return;\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flushBuffer();\n await this.writeChain;\n try {\n await this.handle.datasync();\n } catch (err: unknown) {\n // Handle may be closed (e.g. after clearSession with no intervening\n // append). Reopen lazily so the handle is ready for the next write.\n const nodeErr = err as NodeJS.ErrnoException;\n if (nodeErr?.code === 'EBADF') {\n this.handle = await fsp.open(this.filePath, 'a', 0o600);\n return;\n }\n throw err;\n }\n }\n\n /**\n * Last-gasp synchronous drain for hard-exit paths (process.exit after\n * rapid Ctrl+C). The async write chain cannot be awaited when the process\n * is about to die, but whatever still sits in the in-memory buffer CAN be\n * saved with a blocking append. Best-effort: an in-flight async write may\n * be cut off by the exit regardless; errors here are swallowed.\n */\n flushSync(): void {\n if (this.writeBuffer.length === 0 || !this.filePath) return;\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n const batch = this.writeBuffer.map((e) => JSON.stringify(e)).join('\\n') + '\\n';\n this.writeBuffer = [];\n this.writeBufferBytes = 0;\n let fd: number | undefined;\n try {\n fd = openSync(this.filePath, 'a');\n writeSync(fd, batch, null, 'utf8');\n fsyncSync(fd);\n } catch {\n // best-effort \u2014 the process is exiting either way\n } finally {\n if (fd !== undefined) {\n try {\n closeSync(fd);\n } catch {\n // best-effort \u2014 the process is exiting either way\n }\n }\n }\n }\n\n /** Schedule a deferred flush. No-op if a timer is already pending. */\n private scheduleFlush(): void {\n if (this.flushTimer) return;\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n /* v8 ignore start -- defensive: flushBuffer logs its own errors; this guards the timer callback */\n this.flushBuffer().catch(() => {\n // flushBuffer already logs via the throttled-warning path;\n // this catch prevents an unhandled rejection in the timer callback.\n });\n /* v8 ignore stop */\n }, FileSessionWriter.FLUSH_INTERVAL_MS);\n }\n\n /**\n * Flush all buffered events to disk as a single appendFile call.\n * Concurrent callers join one coalesced drain. On failure the drained batch\n * is prepended to the live buffer, preserving chronology and allowing the\n * next timer/boundary flush to retry it. Warnings retain the existing\n * throttled behavior.\n */\n private flushBuffer(): Promise<void> {\n if (this.flushPromise) return this.flushPromise;\n const flush = (async () => {\n while (this.writeBuffer.length > 0) await this.flushBufferOnce();\n })().finally(() => {\n if (this.flushPromise === flush) this.flushPromise = null;\n });\n this.flushPromise = flush;\n return flush;\n }\n\n private async flushBufferOnce(): Promise<void> {\n if (this.writeBuffer.length === 0) return;\n const events = this.writeBuffer;\n const eventCount = events.length;\n const eventBytes = this.writeBufferBytes;\n const batch = events.map((e) => JSON.stringify(e)).join('\\n') + '\\n';\n this.writeBuffer = [];\n this.writeBufferBytes = 0;\n const t0 = Date.now();\n let outcome: 'success' | 'failure' = 'success';\n let errorMsg: string | undefined;\n try {\n await this.enqueueWrite(batch);\n } catch (err) {\n outcome = 'failure';\n errorMsg = toErrorMessage(err);\n // Retain the failed prefix, then admit as much of the newer tail as the\n // hard budget allows. This preserves chronology without letting a\n // persistent ENOSPC/permission failure consume the heap indefinitely.\n const newer = this.writeBuffer;\n this.writeBuffer = events;\n this.writeBufferBytes = eventBytes;\n for (const newerEvent of newer) this.pushWriteBuffer(newerEvent);\n this.appendFailCount += eventCount;\n const now = Date.now();\n if (now - this.lastAppendWarnAt > 5000) {\n const suppressed = this.appendFailCount - 1;\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'session.flush_failed',\n sessionId: this.id,\n message: toErrorMessage(err),\n ...(suppressed > 0 ? { suppressed } : {}),\n timestamp: new Date().toISOString(),\n }),\n );\n this.lastAppendWarnAt = now;\n this.appendFailCount = 0;\n }\n if (!this.closed) this.scheduleFlush();\n throw err;\n } finally {\n this.events?.emit('storage.write', {\n sessionId: this.id,\n store: 'session',\n filePath: this.filePath,\n operation: 'flush',\n outcome,\n durationMs: Date.now() - t0,\n ...(errorMsg !== undefined ? { error: errorMsg } : {}),\n ...(eventCount !== undefined ? { eventCount } : {}),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n }\n }\n\n /**\n * Rebuild every accumulated summary counter by replaying the JSONL as it now\n * stands on disk.\n *\n * Only truncation needs this: the counters are fed by `observeForSummary` as\n * events go by and cannot un-count. Stream the JSONL so a very long session\n * does not need a second file-sized allocation during rewind.\n *\n * `title` is deliberately re-derived too: the first surviving user_input may\n * differ once earlier prompts are gone.\n */\n private async recomputeSummaryFromDisk(): Promise<void> {\n if (!this.filePath) return;\n const accessible = await fsp\n .access(this.filePath)\n .then(() => true)\n .catch(() => false);\n if (!accessible) return;\n\n this.iterationCount = 0;\n this.toolCallCount = 0;\n this.toolErrorCount = 0;\n this.toolBreakdown = {};\n this.fileChangeCount = 0;\n this.compactionCount = 0;\n this.messageCount = 0;\n this.lastUserMessage = undefined;\n this.lastActivityAt = this.startedAt;\n this.outcome = undefined;\n this.openToolUses = new Set<string>();\n this.tokenIn = 0;\n this.tokenOut = 0;\n this.baseTokenTotal = 0;\n const { lastUserMessage: _lastUserMessage, ...summaryWithoutPreview } = this.summary;\n this.summary = {\n ...summaryWithoutPreview,\n title: '(empty session)',\n tokenTotal: 0,\n messageCount: 0,\n lastActivityAt: this.startedAt,\n };\n\n const input = createReadStream(this.filePath, { encoding: 'utf8' });\n const lines = createInterface({ input, crlfDelay: Number.POSITIVE_INFINITY });\n try {\n for await (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.length === 0) continue;\n try {\n this.observeForSummary(JSON.parse(trimmed) as SessionEvent);\n } catch {\n // Skip a malformed line \u2014 the rewrite preserves it verbatim, and a\n // torn record must not abort the recount of everything after it.\n }\n }\n } finally {\n lines.close();\n input.destroy();\n }\n }\n\n private observeForSummary(event: SessionEvent): void {\n const eventActivityMs = Date.parse(event.ts);\n const lastActivityMs = Date.parse(this.lastActivityAt);\n if (\n Number.isFinite(eventActivityMs) &&\n (!Number.isFinite(lastActivityMs) || eventActivityMs > lastActivityMs)\n ) {\n this.lastActivityAt = event.ts;\n }\n // Track open tool uses so we can serialize them on close for resume.\n // The authoritative source is the llm_response content (a core event,\n // always written at every audit level); the legacy 'tool_use' event is\n // kept for alternate writers that still emit it.\n if (event.type === 'llm_response') {\n for (const block of event.content) {\n if (block.type === 'tool_use') this.openToolUses.add(block.id);\n }\n }\n if (event.type === 'tool_use') {\n this.openToolUses.add(event.id);\n } else if (event.type === 'tool_call_start') {\n this.toolCallCount++;\n this.toolBreakdown[event.name] = (this.toolBreakdown[event.name] ?? 0) + 1;\n } else if (event.type === 'tool_result') {\n this.openToolUses.delete(event.id);\n if (event.isError) {\n this.toolErrorCount++;\n this.outcome = 'error';\n }\n } else if (event.type === 'file_snapshot') {\n this.fileChangeCount += event.files.length;\n } else if (event.type === 'compaction') {\n this.compactionCount++;\n }\n // Error events (provider errors, execution errors) mark the session as failed.\n if (event.type === 'error' || event.type === 'provider_error') {\n this.outcome = 'error';\n }\n if (event.type === 'user_input') {\n if (this.summary.title === '(empty session)') {\n this.summary = { ...this.summary, title: userInputTitle(event.content) };\n }\n this.lastUserMessage = sessionContentPreview(event.content);\n this.messageCount++;\n } else if (event.type === 'llm_response') {\n this.messageCount++;\n this.tokenIn += event.usage.input;\n this.tokenOut += event.usage.output;\n this.summary = {\n ...this.summary,\n tokenTotal: this.baseTokenTotal + this.tokenIn + this.tokenOut,\n };\n } else if (event.type === 'session_end') {\n const total = event.usage.input + event.usage.output;\n if (total > 0) {\n this.summary = {\n ...this.summary,\n tokenTotal: Math.max(this.summary.tokenTotal, total),\n };\n }\n } else if (event.type === 'in_flight_start') {\n this.iterationCount++;\n }\n }\n\n async close(): Promise<void> {\n // Idempotent AND awaitable: concurrent/repeat callers share the same\n // promise, so nobody proceeds (e.g. to tear down the session directory)\n // while the first close is still flushing.\n if (this.closePromise) return this.closePromise;\n this.closePromise = this.doClose().catch((err) => {\n // A failed durable drain must not permanently brick the writer. Keep the\n // handle open and allow the timer or a later close() call to retry.\n this.closed = false;\n this.closePromise = null;\n if (this.writeBuffer.length > 0) this.scheduleFlush();\n throw err;\n });\n return this.closePromise;\n }\n\n private async doClose(): Promise<void> {\n if (this.pendingFileSnapshots.length > 0) {\n await this.writeFileSnapshot(this.activePromptIndex ?? 0, [...this.pendingFileSnapshots]);\n this.pendingFileSnapshots = [];\n this.pendingFileSnapshotBytes = 0;\n }\n this.closed = true;\n // Flush any buffered events before finalizing. The summary counters\n // (toolCallCount, tokenIn/Out, outcome) are already up to date because\n // observeForSummary runs synchronously on every append, but the JSONL\n // must have all events on disk before we write the .summary.json sidecar.\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flushBuffer();\n // Drain any write enqueued outside flushBuffer (e.g. the lazy\n // session_start record) before the handle is closed.\n await this.writeChain;\n try {\n await this.handle.datasync();\n } catch (err: unknown) {\n // Handle may already be closed (e.g. clearSession closed it but no\n // append reopened). Best-effort: the fd is still valid for close().\n const nodeErr = err as NodeJS.ErrnoException;\n if (nodeErr?.code !== 'EBADF') throw err;\n }\n // Finalize the summary before writing.\n const endedAt = new Date().toISOString();\n const observedActivityMs = Date.parse(this.lastActivityAt);\n const endedAtMs = Date.parse(endedAt);\n const finalActivityAt =\n Number.isFinite(observedActivityMs) && observedActivityMs > endedAtMs\n ? this.lastActivityAt\n : endedAt;\n const previousName = this.summary.name;\n const { lastUserMessage: _lastUserMessage, name: _name, ...summaryWithoutDisplay } = this.summary;\n this.summary = {\n ...summaryWithoutDisplay,\n ...(previousName !== undefined ? { name: previousName } : {}),\n endedAt,\n lastActivityAt: finalActivityAt,\n messageCount: this.messageCount,\n ...(this.lastUserMessage !== undefined ? { lastUserMessage: this.lastUserMessage } : {}),\n iterationCount: this.iterationCount,\n toolCallCount: this.toolCallCount,\n toolErrorCount: this.toolErrorCount,\n fileChangeCount: this.fileChangeCount,\n compactionCount: this.compactionCount > 0 ? this.compactionCount : undefined,\n toolBreakdown: { ...this.toolBreakdown },\n outcome: this.outcome ?? 'completed',\n };\n\n const manifestT0 = Date.now();\n let manifestOutcome: 'success' | 'failure' = 'success';\n let manifestError: string | undefined;\n const idxT0 = Date.now();\n let idxOutcome: 'success' | 'failure' = 'success';\n let idxError: string | undefined;\n const persistSummary = async (): Promise<void> => {\n const resolvedName = this.resolveNameCb\n ? await this.resolveNameCb().catch(() => null)\n : null;\n if (resolvedName !== null) {\n const { name: _drop, ...summaryWithoutName } = this.summary;\n this.summary = {\n ...summaryWithoutName,\n ...(resolvedName.name !== undefined ? { name: resolvedName.name } : {}),\n };\n }\n if (this.manifestFile) {\n try {\n await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 0o600 });\n } catch (err) {\n manifestOutcome = 'failure';\n manifestError = toErrorMessage(err);\n }\n }\n try {\n await this.onCloseCb?.(this.summary);\n /* v8 ignore start -- best-effort: appendToIndex swallows its own errors */\n } catch (err) {\n idxOutcome = 'failure';\n idxError = toErrorMessage(err);\n /* v8 ignore stop */\n }\n };\n if (this.manifestFile) await withFileLock(this.manifestFile, persistSummary);\n else await persistSummary();\n\n if (this.manifestFile) {\n this.events?.emit('storage.write', {\n sessionId: this.id,\n store: 'session',\n filePath: this.manifestFile,\n operation: 'close',\n outcome: manifestOutcome,\n durationMs: Date.now() - manifestT0,\n ...(manifestError !== undefined ? { error: manifestError } : {}),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n }\n this.events?.emit('storage.write', {\n sessionId: this.summary.id,\n store: 'session',\n filePath: this.filePath,\n operation: 'index_append',\n outcome: idxOutcome,\n durationMs: Date.now() - idxT0,\n ...(idxError !== undefined ? { error: idxError } : {}),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n try {\n await this.handle.close();\n } catch {\n // ignore\n }\n }\n\n async writeCheckpoint(promptIndex: number, promptPreview: string): Promise<void> {\n const fileCount = this.pendingFileSnapshots.length;\n if (fileCount > 0) {\n await this.writeFileSnapshot(promptIndex, [...this.pendingFileSnapshots]);\n this.pendingFileSnapshots = [];\n this.pendingFileSnapshotBytes = 0;\n }\n let workspaceCheckpoint: WorkspaceCheckpointRef | undefined;\n try {\n workspaceCheckpoint = await this.checkpointCas?.capture(this.id, promptIndex);\n } catch (err) {\n // Conversation checkpoints remain usable even when Git/CAS capture is\n // unavailable. The missing workspaceCheckpoint field makes the reduced\n // guarantee explicit to fork/materialization callers.\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'session.workspace_checkpoint_capture_failed',\n sessionId: this.id,\n promptIndex,\n message: toErrorMessage(err),\n timestamp: new Date().toISOString(),\n }),\n );\n }\n await this.append({\n type: 'checkpoint',\n ts: new Date().toISOString(),\n promptIndex,\n promptPreview,\n ...(workspaceCheckpoint ? { workspaceCheckpoint } : {}),\n });\n this.activePromptIndex = promptIndex;\n this.events?.emit('checkpoint.written', {\n sessionId: this.id,\n promptIndex,\n promptPreview,\n ts: new Date().toISOString(),\n fileCount,\n });\n }\n\n async writeFileSnapshot(promptIndex: number, files: FileSnapshot[]): Promise<void> {\n await this.append({\n type: 'file_snapshot',\n ts: new Date().toISOString(),\n promptIndex,\n files,\n });\n }\n\n /**\n * Truncate the session file to the checkpoint with the given promptIndex,\n * removing all events that follow it. Uses a single-pass byte-offset scan\n * so post-checkpoint content is never read or parsed \u2014 O(1) memory instead\n * of O(N) JSON.parse calls over the full file.\n */\n async truncateToCheckpoint(\n targetPromptIndex: number,\n revertedFiles: readonly string[] = [],\n ): Promise<number> {\n /* v8 ignore next -- defensive: filePath is always set for a live writer */\n if (!this.filePath) return 0;\n\n // Flush buffered events to disk before reading \u2014 otherwise the in-memory\n // events that haven't hit the JSONL yet would be invisible to the\n // truncation logic and would be silently dropped by the rewrite.\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n await this.flushBuffer();\n // Drain the write chain so no in-flight write straddles the close/rename/reopen.\n await this.writeChain;\n\n const plan = await findSessionCheckpointTruncatePlan(this.filePath, targetPromptIndex);\n if (!plan) return 0;\n\n // Windows EPERM fix: close the append-mode handle before replacing the\n // file. Windows rejects rename() when the destination still has an open\n // handle, even if that handle belongs to this process.\n await this.writeChain;\n try {\n await this.handle.close();\n } catch {\n // Ignore \u2014 handle may already be closed (e.g. by clearSession).\n // Consistent with the doClose() best-effort pattern.\n }\n try {\n await rewriteSessionToCheckpoint(this.filePath, plan.checkpointByteOffset);\n // Re-open in append mode for continued use of this file.\n this.handle = await fsp.open(this.filePath, 'a', 0o600);\n /* v8 ignore start -- defensive: close/rename/reopen of a just-written temp file */\n } catch (err) {\n this.handle = await fsp.open(this.filePath, 'a', 0o600).catch(() => this.handle);\n throw err;\n }\n /* v8 ignore stop */\n\n // The summary counters accumulate as events are observed and know nothing\n // about truncation, so without this `close()` would write a .summary.json \u2014\n // and an _index.jsonl row, which list() reads \u2014 still counting the tool\n // calls, file changes and tokens of the work just rewound.\n await this.recomputeSummaryFromDisk();\n\n const reverted = [...revertedFiles];\n await this.append({\n type: 'rewound',\n ts: new Date().toISOString(),\n toPromptIndex: targetPromptIndex,\n revertedFiles: reverted,\n });\n this.activePromptIndex = targetPromptIndex;\n\n this.events?.emit('session.rewound', {\n sessionId: this.id,\n toPromptIndex: targetPromptIndex,\n revertedFiles: reverted,\n removedEvents: plan.removedCount,\n });\n\n return plan.removedCount;\n }\n\n async clearSession(): Promise<void> {\n /* v8 ignore next -- defensive: filePath is always set for a live writer */\n if (!this.filePath) return;\n // Discard any buffered events \u2014 the caller is explicitly resetting the\n // session to a clean slate. Cancel the timer so it doesn't fire and\n // append stale events to the freshly-cleared file.\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n // Wait for a currently-draining batch first. If it failed and re-queued\n // itself, the explicit reset below discards it along with the rest of the\n // old conversation; if it succeeded, writeChain has already serialized it.\n await this.flushPromise?.catch(() => undefined);\n this.writeBuffer = [];\n this.writeBufferBytes = 0;\n // Let any in-flight append land first \u2014 otherwise it would re-append\n // stale events AFTER the reset record below.\n await this.writeChain;\n const resetAt = new Date().toISOString();\n const record = `${JSON.stringify({\n type: 'session_start',\n ts: resetAt,\n id: this.id,\n model: this.meta.model ?? 'unknown',\n provider: this.meta.provider ?? 'unknown',\n })}\\n`;\n // Windows EPERM fix: close the append-mode handle before replacing the\n // file. Windows rejects rename() when the destination still has an open\n // handle, even if that handle belongs to this process. The caller\n // (/clear \u2192 buildClearCommand) may also call clearHistory which uses\n // atomicWrite (tmp + rename) \u2014 so we do NOT reopen here. The handle is\n // lazily reopened in enqueueWrite on the next append.\n await this.handle.close();\n await fsp.writeFile(this.filePath, record, 'utf8');\n const explicitName = this.summary.name;\n this.baseTokenTotal = 0;\n this.tokenIn = 0;\n this.tokenOut = 0;\n this.iterationCount = 0;\n this.toolCallCount = 0;\n this.toolErrorCount = 0;\n this.toolBreakdown = {};\n this.fileChangeCount = 0;\n this.compactionCount = 0;\n this.messageCount = 0;\n this.lastUserMessage = undefined;\n this.lastActivityAt = resetAt;\n this.outcome = undefined;\n this.openToolUses = new Set<string>();\n this.summary = {\n id: this.id,\n title: '(empty session)',\n ...(explicitName !== undefined ? { name: explicitName } : {}),\n startedAt: resetAt,\n model: this.meta.model ?? 'unknown',\n provider: this.meta.provider ?? 'unknown',\n tokenTotal: 0,\n lastActivityAt: resetAt,\n messageCount: 0,\n };\n this.activePromptIndex = null;\n this.pendingFileSnapshots = [];\n this.pendingFileSnapshotBytes = 0;\n // initPromise is deliberately NOT reset \u2014 clearSession already wrote a\n // session_start line directly via fsp.writeFile (above). Resetting it\n // would cause the next append() to push a second preamble via\n // writeSessionStartLazy(), producing duplicate session_start lines.\n // Reset failure counters to a clean slate.\n this.appendFailCount = 0;\n this.lastAppendWarnAt = 0;\n this.bufferOverflowCount = 0;\n this.lastBufferOverflowWarnAt = 0;\n }\n\n /**\n * Write an in-flight marker. The agent loop should call\n * this at the start of each long-running operation; a matching\n * `clearInFlightMarker` follows on clean exit. A stale marker\n * (no end) is what `SessionRecovery.detectStale` looks for.\n */\n async writeInFlightMarker(context: string): Promise<void> {\n if (!context || context.length > 500) {\n throw new Error('In-flight context must be 1..500 chars');\n }\n await this.append({\n type: 'in_flight_start',\n ts: new Date().toISOString(),\n context,\n });\n this.events?.emit('in_flight.started', {\n sessionId: this.id,\n context,\n ts: new Date().toISOString(),\n });\n }\n\n /**\n * Close the in-flight marker. Idempotent in spirit\n * (you can call it after a successful iteration even if you\n * didn't open one this round) \u2014 but the session log records\n * every call so postmortem tooling can see \"the agent finished\n * cleanly X times, then died without finishing Y\".\n */\n async clearInFlightMarker(reason: 'clean' | 'aborted' | 'recovered'): Promise<void> {\n if (this.pendingFileSnapshots.length > 0) {\n await this.writeFileSnapshot(this.activePromptIndex ?? 0, [...this.pendingFileSnapshots]);\n this.pendingFileSnapshots = [];\n this.pendingFileSnapshotBytes = 0;\n }\n await this.append({\n type: 'in_flight_end',\n ts: new Date().toISOString(),\n reason,\n });\n this.events?.emit('in_flight.ended', {\n sessionId: this.id,\n reason,\n ts: new Date().toISOString(),\n });\n }\n}\n", "import type { ContentBlock } from '../types/blocks.js';\n\nfunction consumeControlString(value: string, start: number): number {\n let index = start;\n while (index < value.length) {\n const code = value.charCodeAt(index);\n if (code === 0x07 || code === 0x9c) return index;\n if (code === 0x1b && value.charCodeAt(index + 1) === 0x5c) return index + 1;\n index++;\n }\n return value.length;\n}\n\nfunction consumeCsi(value: string, start: number): number {\n let index = start;\n while (index < value.length && value.charCodeAt(index) < 0x40) index++;\n return index;\n}\n\nfunction stripTerminalControls(value: string): string {\n let out = '';\n for (let index = 0; index < value.length; index++) {\n const code = value.charCodeAt(index);\n if (code === 0x1b) {\n const family = value.charCodeAt(index + 1);\n if (family === 0x5b) index = consumeCsi(value, index + 2);\n else if ([0x5d, 0x50, 0x58, 0x5e, 0x5f].includes(family)) {\n index = consumeControlString(value, index + 2);\n } else index++;\n continue;\n }\n if (code === 0x9b) {\n index = consumeCsi(value, index + 1);\n continue;\n }\n if ([0x90, 0x98, 0x9d, 0x9e, 0x9f].includes(code)) {\n index = consumeControlString(value, index + 1);\n continue;\n }\n if (\n (code < 0x20 && code !== 0x09 && code !== 0x0a && code !== 0x0d) ||\n (code >= 0x7f && code <= 0x9f)\n ) {\n continue;\n }\n out += value[index];\n }\n return out;\n}\n\n/**\n * Derive a short title from a user_input event's content. Used by both\n * `DefaultSessionStore.summarize()` (offline summary rebuild) and\n * `FileSessionWriter.observeForSummary()` (live tracking), so it lives\n * here to avoid a bidirectional import between those two modules.\n */\nexport function sessionContentText(content: string | ContentBlock[]): string {\n const text =\n typeof content === 'string'\n ? content\n : content\n .filter((b): b is { type: 'text'; text: string } => b.type === 'text')\n .map((b) => b.text)\n .join(' ');\n return stripTerminalControls(text).trim().replace(/\\s+/g, ' ');\n}\n\nexport function sessionContentPreview(\n content: string | ContentBlock[],\n maxLength = 160,\n): string {\n const text = sessionContentText(content) || '(non-text input)';\n return text.length > maxLength ? `${text.slice(0, Math.max(0, maxLength - 1))}\u2026` : text;\n}\n\nexport function userInputTitle(content: string | ContentBlock[]): string {\n return sessionContentPreview(content, 60);\n}\n", "import * as fsp from 'node:fs/promises';\n\nconst CHUNK_SIZE = 65_536;\n\nexport interface SessionTruncatePlan {\n checkpointByteOffset: number;\n removedCount: number;\n}\n\nexport async function findSessionCheckpointTruncatePlan(\n filePath: string,\n targetPromptIndex: number,\n): Promise<SessionTruncatePlan | null> {\n let fd: fsp.FileHandle | undefined;\n let fileOffset = 0;\n let pendingLine = Buffer.alloc(0);\n let checkpointByteOffset = -1;\n let removedCount = 0;\n let targetCheckpointSeen = false;\n\n try {\n fd = await fsp.open(filePath, 'r', 0o600);\n\n while (true) {\n const buf = Buffer.alloc(CHUNK_SIZE);\n const { bytesRead } = await fd.read(buf, 0, CHUNK_SIZE, fileOffset);\n if (bytesRead === 0) break;\n\n const carriedBytes = pendingLine.length;\n const chunk =\n carriedBytes === 0\n ? buf.subarray(0, bytesRead)\n : Buffer.concat([pendingLine, buf.subarray(0, bytesRead)]);\n const chunkFileOffset = fileOffset - carriedBytes;\n let chunkPos = 0;\n while (chunkPos < chunk.length) {\n const idx = chunk.indexOf('\\n', chunkPos);\n if (idx === -1) break;\n\n const lineStartOffset = chunkFileOffset + chunkPos;\n if (checkpointByteOffset !== -1) {\n removedCount++;\n } else {\n const lineBytes = chunk.subarray(chunkPos, idx);\n // eslint-disable-next-line no-sync\n const line = new TextDecoder('utf-8', { fatal: false }).decode(lineBytes);\n if (line.trim()) {\n try {\n const event = JSON.parse(line) as { type?: string; promptIndex?: number };\n if (event.type === 'checkpoint') {\n if (event.promptIndex === targetPromptIndex) {\n checkpointByteOffset = lineStartOffset;\n targetCheckpointSeen = true;\n } else if (event.promptIndex !== undefined && event.promptIndex > targetPromptIndex) {\n checkpointByteOffset = lineStartOffset;\n }\n } else if (\n targetCheckpointSeen &&\n event.promptIndex !== undefined &&\n event.promptIndex > targetPromptIndex\n ) {\n removedCount++;\n } else if (targetCheckpointSeen && event.promptIndex === undefined) {\n removedCount++;\n } else if (!targetCheckpointSeen && event.promptIndex === undefined) {\n removedCount++;\n } else if (\n !targetCheckpointSeen &&\n event.promptIndex !== undefined &&\n event.promptIndex > targetPromptIndex\n ) {\n removedCount++;\n }\n } catch {\n // Malformed JSON is preserved by the rewrite step.\n }\n }\n }\n\n chunkPos = idx + 1;\n }\n\n pendingLine = chunk.subarray(chunkPos);\n fileOffset += bytesRead;\n }\n } finally {\n await fd?.close();\n }\n\n if (checkpointByteOffset === -1) return null;\n return { checkpointByteOffset, removedCount };\n}\n\nexport async function rewriteSessionToCheckpoint(\n filePath: string,\n checkpointByteOffset: number,\n): Promise<void> {\n const tmpPath = `${filePath}.rewind.tmp`;\n const src = await fsp.open(filePath, 'r', 0o600);\n let srcClosed = false;\n\n const closeSrc = async (): Promise<void> => {\n if (srcClosed) return;\n srcClosed = true;\n await src.close();\n };\n\n try {\n const statResult = await src.stat();\n const totalSize = statResult.size;\n const prefixBytes = checkpointByteOffset;\n let newlineAfterCheckpoint = prefixBytes;\n\n if (prefixBytes < totalSize) {\n const probeBuf = Buffer.alloc(Math.min(CHUNK_SIZE, totalSize - prefixBytes));\n const { bytesRead: probeRead } = await src.read(probeBuf, 0, probeBuf.length, prefixBytes);\n if (probeRead > 0) {\n const nl = probeBuf.indexOf('\\n');\n newlineAfterCheckpoint = nl !== -1 ? prefixBytes + nl + 1 : totalSize;\n }\n } else {\n newlineAfterCheckpoint = totalSize;\n }\n\n const writeFd = await fsp.open(tmpPath, 'w', 0o600);\n try {\n let readOffset = 0;\n while (readOffset < newlineAfterCheckpoint) {\n const toCopy = Math.min(CHUNK_SIZE, newlineAfterCheckpoint - readOffset);\n const copyBuf = Buffer.alloc(toCopy);\n const { bytesRead: r } = await src.read(copyBuf, 0, toCopy, readOffset);\n if (r === 0) break;\n await writeFd.write(copyBuf, 0, r);\n readOffset += r;\n }\n\n let tailOffset = newlineAfterCheckpoint;\n let leftover = '';\n while (tailOffset < totalSize) {\n const toRead = Math.min(CHUNK_SIZE, totalSize - tailOffset);\n const tailBuf = Buffer.alloc(toRead);\n const { bytesRead: tr } = await src.read(tailBuf, 0, toRead, tailOffset);\n if (tr === 0) break;\n const chunk = leftover + tailBuf.subarray(0, tr).toString('utf8');\n const lastNl = chunk.lastIndexOf('\\n');\n if (lastNl === -1) {\n leftover = chunk;\n } else {\n for (const line of chunk.slice(0, lastNl + 1).split('\\n')) {\n if (!line.trim()) continue;\n try {\n JSON.parse(line);\n } catch {\n await writeFd.write(`${line}\\n`, undefined, 'utf8');\n }\n }\n leftover = chunk.slice(lastNl + 1);\n }\n tailOffset += tr;\n }\n\n if (leftover.trim()) {\n try {\n JSON.parse(leftover);\n } catch {\n await writeFd.write(`${leftover}\\n`, undefined, 'utf8');\n }\n }\n\n await writeFd.sync();\n } finally {\n await writeFd.close();\n }\n\n await closeSrc();\n await fsp.rename(tmpPath, filePath);\n } catch (err) {\n await closeSrc().catch(() => undefined);\n await fsp.unlink(tmpPath).catch(() => undefined);\n throw err;\n }\n}\n", "import type { Message } from '../types/messages.js';\nimport type { SecretScrubber } from '../types/secret-scrubber.js';\nimport type { FileSnapshot, SessionEvent } from '../types/session.js';\n\nexport function scrubSessionWriterEvent(\n event: SessionEvent,\n secretScrubber?: SecretScrubber | undefined,\n): SessionEvent {\n const persistMessage = (message: Message): Message => {\n const { _estTokens: _ignored, ...persisted } = message;\n return {\n ...persisted,\n content:\n typeof persisted.content === 'string'\n ? (secretScrubber?.scrub(persisted.content) ?? persisted.content)\n : (secretScrubber?.scrubObject(persisted.content) ?? persisted.content),\n };\n };\n if (event.type === 'context_snapshot' || event.type === 'messages_replaced') {\n return { ...event, messages: event.messages.map(persistMessage) };\n }\n if (event.type === 'message_appended' || event.type === 'message_updated') {\n return { ...event, message: persistMessage(event.message) };\n }\n if (!secretScrubber) return event;\n if (event.type === 'user_input') {\n return {\n ...event,\n content:\n typeof event.content === 'string'\n ? secretScrubber.scrub(event.content)\n : secretScrubber.scrubObject(event.content),\n };\n }\n if (event.type === 'llm_response') {\n return { ...event, content: secretScrubber.scrubObject(event.content) };\n }\n if (event.type === 'file_snapshot') {\n return {\n ...event,\n files: event.files.map((f: FileSnapshot) => ({\n ...f,\n before: f.before !== null ? secretScrubber.scrub(f.before) : null,\n after: f.after !== null ? secretScrubber.scrub(f.after) : null,\n })),\n };\n }\n return event;\n}\n", "import { spawn } from 'node:child_process';\nimport { createHash, randomUUID } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type {\n WorkspaceCheckpointRef,\n WorkspaceMaterializationResult,\n} from '../types/session.js';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { buildChildEnv } from '../utils/child-env.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { mapWithConcurrency } from './storage-concurrency.js';\n\nexport interface CheckpointGitResult {\n code: number;\n stdout: string;\n stderr: string;\n /** Set when the runner discarded output after its safety limit. */\n stdoutTruncated?: boolean | undefined;\n stderrTruncated?: boolean | undefined;\n}\n\nexport interface SessionCheckpointCasOptions {\n rootDir: string;\n projectRoot: string;\n runGit?: ((args: string[], cwd: string) => Promise<CheckpointGitResult>) | undefined;\n}\n\ntype WorkspaceCheckpointEntry =\n | { path: string; state: 'file'; blobHash: string; mode: number }\n | { path: string; state: 'absent' }\n | { path: string; state: 'symlink'; linkTarget: string };\n\ninterface WorkspaceCheckpointManifest {\n version: 1;\n baseHead: string;\n coverage: 'git-head-plus-dirty';\n entries: WorkspaceCheckpointEntry[];\n unresolved: Array<{ path: string; reason: string }>;\n}\n\nconst HASH_RE = /^[a-f\\d]{64}$/;\nconst CAPTURE_CONCURRENCY = 8;\nconst MAX_GIT_OUTPUT = 16 * 1024 * 1024;\nconst MAX_BLOB_BYTES = 64 * 1024 * 1024;\nconst MAX_CHECKPOINT_BYTES = 512 * 1024 * 1024;\n\nfunction sha256(content: string | Uint8Array): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nfunction isInside(root: string, target: string): boolean {\n const relative = path.relative(root, target);\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction normalizeRelative(input: string): string | null {\n if (!input || path.isAbsolute(input)) return null;\n const normalized = input.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n const resolved = path.posix.normalize(normalized);\n if (!resolved || resolved === '.' || resolved === '..' || resolved.startsWith('../')) return null;\n return resolved;\n}\n\nfunction parseNulPaths(output: string): string[] {\n return output\n .split('\\0')\n .map(normalizeRelative)\n .filter((value): value is string => value !== null);\n}\n\nfunction isWrongStackWorktreePath(relative: string): boolean {\n return relative === '.wrongstack/worktrees' || relative.startsWith('.wrongstack/worktrees/');\n}\n\n/**\n * Content-addressed workspace checkpoint store.\n *\n * A manifest references a Git base tree plus CAS blobs for every changed or\n * untracked, non-ignored path. Applying it to a checkout at the same baseHead\n * reproduces the checkpoint without touching the parent working tree.\n */\nexport class SessionCheckpointCas {\n private readonly rootDir: string;\n private readonly projectRoot: string;\n private readonly runGit: (args: string[], cwd: string) => Promise<CheckpointGitResult>;\n\n constructor(opts: SessionCheckpointCasOptions) {\n this.rootDir = path.resolve(opts.rootDir);\n this.projectRoot = path.resolve(opts.projectRoot);\n this.runGit = opts.runGit ?? defaultRunGit;\n }\n\n async capture(_sessionId: string, _promptIndex: number): Promise<WorkspaceCheckpointRef | undefined> {\n const head = await this.runGit(['rev-parse', '--verify', 'HEAD'], this.projectRoot);\n const baseHead = head.stdout.trim();\n if (\n head.code !== 0 ||\n head.stdoutTruncated ||\n !/^[a-f\\d]{40,64}$/i.test(baseHead)\n ) {\n return undefined;\n }\n\n const [tracked, untracked] = await Promise.all([\n this.runGit(['diff', 'HEAD', '--name-only', '-z', '--'], this.projectRoot),\n this.runGit(['ls-files', '--others', '--exclude-standard', '-z'], this.projectRoot),\n ]);\n if (\n tracked.code !== 0 ||\n untracked.code !== 0 ||\n tracked.stdoutTruncated ||\n untracked.stdoutTruncated\n ) {\n return undefined;\n }\n\n const relativePaths = [\n ...new Set([...parseNulPaths(tracked.stdout), ...parseNulPaths(untracked.stdout)]),\n ]\n // Never recursively capture WrongStack's own allocated checkouts, even\n // if a repository accidentally tracks that administrative directory.\n .filter((relative) => !isWrongStackWorktreePath(relative))\n .sort();\n const unresolved: WorkspaceCheckpointManifest['unresolved'] = [];\n let scheduledBytes = 0;\n const captured = await mapWithConcurrency(\n relativePaths,\n CAPTURE_CONCURRENCY,\n async (relative): Promise<WorkspaceCheckpointEntry | null> => {\n const absolute = path.resolve(this.projectRoot, ...relative.split('/'));\n if (!isInside(this.projectRoot, absolute)) {\n unresolved.push({ path: relative, reason: 'path escapes project root' });\n return null;\n }\n try {\n const stat = await fsp.lstat(absolute);\n if (stat.isSymbolicLink()) {\n const linkTarget = await fsp.readlink(absolute);\n const resolvedLink = path.resolve(path.dirname(absolute), linkTarget);\n if (path.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {\n unresolved.push({\n path: relative,\n reason: 'symlink target escapes project root',\n });\n return null;\n }\n return { path: relative, state: 'symlink', linkTarget };\n }\n if (!stat.isFile()) {\n unresolved.push({ path: relative, reason: 'changed path is not a regular file' });\n return null;\n }\n if (stat.size > MAX_BLOB_BYTES) {\n unresolved.push({\n path: relative,\n reason: `file exceeds ${MAX_BLOB_BYTES}-byte checkpoint blob limit`,\n });\n return null;\n }\n if (scheduledBytes + stat.size > MAX_CHECKPOINT_BYTES) {\n unresolved.push({\n path: relative,\n reason: `checkpoint exceeds ${MAX_CHECKPOINT_BYTES}-byte aggregate blob limit`,\n });\n return null;\n }\n scheduledBytes += stat.size;\n const content = await fsp.readFile(absolute);\n const blobHash = sha256(content);\n await this.putBlob(blobHash, content);\n return { path: relative, state: 'file', blobHash, mode: stat.mode & 0o777 };\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return { path: relative, state: 'absent' };\n }\n unresolved.push({ path: relative, reason: toErrorMessage(err) });\n return null;\n }\n },\n );\n\n const capturedAt = new Date().toISOString();\n const manifest: WorkspaceCheckpointManifest = {\n version: 1,\n baseHead: baseHead.toLowerCase(),\n coverage: 'git-head-plus-dirty',\n entries: captured.filter((entry): entry is WorkspaceCheckpointEntry => entry !== null),\n unresolved: [...unresolved].sort((a, b) => a.path.localeCompare(b.path)),\n };\n const encoded = JSON.stringify(manifest);\n const manifestHash = sha256(encoded);\n await this.putManifest(manifestHash, encoded);\n return {\n manifestHash,\n baseHead: manifest.baseHead,\n entryCount: manifest.entries.length,\n unresolvedCount: manifest.unresolved.length,\n capturedAt,\n coverage: manifest.coverage,\n };\n }\n\n async materialize(\n checkpoint: WorkspaceCheckpointRef,\n targetRoot: string,\n ): Promise<WorkspaceMaterializationResult> {\n const target = path.resolve(targetRoot);\n if (target === this.projectRoot) {\n throw new Error('Refusing to materialize a workspace checkpoint over the parent project root');\n }\n const targetStat = await fsp.stat(target);\n if (!targetStat.isDirectory()) throw new Error(`Checkpoint target is not a directory: ${target}`);\n\n const manifest = await this.loadManifest(checkpoint.manifestHash);\n if (\n manifest.baseHead !== checkpoint.baseHead.toLowerCase() ||\n manifest.coverage !== checkpoint.coverage ||\n manifest.entries.length !== checkpoint.entryCount ||\n manifest.unresolved.length !== checkpoint.unresolvedCount\n ) {\n throw new Error('Workspace checkpoint reference does not match its CAS manifest');\n }\n if (manifest.unresolved.length > 0) {\n throw new Error(\n `Workspace checkpoint has ${manifest.unresolved.length} unresolved path(s); exact materialization refused`,\n );\n }\n const targetHead = await this.runGit(['rev-parse', '--verify', 'HEAD'], target);\n if (\n targetHead.code !== 0 ||\n targetHead.stdoutTruncated ||\n targetHead.stdout.trim().toLowerCase() !== manifest.baseHead\n ) {\n throw new Error(\n `Checkpoint target HEAD must equal ${manifest.baseHead}; got ${targetHead.stdout.trim() || 'unknown'}`,\n );\n }\n const targetStatus = await this.runGit(\n ['status', '--porcelain=v1', '-z', '--untracked-files=all', '--ignored=matching'],\n target,\n );\n if (targetStatus.code !== 0 || targetStatus.stdoutTruncated) {\n throw new Error('Unable to verify that the checkpoint target is clean');\n }\n if (targetStatus.stdout.length > 0) {\n throw new Error('Checkpoint target must be a clean checkout before materialization');\n }\n\n const realTarget = await fsp.realpath(target);\n const writtenFiles: string[] = [];\n const deletedFiles: string[] = [];\n const errors: string[] = [];\n const prepared: Array<{\n entry: WorkspaceCheckpointEntry;\n output: string;\n content?: Buffer | undefined;\n }> = [];\n for (const entry of manifest.entries) {\n try {\n const output = await this.safeOutputPath(target, realTarget, entry.path);\n if (entry.state === 'symlink') {\n if (path.isAbsolute(entry.linkTarget)) {\n throw new Error('absolute symlink target refused');\n }\n const resolvedLink = path.resolve(path.dirname(output), entry.linkTarget);\n if (!isInside(target, resolvedLink)) throw new Error('symlink target escapes checkpoint root');\n }\n prepared.push({\n entry,\n output,\n ...(entry.state === 'file' ? { content: await this.readBlob(entry.blobHash) } : {}),\n });\n } catch (err) {\n errors.push(`${entry.path}: ${toErrorMessage(err)}`);\n }\n }\n // Integrity and path validation happen before the first mutation. This is\n // not a cross-file filesystem transaction, but corrupt input cannot leave\n // an otherwise clean target half-applied.\n if (errors.length > 0) {\n return { targetRoot: target, writtenFiles, deletedFiles, errors };\n }\n\n for (const { entry, output, content } of prepared) {\n try {\n if (entry.state === 'absent') {\n await fsp.unlink(output).catch((err) => {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n });\n deletedFiles.push(entry.path);\n continue;\n }\n if (entry.state === 'symlink') {\n await fsp.unlink(output).catch((err) => {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n });\n await fsp.mkdir(path.dirname(output), { recursive: true });\n await fsp.symlink(entry.linkTarget, output);\n writtenFiles.push(entry.path);\n continue;\n }\n if (!content) throw new Error('CAS blob was not prepared');\n await atomicWrite(output, content, { mode: entry.mode });\n await fsp.chmod(output, entry.mode).catch(() => undefined);\n writtenFiles.push(entry.path);\n } catch (err) {\n errors.push(`${entry.path}: ${toErrorMessage(err)}`);\n }\n }\n return { targetRoot: target, writtenFiles, deletedFiles, errors };\n }\n\n private objectPath(hash: string): string {\n if (!HASH_RE.test(hash)) throw new Error(`Invalid CAS object hash: ${hash}`);\n return path.join(this.rootDir, 'objects', hash.slice(0, 2), hash.slice(2));\n }\n\n private manifestPath(hash: string): string {\n if (!HASH_RE.test(hash)) throw new Error(`Invalid checkpoint manifest hash: ${hash}`);\n return path.join(this.rootDir, 'manifests', `${hash}.json`);\n }\n\n private async putBlob(hash: string, content: Buffer): Promise<void> {\n const target = this.objectPath(hash);\n await fsp.mkdir(path.dirname(target), { recursive: true });\n try {\n const existing = await fsp.readFile(target);\n if (sha256(existing) !== hash) throw new Error(`Corrupt CAS object collision: ${hash}`);\n return;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n // Publish only a fully-written object. Creating the final path first would\n // expose a partial blob to another process (or leave one after a crash).\n // A same-directory hard link is an atomic create-if-absent operation.\n const temp = path.join(\n path.dirname(target),\n `.${path.basename(target)}.${process.pid}.${randomUUID()}.tmp`,\n );\n let handle: fsp.FileHandle | undefined;\n try {\n handle = await fsp.open(temp, 'wx', 0o600);\n await handle.writeFile(content);\n await handle.sync();\n await handle.close();\n handle = undefined;\n try {\n await fsp.link(temp, target);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'EEXIST') throw err;\n const existing = await fsp.readFile(target);\n if (sha256(existing) !== hash) throw new Error(`Corrupt CAS object collision: ${hash}`);\n }\n } finally {\n await handle?.close().catch(() => undefined);\n await fsp.unlink(temp).catch(() => undefined);\n }\n }\n\n private async putManifest(hash: string, encoded: string): Promise<void> {\n const target = this.manifestPath(hash);\n const existing = await fsp.readFile(target).catch(() => null);\n if (existing) {\n if (sha256(existing) !== hash) throw new Error(`Corrupt checkpoint manifest collision: ${hash}`);\n return;\n }\n await atomicWrite(target, encoded, { mode: 0o600 });\n }\n\n private async readBlob(hash: string): Promise<Buffer> {\n const content = await fsp.readFile(this.objectPath(hash));\n if (sha256(content) !== hash) throw new Error(`CAS blob integrity check failed: ${hash}`);\n return content;\n }\n\n private async loadManifest(hash: string): Promise<WorkspaceCheckpointManifest> {\n const raw = await fsp.readFile(this.manifestPath(hash));\n if (sha256(raw) !== hash) throw new Error(`Checkpoint manifest integrity check failed: ${hash}`);\n const parsed = JSON.parse(raw.toString('utf8')) as Partial<WorkspaceCheckpointManifest>;\n if (\n parsed.version !== 1 ||\n typeof parsed.baseHead !== 'string' ||\n parsed.coverage !== 'git-head-plus-dirty' ||\n !Array.isArray(parsed.entries) ||\n !Array.isArray(parsed.unresolved)\n ) {\n throw new Error(`Invalid workspace checkpoint manifest: ${hash}`);\n }\n const entriesValid = parsed.entries.every((entry) => {\n if (!entry || typeof entry !== 'object') return false;\n const candidate = entry as Partial<WorkspaceCheckpointEntry>;\n if (typeof candidate.path !== 'string' || !normalizeRelative(candidate.path)) return false;\n if (candidate.state === 'absent') return true;\n if (candidate.state === 'symlink') return typeof candidate.linkTarget === 'string';\n return (\n candidate.state === 'file' &&\n typeof candidate.blobHash === 'string' &&\n HASH_RE.test(candidate.blobHash) &&\n typeof candidate.mode === 'number' &&\n Number.isInteger(candidate.mode) &&\n candidate.mode >= 0 &&\n candidate.mode <= 0o777\n );\n });\n const unresolvedValid = parsed.unresolved.every(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n typeof entry.path === 'string' &&\n typeof entry.reason === 'string',\n );\n if (!entriesValid || !unresolvedValid) {\n throw new Error(`Invalid workspace checkpoint manifest entries: ${hash}`);\n }\n return parsed as WorkspaceCheckpointManifest;\n }\n\n private async safeOutputPath(target: string, realTarget: string, relative: string): Promise<string> {\n const normalized = normalizeRelative(relative);\n if (!normalized) throw new Error('invalid relative path');\n const output = path.resolve(target, ...normalized.split('/'));\n if (!isInside(target, output)) throw new Error('path escapes checkpoint target');\n\n let probe = output;\n for (;;) {\n try {\n const real = await fsp.realpath(probe);\n if (!isInside(realTarget, real)) throw new Error('path resolves through a symlink outside checkpoint target');\n return output;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n const parent = path.dirname(probe);\n if (parent === probe) throw err;\n probe = parent;\n }\n }\n }\n}\n\nfunction defaultRunGit(args: string[], cwd: string): Promise<CheckpointGitResult> {\n return new Promise((resolve) => {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let stdoutBytes = 0;\n let stderrBytes = 0;\n let stdoutTruncated = false;\n let stderrTruncated = false;\n const child = spawn('git', args, {\n cwd,\n env: buildChildEnv(),\n stdio: ['ignore', 'pipe', 'pipe'],\n signal: AbortSignal.timeout(30_000),\n windowsHide: true,\n });\n child.stdout?.on('data', (chunk: Buffer) => {\n const remaining = MAX_GIT_OUTPUT - stdoutBytes;\n if (remaining <= 0) {\n stdoutTruncated = true;\n return;\n }\n const kept = chunk.subarray(0, remaining);\n stdoutChunks.push(kept);\n stdoutBytes += kept.length;\n if (kept.length < chunk.length) stdoutTruncated = true;\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n const remaining = MAX_GIT_OUTPUT - stderrBytes;\n if (remaining <= 0) {\n stderrTruncated = true;\n return;\n }\n const kept = chunk.subarray(0, remaining);\n stderrChunks.push(kept);\n stderrBytes += kept.length;\n if (kept.length < chunk.length) stderrTruncated = true;\n });\n const result = (code: number, extraStderr?: string): CheckpointGitResult => ({\n code,\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: `${Buffer.concat(stderrChunks).toString('utf8')}${extraStderr ?? ''}`,\n stdoutTruncated,\n stderrTruncated,\n });\n child.on('error', (err) => resolve(result(1, err.message)));\n child.on('close', (code) => resolve(result(code ?? 1)));\n });\n}\n", "export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n mapper: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n if (items.length === 0) return [];\n const limit = Math.max(1, Math.floor(concurrency));\n const results = new Array<R>(items.length);\n let next = 0;\n\n async function worker(): Promise<void> {\n while (true) {\n const index = next;\n next++;\n if (index >= items.length) return;\n results[index] = await mapper(items[index] as T, index);\n }\n }\n\n const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());\n await Promise.all(workers);\n return results;\n}\n", "/**\n * Session ID generation \u2014 extracted from session-store.ts.\n *\n * Pure functions: no I/O, no class state, no side effects.\n * Safe to unit-test in isolation.\n */\nimport { ulid } from '../utils/ulid.js';\n\n/**\n * @deprecated Legacy helper kept for callers that still need filename-safe\n * labels. New session ids are opaque and do not include model/provider names.\n */\nexport function sanitizeModel(model: string): string {\n return model\n .replace(/[^a-zA-Z0-9_-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 40);\n}\n\n/**\n * Generate a session ID in the format:\n * `YYYY-MM-DD/sess_<ULID>`\n *\n * Examples:\n * `2026-06-06/sess_01JX2S9V7T5M6N7P8Q9R0STXVW`\n *\n * The date prefix becomes a subdirectory so sessions group naturally by day.\n * The leaf is an opaque sortable id; provider/model names belong in metadata,\n * not file paths. Older IDs that contain model/provider text remain readable.\n */\nexport function generateSessionId(startedAt: string, _model?: string): string {\n const date = startedAt.slice(0, 10); // \"2026-06-06\"\n const seedTime = Number.isNaN(Date.parse(startedAt)) ? Date.now() : Date.parse(startedAt);\n return `${date}/sess_${ulid(seedTime)}`;\n}\n", "import * as path from 'node:path';\n\nexport type SessionIdResolution =\n | { status: 'resolved'; id: string }\n | { status: 'missing'; query: string }\n | { status: 'ambiguous'; query: string; candidates: string[] };\n\n/**\n * Resolve a user-facing session reference without ever guessing.\n *\n * Precedence: exact canonical id \u2192 exact leaf \u2192 unique canonical/leaf prefix.\n * Ambiguous leaf or prefix matches are returned to the caller for an explicit\n * error instead of selecting whichever session happened to sort first.\n */\nexport function resolveSessionId(\n query: string,\n candidateIds: readonly string[],\n): SessionIdResolution {\n const normalized = query.trim();\n if (!normalized) return { status: 'missing', query: normalized };\n\n const uniqueIds = [...new Set(candidateIds)];\n if (uniqueIds.includes(normalized)) return { status: 'resolved', id: normalized };\n\n const leaf = (id: string): string => path.posix.basename(id.replace(/\\\\/g, '/'));\n const exactLeafMatches = uniqueIds.filter((id) => leaf(id) === normalized);\n if (exactLeafMatches.length === 1) {\n return { status: 'resolved', id: exactLeafMatches[0]! };\n }\n if (exactLeafMatches.length > 1) {\n return {\n status: 'ambiguous',\n query: normalized,\n candidates: exactLeafMatches.sort(),\n };\n }\n\n const prefixMatches = uniqueIds.filter(\n (id) => id.startsWith(normalized) || leaf(id).startsWith(normalized),\n );\n if (prefixMatches.length === 1) return { status: 'resolved', id: prefixMatches[0]! };\n if (prefixMatches.length > 1) {\n return { status: 'ambiguous', query: normalized, candidates: prefixMatches.sort() };\n }\n return { status: 'missing', query: normalized };\n}\n\nexport function sessionIdResolutionError(resolution: Exclude<SessionIdResolution, { status: 'resolved' }>): Error {\n if (resolution.status === 'ambiguous') {\n return new Error(\n `Ambiguous session id \"${resolution.query}\". Matches: ${resolution.candidates.join(', ')}`,\n );\n }\n return new Error(`Session not found: ${resolution.query}`);\n}\n", "import type { SecretScrubber } from '../types/secret-scrubber.js';\nimport type { SessionData, SessionEvent, SessionSummary } from '../types/session.js';\nimport { sessionContentText } from './session-helpers.js';\n\n/**\n * Read-side migration boundary for transcripts written before write-time\n * secret scrubbing existed. Scrub the complete object graph before it enters\n * a cache, message projection, search result, export, or surface response.\n */\nexport function scrubPersistedSessionEvent(\n event: SessionEvent,\n scrubber: SecretScrubber,\n): SessionEvent {\n return scrubber.scrubObject(event);\n}\n\nexport function scrubPersistedSessionData(\n data: SessionData,\n scrubber: SecretScrubber,\n): SessionData {\n return scrubber.scrubObject(data);\n}\n\nexport function scrubPersistedSessionSummary(\n summary: SessionSummary,\n scrubber: SecretScrubber,\n): SessionSummary {\n const scrubbed = scrubber.scrubObject(summary);\n const { name: _name, lastUserMessage: _lastUserMessage, ...rest } = scrubbed;\n const title = sessionContentText(scrubbed.title) || '(empty session)';\n const name = scrubbed.name === undefined ? '' : sessionContentText(scrubbed.name);\n const lastUserMessage =\n scrubbed.lastUserMessage === undefined ? '' : sessionContentText(scrubbed.lastUserMessage);\n return {\n ...rest,\n title,\n ...(name ? { name } : {}),\n ...(lastUserMessage ? { lastUserMessage } : {}),\n };\n}\n", "import { createHash } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type {\n ResumeFileValidationEntry,\n ResumeValidation,\n SessionEvent,\n} from '../types/session.js';\nimport { toErrorMessage } from '../utils/index.js';\nimport { mapWithConcurrency } from './storage-concurrency.js';\n\nconst MAX_REVALIDATE_BYTES = 5 * 1024 * 1024;\nconst VALIDATION_CONCURRENCY = 8;\nconst NOTICE_PATH_LIMIT = 20;\n\ninterface FileObservation {\n path: string;\n hash: string;\n ts: string;\n}\n\nfunction isInside(root: string, target: string): boolean {\n const relative = path.relative(root, target);\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction errno(err: unknown): string | undefined {\n return err && typeof err === 'object' && 'code' in err\n ? String((err as NodeJS.ErrnoException).code)\n : undefined;\n}\n\nfunction latestObservations(events: readonly SessionEvent[], projectRoot: string): FileObservation[] {\n const latest = new Map<string, FileObservation>();\n for (const event of events) {\n if (\n event.type !== 'file_observation' ||\n typeof event.path !== 'string' ||\n event.path.length === 0 ||\n typeof event.hash !== 'string' ||\n !/^[a-f\\d]{64}$/i.test(event.hash)\n ) {\n continue;\n }\n const normalized = path.resolve(projectRoot, event.path);\n latest.set(normalized, {\n path: normalized,\n hash: event.hash.toLowerCase(),\n ts: event.ts,\n });\n }\n return [...latest.values()];\n}\n\nasync function validateOne(\n observation: FileObservation,\n lexicalRoot: string,\n realRoot: string,\n): Promise<ResumeFileValidationEntry | null> {\n const base = {\n path: observation.path,\n observedAt: observation.ts,\n expectedHash: observation.hash,\n };\n\n if (!isInside(lexicalRoot, observation.path)) {\n return {\n ...base,\n status: 'outside_project',\n detail: 'Recorded path is outside the active project root.',\n };\n }\n\n let realFile: string;\n try {\n realFile = await fsp.realpath(observation.path);\n } catch (err) {\n if (errno(err) === 'ENOENT') return { ...base, status: 'deleted' };\n return { ...base, status: 'unreadable', detail: toErrorMessage(err) };\n }\n\n if (!isInside(realRoot, realFile)) {\n return {\n ...base,\n status: 'outside_project',\n detail: 'Recorded path resolves through a symlink outside the active project root.',\n };\n }\n\n try {\n const stat = await fsp.stat(realFile);\n if (!stat.isFile()) {\n return { ...base, status: 'unreadable', detail: 'Path is no longer a regular file.' };\n }\n if (stat.size > MAX_REVALIDATE_BYTES) {\n return {\n ...base,\n status: 'unreadable',\n detail: `File now exceeds the ${MAX_REVALIDATE_BYTES}-byte validation limit.`,\n };\n }\n // File tools persist sha256(UTF-8 text), so validation deliberately uses\n // the same decoding/hash semantics rather than hashing raw bytes.\n const content = await fsp.readFile(realFile, 'utf8');\n const actualHash = createHash('sha256').update(content, 'utf8').digest('hex');\n if (actualHash === observation.hash) return null;\n return { ...base, status: 'modified', actualHash };\n } catch (err) {\n if (errno(err) === 'ENOENT') return { ...base, status: 'deleted' };\n return { ...base, status: 'unreadable', detail: toErrorMessage(err) };\n }\n}\n\n/** Revalidate the latest persisted hash for every distinct observed path. */\nexport async function validateResumeFileObservations(\n events: readonly SessionEvent[],\n projectRoot: string,\n): Promise<ResumeValidation> {\n const lexicalRoot = path.resolve(projectRoot);\n const realRoot = await fsp.realpath(lexicalRoot).catch(() => lexicalRoot);\n const observations = latestObservations(events, lexicalRoot);\n const results = await mapWithConcurrency(\n observations,\n VALIDATION_CONCURRENCY,\n (observation) => validateOne(observation, lexicalRoot, realRoot),\n );\n return {\n checkedAt: new Date().toISOString(),\n checkedFileCount: observations.length,\n staleFiles: results.filter((entry): entry is ResumeFileValidationEntry => entry !== null),\n };\n}\n\n/** Build the ephemeral system message injected into the first resumed turn. */\nexport function formatResumeValidationNotice(\n validation: ResumeValidation,\n projectRoot: string,\n): string | null {\n if (validation.staleFiles.length === 0) return null;\n const root = path.resolve(projectRoot);\n const shown = validation.staleFiles.slice(0, NOTICE_PATH_LIMIT).map((entry) => {\n const relative = path.relative(root, entry.path);\n const display = isInside(root, entry.path) ? relative || '.' : entry.path;\n return `- ${JSON.stringify(display)} [${entry.status}]`;\n });\n const omitted = validation.staleFiles.length - shown.length;\n if (omitted > 0) shown.push(`- ... and ${omitted} more stale path(s)`);\n\n return [\n '[SESSION RESUME FILE VALIDATION]',\n `Journal hash validation found ${validation.staleFiles.length} previously observed file(s) whose current state cannot be trusted.`,\n 'Prior tool results and reasoning based on these paths are stale. Re-read each relevant file before relying on it or editing it.',\n ...shown,\n ].join('\\n');\n}\n\n/**\n * Build the ephemeral system message injected when the resumed session had\n * tool calls still in flight (the previous run crashed or was interrupted\n * before their results were recorded). The interrupted `tool_use` blocks are\n * stripped from the reconstructed conversation by adjacency repair and are NOT\n * re-executed on resume \u2014 this notice simply makes the interruption visible so\n * the model can decide whether to retry the work. Returns null when nothing\n * was in flight.\n */\nexport function formatInterruptedToolNotice(pendingToolUseCount: number): string | null {\n if (!Number.isFinite(pendingToolUseCount) || pendingToolUseCount <= 0) return null;\n const plural = pendingToolUseCount === 1 ? 'tool call was' : 'tool calls were';\n return [\n '[SESSION RESUME INTERRUPTED WORK]',\n `The previous run left ${pendingToolUseCount} ${plural} in flight \u2014 no result was recorded before it stopped (crash or interrupt).`,\n 'Those interrupted tool calls were removed from the restored conversation and were NOT re-executed. Re-run the work if it still needs doing.',\n ].join('\\n');\n}\n", "import * as fsp from 'node:fs/promises';\nimport type { SessionSummary } from '../../types/session.js';\n\ntype SessionIndexEntry = {\n action?: string | undefined;\n id?: string | undefined;\n} & SessionSummary;\n\nexport function applySessionIndexLines(\n raw: string,\n byId: Map<string, SessionSummary>,\n deleted: Set<string>,\n): void {\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line) as SessionIndexEntry;\n if (entry.action === 'delete' && entry.id) {\n deleted.add(entry.id);\n byId.delete(entry.id);\n continue;\n }\n if (entry.id && !deleted.has(entry.id)) {\n // Keep the latest entry for each session (multiple appends on resume).\n byId.set(entry.id, entry as SessionSummary);\n }\n } catch {\n // Skip corrupt lines. A later valid line remains usable.\n }\n }\n}\n\nexport async function readFileRange(\n file: string,\n start: number,\n end: number,\n): Promise<{ raw: string; end: number } | null> {\n const length = end - start;\n if (length <= 0) return { raw: '', end: start };\n const buffer = Buffer.allocUnsafe(length);\n let offset = 0;\n let handle: fsp.FileHandle | undefined;\n try {\n handle = await fsp.open(file, 'r');\n while (offset < length) {\n const { bytesRead } = await handle.read(buffer, offset, length - offset, start + offset);\n if (bytesRead === 0) return null;\n offset += bytesRead;\n }\n let completeLength = buffer.length;\n if (buffer.at(-1) !== 0x0a) {\n const lastNewline = buffer.lastIndexOf(0x0a);\n const trailing = buffer\n .subarray(lastNewline + 1)\n .toString('utf8')\n .trim();\n try {\n if (trailing) JSON.parse(trailing);\n else completeLength = lastNewline + 1;\n } catch {\n // A reader can observe an append between the kernel writes that make\n // up a large line. Keep the incomplete suffix unread so the next\n // stat/growth check retries it from the last complete newline.\n completeLength = lastNewline + 1;\n }\n }\n return {\n raw: buffer.subarray(0, completeLength).toString('utf8'),\n end: start + completeLength,\n };\n } catch {\n return null;\n } finally {\n await handle?.close().catch(() => undefined);\n }\n}\n", "export function shouldSkipSessionDirectoryEntry(name: string): boolean {\n return (\n (name.startsWith('.') && name !== '.wrongstack') ||\n name === 'shared' ||\n name === 'subagents' ||\n name === 'attachments'\n );\n}\n\nexport function isSessionJsonlFileName(name: string): boolean {\n return name.endsWith('.jsonl') && name !== '_index.jsonl';\n}\n", "import type { Dirent } from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { SessionFileRef } from './types.js';\nimport { isSessionJsonlFileName, shouldSkipSessionDirectoryEntry } from './directory-scan.js';\n\nfunction sessionIdForFile(prefix: string, name: string): string {\n const base = name.replace(/\\.jsonl$/, '');\n return prefix ? `${prefix}/${base}` : base;\n}\n\nfunction childPrefixFor(entry: Dirent, prefix: string, depth: number): string {\n return depth === 0 ? entry.name : `${prefix}/${entry.name}`;\n}\n\nexport async function collectSessionFiles(\n dir: string,\n prefix = '',\n depth = 0,\n): Promise<SessionFileRef[]> {\n let entries: Dirent[];\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const dirEntries: Dirent[] = [];\n const files: SessionFileRef[] = [];\n for (const entry of entries) {\n if (shouldSkipSessionDirectoryEntry(entry.name)) continue;\n if (entry.isDirectory()) {\n dirEntries.push(entry);\n } else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {\n files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path.join(dir, entry.name) });\n }\n }\n\n const childFileArrays = await Promise.all(\n dirEntries.map((entry) =>\n collectSessionFiles(path.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1),\n ),\n );\n\n return [...childFileArrays.flat(), ...files];\n}\n\nexport async function collectSessionIds(dir: string, prefix = '', depth = 0): Promise<string[]> {\n let entries: Dirent[];\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const dirEntries: Dirent[] = [];\n const fileIds: string[] = [];\n for (const entry of entries) {\n if (shouldSkipSessionDirectoryEntry(entry.name)) continue;\n if (entry.isDirectory()) {\n dirEntries.push(entry);\n } else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {\n fileIds.push(sessionIdForFile(prefix, entry.name));\n }\n }\n\n const childIdArrays = await Promise.all(\n dirEntries.map((entry) =>\n collectSessionIds(path.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1),\n ),\n );\n\n return [...childIdArrays.flat(), ...fileIds];\n}\n", "import * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { toErrorMessage } from '../../utils/index.js';\nimport { sessionPath as sessionStorePath } from './paths.js';\n\nexport type DeleteSessionArtifactsOptions = {\n rootDir: string;\n id: string;\n jsonlPath: string;\n};\n\nexport async function deleteSessionArtifacts({\n rootDir,\n id,\n jsonlPath,\n}: DeleteSessionArtifactsOptions): Promise<void> {\n const shardDir = path.dirname(jsonlPath);\n const base = path.basename(id);\n const sessDir = path.join(shardDir, base);\n\n const deletions: Array<Promise<void>> = [\n fsp.unlink(jsonlPath),\n fsp.unlink(sessionStorePath(rootDir, id, '.summary.json')),\n fsp.unlink(sessionStorePath(rootDir, id, '.plan.json')),\n fsp.unlink(sessionStorePath(rootDir, id, '.tasks.json')),\n fsp.unlink(sessionStorePath(rootDir, id, '.todos.json')),\n ];\n\n const results = await Promise.allSettled(deletions);\n for (const result of results) {\n if (result.status === 'rejected') {\n warnDeleteFailure(id, result.reason);\n }\n }\n\n await fsp.rm(sessDir, { recursive: true, force: true }).catch((err) => {\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'session_store.rmdir_failed',\n sessionId: id,\n message: toErrorMessage(err),\n timestamp: new Date().toISOString(),\n }),\n );\n });\n}\n\nfunction warnDeleteFailure(id: string, reason: unknown): void {\n const msg = reason instanceof Error ? reason.message : String(reason);\n if ((reason as NodeJS.ErrnoException)?.code === 'ENOENT') return;\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'session_store.delete_failed',\n sessionId: id,\n message: msg,\n timestamp: new Date().toISOString(),\n }),\n );\n}\n", "import * as path from 'node:path';\nimport { ensureDir } from '../../utils/atomic-write.js';\nimport { sessionScopedPath } from '../../utils/session-scoped-path.js';\n\nexport function sessionPath(storeDir: string, id: string, ext: string): string {\n return sessionScopedPath(storeDir, id, ext);\n}\n\nexport function shardManifestPath(storeDir: string, shardKey: string): string {\n return shardKey\n ? path.join(storeDir, shardKey, '_manifest.json')\n : path.join(storeDir, '_manifest.json');\n}\n\nexport function shardKeyForSessionId(id: string): string {\n const dirName = path.dirname(id);\n return dirName === '.' ? '' : dirName;\n}\n\nexport async function ensureShardDir(storeDir: string, id: string): Promise<string> {\n const dirPath = path.dirname(sessionScopedPath(storeDir, id, ''));\n await ensureDir(dirPath);\n return dirPath;\n}\n", "export async function assertSessionCanBeDeleted(\n sessionId: string,\n isSessionInUse?: ((sessionId: string) => Promise<string | null>) | undefined,\n): Promise<void> {\n if (!isSessionInUse) return;\n const reason = await isSessionInUse(sessionId);\n if (reason) {\n throw new Error(`Session ${sessionId} is in use (${reason}) and cannot be deleted.`);\n }\n}\n", "import type { SessionData } from '../../types/session.js';\nimport type { LoadCacheEntry } from './types.js';\n\nconst LOAD_CACHE_MAX_ENTRIES = 50;\nconst LOAD_CACHE_MAX_BYTES = 64 * 1024 * 1024;\n\ninterface FileStatSnapshot {\n mtimeMs: number;\n size: number;\n}\n\nexport class SessionLoadCache {\n constructor(private readonly entries = new Map<string, LoadCacheEntry>()) {}\n\n private bytes = 0;\n\n clear(sessionId?: string): void {\n if (sessionId !== undefined) {\n this.delete(sessionId);\n return;\n }\n this.entries.clear();\n this.bytes = 0;\n }\n\n getFresh(id: string, stat: FileStatSnapshot, full: boolean): SessionData | null {\n const cached = this.entries.get(id);\n if (!cached || cached.mtimeMs !== stat.mtimeMs || cached.size !== stat.size) {\n return null;\n }\n this.entries.delete(id);\n this.entries.set(id, cached);\n if (full) return cached.data;\n return { ...cached.data, messages: [] };\n }\n\n set(id: string, stat: FileStatSnapshot, data: SessionData): void {\n this.delete(id);\n while (\n stat.size <= LOAD_CACHE_MAX_BYTES &&\n (this.entries.size >= LOAD_CACHE_MAX_ENTRIES || this.bytes + stat.size > LOAD_CACHE_MAX_BYTES)\n ) {\n const oldest = this.entries.keys().next().value;\n if (oldest === undefined) break;\n this.delete(oldest);\n }\n if (stat.size <= LOAD_CACHE_MAX_BYTES) {\n this.entries.set(id, { mtimeMs: stat.mtimeMs, size: stat.size, data });\n this.bytes += stat.size;\n }\n }\n\n private delete(sessionId: string): void {\n const cached = this.entries.get(sessionId);\n if (cached) this.bytes = Math.max(0, this.bytes - cached.size);\n this.entries.delete(sessionId);\n }\n}\n", "import * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\n\nexport function isPrunableSessionJsonl(name: string): boolean {\n return (\n name.endsWith('.jsonl') &&\n name !== '_index.jsonl' &&\n name !== '_mailbox.jsonl' &&\n !name.endsWith('.replay.jsonl') &&\n !name.endsWith('.audit.jsonl')\n );\n}\n\nexport async function pruneSessionFiles(\n storeDir: string,\n maxAgeDays: number,\n deleteSession: (id: string) => Promise<void>,\n): Promise<number> {\n const cutoff = Date.now() - maxAgeDays * 86_400_000;\n let deleted = 0;\n\n const pruneFile = async (dir: string, name: string, prefix: string): Promise<void> => {\n const jsonlPath = path.join(dir, name);\n try {\n const stat = await fsp.stat(jsonlPath);\n if (stat.mtimeMs >= cutoff) return;\n /* v8 ignore start -- defensive: file vanished between readdir and stat */\n } catch {\n return;\n }\n /* v8 ignore stop */\n const base = name.replace(/\\.jsonl$/, '');\n const id = prefix ? `${prefix}/${base}` : base;\n await deleteSession(id);\n deleted++;\n };\n\n /* v8 ignore next -- defensive: store dir is ensured before prune runs */\n const entries = await fsp.readdir(storeDir, { withFileTypes: true }).catch(() => []);\n for (const entry of entries) {\n if (entry.isFile()) {\n if (isPrunableSessionJsonl(entry.name)) await pruneFile(storeDir, entry.name, '');\n continue;\n }\n /* v8 ignore next -- defensive: root entries are only files or directories */\n if (!entry.isDirectory()) continue;\n const dateDir = path.join(storeDir, entry.name);\n /* v8 ignore next -- defensive: dateDir came from readdir and is readable */\n const files = await fsp.readdir(dateDir, { withFileTypes: true }).catch(() => []);\n for (const file of files) {\n if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;\n await pruneFile(dateDir, file.name, entry.name);\n }\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const dateDir = path.join(storeDir, entry.name);\n try {\n const remaining = await fsp.readdir(dateDir);\n if (remaining.length === 0) {\n /* v8 ignore next -- best-effort: rmdir of a confirmed-empty dir does not reject */\n await fsp.rmdir(dateDir).catch(() => undefined);\n }\n } catch {\n // best-effort\n }\n }\n\n return deleted;\n}\n", "import type { EventBus } from '../../kernel/events.js';\n\nexport type SessionStoreReadOperation =\n | 'load'\n | 'load_events_only'\n | 'list'\n | 'summary'\n | 'index_read';\n\nexport type SessionStoreWriteOperation =\n | 'create'\n | 'resume'\n | 'rename'\n | 'append'\n | 'flush'\n | 'close'\n | 'index_append'\n | 'compact'\n | 'checkpoint';\n\nexport type SessionStoreOutcome = 'success' | 'failure';\n\nexport function emitSessionStoreRead(\n events: EventBus | undefined,\n sessionId: string,\n filePath: string,\n operation: SessionStoreReadOperation,\n outcome: SessionStoreOutcome,\n durationMs: number,\n error?: string,\n): void {\n events?.emit('storage.read', {\n sessionId,\n store: 'session',\n filePath,\n operation,\n outcome,\n durationMs,\n ...(error !== undefined ? { error } : {}),\n });\n}\n\nexport function emitSessionStoreWrite(\n events: EventBus | undefined,\n sessionId: string,\n filePath: string,\n operation: SessionStoreWriteOperation,\n outcome: SessionStoreOutcome,\n durationMs: number,\n eventCount?: number,\n error?: string,\n): void {\n events?.emit('storage.write', {\n sessionId,\n store: 'session',\n filePath,\n operation,\n outcome,\n durationMs,\n ...(eventCount !== undefined ? { eventCount } : {}),\n ...(error !== undefined ? { error } : {}),\n });\n}\n\nexport function emitSessionStoreError(\n events: EventBus | undefined,\n sessionId: string,\n filePath: string,\n operation: string,\n error: string,\n recoverable: boolean,\n): void {\n events?.emit('storage.error', {\n sessionId,\n store: 'session',\n filePath,\n operation,\n error,\n recoverable,\n });\n}\n", "import { createHash } from 'node:crypto';\nimport type {\n ForkedSession,\n SessionData,\n SessionEvent,\n SessionForkOptions,\n SessionMetadata,\n SessionWriter,\n} from '../../types/session.js';\nimport { inheritsIntoFork } from './replay.js';\n\nexport interface SessionForkHost {\n load(id: string): Promise<SessionData>;\n create(meta: Omit<SessionMetadata, 'startedAt'>): Promise<SessionWriter>;\n delete(id: string): Promise<void>;\n}\n\nexport async function forkSession(\n host: SessionForkHost,\n id: string,\n opts: SessionForkOptions = {},\n): Promise<ForkedSession> {\n const parent = await host.load(id);\n let boundary = parent.events.length - 1;\n let targetCheckpoint: Extract<SessionEvent, { type: 'checkpoint' }> | undefined;\n if (opts.checkpointPromptIndex !== undefined) {\n boundary = -1;\n for (let i = 0; i < parent.events.length; i++) {\n const event = parent.events[i];\n if (event?.type === 'checkpoint' && event.promptIndex === opts.checkpointPromptIndex) {\n // Prefer the latest matching checkpoint if a legacy/non-truncated\n // journal reused prompt indices after a rewind.\n boundary = i;\n targetCheckpoint = event;\n }\n }\n if (boundary === -1) {\n throw new Error(`Checkpoint ${opts.checkpointPromptIndex} not found in session \"${id}\"`);\n }\n }\n\n const parentPrefix = parent.events.slice(0, boundary + 1);\n const workspaceCheckpoint = targetCheckpoint?.workspaceCheckpoint;\n const checkpointHash = createHash('sha256')\n .update(parentPrefix.map((event) => JSON.stringify(event)).join('\\n') + '\\n', 'utf8')\n .digest('hex');\n const inherited = parentPrefix.filter(inheritsIntoFork);\n const writer = await host.create({\n id: '',\n title: parent.metadata.title,\n model: parent.metadata.model,\n provider: parent.metadata.provider,\n });\n\n try {\n await writer.append({\n type: 'session_forked',\n ts: new Date().toISOString(),\n parentSessionId: id,\n parentCheckpointPromptIndex: opts.checkpointPromptIndex,\n parentCheckpointHash: checkpointHash,\n workspace: 'shared-current',\n workspaceCheckpointHash: workspaceCheckpoint?.manifestHash,\n });\n const batchSize = 250;\n for (let offset = 0; offset < inherited.length; offset += batchSize) {\n await writer.appendBatch(inherited.slice(offset, offset + batchSize));\n }\n await writer.flush();\n await writer.close();\n const data = await host.load(writer.id);\n return {\n id: writer.id,\n data,\n parentSessionId: id,\n checkpointPromptIndex: opts.checkpointPromptIndex,\n checkpointHash,\n workspace: 'shared-current',\n workspaceCheckpoint,\n };\n } catch (err) {\n await writer.close().catch(() => undefined);\n await host.delete(writer.id).catch(() => undefined);\n throw err;\n }\n}\n", "import type { Message } from '../../types/messages.js';\nimport type { SessionEvent } from '../../types/session.js';\n\nexport function isReplayableMessage(value: unknown): value is Message {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<Message>;\n return (\n (candidate.role === 'user' || candidate.role === 'assistant' || candidate.role === 'system') &&\n (typeof candidate.content === 'string' || Array.isArray(candidate.content))\n );\n}\n\nexport function trackMessageToolState(message: Message, openToolUses: Set<string>): void {\n if (!Array.isArray(message.content)) return;\n for (const block of message.content) {\n if (block.type === 'tool_use') openToolUses.add(block.id);\n else if (block.type === 'tool_result') openToolUses.delete(block.tool_use_id);\n }\n}\n\nexport function replayableMessage(value: unknown, fallbackTs?: string): Message | null {\n if (!isReplayableMessage(value)) return null;\n const { _estTokens: _ignored, ...message } = value;\n return message.ts === undefined && fallbackTs ? { ...message, ts: fallbackTs } : message;\n}\n\nexport function applyContextSnapshot(\n target: Message[],\n openToolUses: Set<string>,\n snapshot: unknown,\n): boolean {\n if (!Array.isArray(snapshot) || !snapshot.every(isReplayableMessage)) return false;\n target.length = 0;\n openToolUses.clear();\n for (const raw of snapshot) {\n const message = replayableMessage(raw);\n if (!message) return false;\n target.push(message);\n trackMessageToolState(message, openToolUses);\n }\n return true;\n}\n\nexport function inheritsIntoFork(event: SessionEvent): boolean {\n switch (event.type) {\n case 'session_start':\n case 'session_resumed':\n case 'session_forked':\n case 'session_end':\n case 'in_flight_start':\n case 'in_flight_end':\n case 'rewound':\n return false;\n // Parent snapshots describe mutations owned by the parent journal. A\n // child that shares the current workspace must not inherit authority to\n // rewind those historical side effects.\n case 'file_snapshot':\n return false;\n default:\n return true;\n }\n}\n", "import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { EventBus } from '../../kernel/events.js';\nimport type { ContentBlock } from '../../types/blocks.js';\nimport type { Message } from '../../types/messages.js';\nimport type { SecretScrubber } from '../../types/secret-scrubber.js';\nimport type { SessionData, SessionEvent, SessionMetadata } from '../../types/session.js';\nimport { repairToolUseAdjacency } from '../../utils/message-invariants.js';\nimport {\n applyContextSnapshot,\n replayableMessage,\n trackMessageToolState,\n} from './replay.js';\nimport { scrubPersistedSessionEvent } from '../session-read-scrubber.js';\nimport { extractToolCallEnds } from '../session-tool-call-ends.js';\n\ntype UsageTotals = { input: number; output: number; cacheRead: number; cacheWrite: number };\n\n/**\n * Budget for the raw `events` array, in source bytes.\n *\n * `events` used to grow with the file, without limit. Measured, that is a\n * 1.88x heap multiplier over the JSONL \u2014 a 564 MB session (15 such files\n * exist in one real project) becomes ~1.03 GB of live objects for this array\n * alone, before `messages` and before parse garbage. Loading one was an OOM,\n * not a slow load.\n *\n * The budget keeps the newest events and drops the oldest, because everything\n * downstream of `events` is tail-oriented: the summary counts, the resume\n * file-observation check, and the transcript all care about recent history.\n * `messages` is unaffected \u2014 it is replayed incrementally as lines arrive and\n * carries its own retention.\n *\n * Sized so it never engages for ordinary sessions: 96 MB of JSONL is above\n * all but a handful of sessions ever recorded, so the common path keeps\n * byte-for-byte the behaviour it had.\n */\nexport const DEFAULT_MAX_RETAINED_EVENT_BYTES = 96 * 1024 * 1024;\n/** Evict down to this fraction of the budget so eviction is amortized, not per-line. */\nconst EVICT_TO_FRACTION = 0.9;\n/** What a snapshot event still costs once `stripSnapshotPayload` empties it. */\nconst SNAPSHOT_ENVELOPE_BYTES = 256;\n\nexport async function loadSessionDataFromFile(params: {\n id: string;\n file: string;\n full: boolean;\n events?: EventBus | undefined;\n secretScrubber: SecretScrubber;\n maxRetainedEventBytes?: number | undefined;\n}): Promise<SessionData> {\n const events: SessionEvent[] = [];\n const eventBytes: number[] = [];\n const eventBudget = params.maxRetainedEventBytes ?? DEFAULT_MAX_RETAINED_EVENT_BYTES;\n const evictTo = Math.floor(eventBudget * EVICT_TO_FRACTION);\n let retainedBytes = 0;\n let eventsDropped = 0;\n /** Absolute index (across evictions) of the newest snapshot still in `events`. */\n let lastSnapshotAbsIndex = -1;\n let sessionStartEvent: SessionEvent | undefined;\n let sessionEndEvent: SessionEvent | undefined;\n let sessionModel: string | undefined;\n let sessionProvider: string | undefined;\n let sessionPendingToolUses: string[] | undefined;\n let sessionForkedEvent: Extract<SessionEvent, { type: 'session_forked' }> | undefined;\n const messages: Message[] | undefined = params.full ? [] : undefined;\n const openToolUses: Set<string> | undefined = params.full ? new Set<string>() : undefined;\n let exactJournalActive = false;\n let usage: UsageTotals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n /** Newest snapshot seen so far; its predecessor is stripped when it arrives. */\n let lastSnapshot: SnapshotEvent | undefined;\n\n const stream = createReadStream(params.file, { encoding: 'utf8' });\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n\n try {\n for await (const line of rl) {\n if (!line.trim()) continue;\n try {\n const parsed: unknown = JSON.parse(line);\n if (!isSessionEventLike(parsed)) continue;\n const ev = scrubPersistedSessionEvent(parsed as SessionEvent, params.secretScrubber);\n // A snapshot event carries a full copy of the conversation, and one is\n // written on every compaction / context rewrite. Replay consumes the\n // copy immediately (see applyContextSnapshot) and nothing downstream\n // reads it back \u2014 `replaySessionMessages`, `buildRestoredCheckpoints`,\n // `buildTranscriptFromEvents` and `extractToolCallEnds` all key off\n // other event types. Retaining every copy therefore kept one whole\n // conversation per compaction alive for the caller's lifetime:\n // measured at 495 MB of a 1.2 GB load on a 564 MB session.\n //\n // Keep only the newest snapshot's payload \u2014 it is the one that decides\n // the final state, so it stays available for anything that later wants\n // to inspect it \u2014 and strip the ones it superseded. Retention becomes\n // O(1) snapshots instead of O(session length).\n events.push(ev);\n eventBytes.push(line.length);\n retainedBytes += line.length;\n if (isSnapshotEvent(ev)) {\n if (lastSnapshot !== undefined) {\n stripSnapshotPayload(lastSnapshot);\n // Refund the stripped payload to the retention budget. Without\n // this the budget would be charged for bytes that are no longer\n // resident, and on a snapshot-heavy session (where source bytes\n // exceed live bytes by an order of magnitude) it would evict\n // events that cost almost nothing to keep.\n const slot = lastSnapshotAbsIndex - eventsDropped;\n if (slot >= 0 && slot < eventBytes.length) {\n const refund = eventBytes[slot]! - SNAPSHOT_ENVELOPE_BYTES;\n if (refund > 0) {\n retainedBytes -= refund;\n eventBytes[slot] = SNAPSHOT_ENVELOPE_BYTES;\n }\n }\n }\n lastSnapshot = ev;\n lastSnapshotAbsIndex = eventsDropped + events.length - 1;\n }\n if (retainedBytes > eventBudget) {\n // Drop from the front in one batch down to `evictTo` \u2014 a per-line\n // `shift()` here would be O(n) on every subsequent line.\n let cut = 0;\n while (cut < eventBytes.length && retainedBytes > evictTo) {\n retainedBytes -= eventBytes[cut]!;\n cut++;\n }\n events.splice(0, cut);\n eventBytes.splice(0, cut);\n eventsDropped += cut;\n }\n\n if (ev.type === 'session_start' && !sessionStartEvent) {\n sessionStartEvent = ev;\n sessionModel = ev.model;\n sessionProvider = ev.provider;\n }\n if (ev.type === 'session_end') {\n sessionEndEvent = ev;\n sessionPendingToolUses = ev.pendingToolUses;\n }\n if (ev.type === 'session_forked' && !sessionForkedEvent) {\n sessionForkedEvent = ev;\n }\n\n if (params.full && messages !== undefined && openToolUses !== undefined) {\n const replayState = replaySessionEvent({\n ev,\n id: params.id,\n events: params.events,\n messages,\n openToolUses,\n exactJournalActive,\n usage,\n });\n exactJournalActive = replayState.exactJournalActive;\n usage = replayState.usage ?? usage;\n } else if (ev.type === 'llm_response') {\n usage = accumulateUsage(usage, ev);\n }\n } catch {\n // skip malformed JSON\n }\n }\n } finally {\n rl.close();\n stream.close();\n }\n\n let finalMessages: Message[] = [];\n if (params.full && messages !== undefined && openToolUses !== undefined) {\n if (openToolUses.size > 0) {\n params.events?.emit('session.damaged', {\n sessionId: params.id,\n detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`,\n });\n }\n const repaired = repairToolUseAdjacency(messages);\n if (repaired.report.changed) {\n params.events?.emit('session.damaged', {\n sessionId: params.id,\n detail:\n `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ` +\n `${repaired.report.removedToolResults.length} tool_result, ` +\n `${repaired.report.removedMessages} empty messages`,\n });\n }\n finalMessages = repaired.messages;\n }\n\n const meta: SessionMetadata = {\n id: params.id,\n startedAt: sessionStartEvent?.ts ?? new Date(0).toISOString(),\n endedAt: sessionEndEvent?.ts,\n model: sessionModel,\n provider: sessionProvider,\n pendingToolUses: sessionPendingToolUses,\n forkedFrom: sessionForkedEvent\n ? {\n sessionId: sessionForkedEvent.parentSessionId,\n checkpointPromptIndex: sessionForkedEvent.parentCheckpointPromptIndex,\n checkpointHash: sessionForkedEvent.parentCheckpointHash,\n workspace: sessionForkedEvent.workspace,\n workspaceCheckpointHash: sessionForkedEvent.workspaceCheckpointHash,\n }\n : undefined,\n };\n\n const toolCallEnds = extractToolCallEnds(events);\n const pendingToolUseCount =\n openToolUses && openToolUses.size > 0 ? openToolUses.size : undefined;\n return {\n metadata: meta,\n events,\n messages: finalMessages,\n usage,\n toolCallEnds,\n ...(pendingToolUseCount !== undefined ? { pendingToolUseCount } : {}),\n ...(eventsDropped > 0 ? { eventsDropped } : {}),\n };\n}\n\n/** Events whose payload is a full copy of the conversation. */\ntype SnapshotEvent = Extract<SessionEvent, { type: 'messages_replaced' | 'context_snapshot' }>;\n\nfunction isSnapshotEvent(event: SessionEvent): event is SnapshotEvent {\n return event.type === 'messages_replaced' || event.type === 'context_snapshot';\n}\n\n/**\n * Drop a superseded snapshot's conversation copy, recording how long it was.\n *\n * Safe to mutate in place: `scrubPersistedSessionEvent` hands back a fresh\n * object per line, so the loader owns it, and by the time a snapshot is\n * superseded its own replay has already consumed the payload.\n */\nfunction stripSnapshotPayload(event: SnapshotEvent): void {\n if (event.messages.length === 0) return;\n event.messagesOmitted = event.messages.length;\n event.messages = [];\n}\n\nfunction isSessionEventLike(value: unknown): value is SessionEvent {\n return (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as { type?: unknown | undefined }).type === 'string' &&\n typeof (value as { ts?: unknown | undefined }).ts === 'string'\n );\n}\n\nfunction replaySessionEvent(params: {\n ev: SessionEvent;\n id: string;\n events?: EventBus | undefined;\n messages: Message[];\n openToolUses: Set<string>;\n exactJournalActive: boolean;\n usage: UsageTotals;\n}): { exactJournalActive: boolean; usage?: UsageTotals } {\n const { ev, messages, openToolUses } = params;\n let exactJournalActive = params.exactJournalActive;\n\n if (ev.type === 'message_appended' && ev.version === 1) {\n const message = replayableMessage(ev.message, ev.ts);\n if (message) {\n if (!exactJournalActive) {\n messages.length = 0;\n openToolUses.clear();\n exactJournalActive = true;\n }\n messages.push(message);\n trackMessageToolState(message, openToolUses);\n } else {\n emitDamaged(params, 'Ignored malformed message_appended event');\n }\n } else if (ev.type === 'message_updated' && ev.version === 1) {\n const message = replayableMessage(ev.message, ev.ts);\n if (message && exactJournalActive && ev.index >= 0 && ev.index < messages.length) {\n messages[ev.index] = message;\n openToolUses.clear();\n for (const current of messages) trackMessageToolState(current, openToolUses);\n } else {\n emitDamaged(params, `Ignored malformed message_updated event at index ${ev.index}`);\n }\n } else if (ev.type === 'messages_replaced' && ev.version === 1) {\n if (applyContextSnapshot(messages, openToolUses, ev.messages)) {\n exactJournalActive = true;\n } else {\n emitDamaged(params, 'Ignored malformed messages_replaced event');\n }\n } else if (ev.type === 'context_snapshot') {\n if (!applyContextSnapshot(messages, openToolUses, ev.messages)) {\n emitDamaged(params, 'Ignored malformed context_snapshot event');\n }\n } else if (!exactJournalActive && ev.type === 'user_input') {\n openToolUses.clear();\n messages.push({ role: 'user', content: ev.content, ts: ev.ts });\n } else if (ev.type === 'llm_response') {\n if (!exactJournalActive) {\n messages.push({ role: 'assistant', content: ev.content, ts: ev.ts });\n for (const b of ev.content) {\n if (b.type === 'tool_use') openToolUses.add(b.id);\n }\n }\n return { exactJournalActive, usage: accumulateUsage(params.usage, ev) };\n } else if (!exactJournalActive && ev.type === 'tool_result') {\n if (!openToolUses.has(ev.id)) {\n emitDamaged(params, `Orphan tool_result \"${ev.id}\" has no matching tool_use`);\n return { exactJournalActive };\n }\n openToolUses.delete(ev.id);\n const resultBlock: ContentBlock = {\n type: 'tool_result',\n tool_use_id: ev.id,\n content: typeof ev.content === 'string' ? ev.content : JSON.stringify(ev.content),\n is_error: ev.isError,\n };\n const last = messages[messages.length - 1];\n const lastIsToolResultUser =\n last?.role === 'user' &&\n Array.isArray(last.content) &&\n last.content.every((b) => (b as ContentBlock).type === 'tool_result');\n if (lastIsToolResultUser && Array.isArray(last.content)) {\n last.content.push(resultBlock);\n } else {\n messages.push({ role: 'user', content: [resultBlock], ts: ev.ts });\n }\n }\n\n return { exactJournalActive };\n}\n\nfunction accumulateUsage(\n usage: UsageTotals,\n ev: Extract<SessionEvent, { type: 'llm_response' }>,\n): UsageTotals {\n return {\n input: usage.input + (ev.usage.input ?? 0),\n output: usage.output + (ev.usage.output ?? 0),\n cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),\n cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0),\n };\n}\n\nfunction emitDamaged(\n params: { id: string; events?: EventBus | undefined },\n detail: string,\n): void {\n params.events?.emit('session.damaged', {\n sessionId: params.id,\n detail,\n });\n}\n", "import type { SessionData, SessionEvent } from '../types/session.js';\n\ntype ToolCallEnd = SessionData['toolCallEnds'][number];\n\nexport function extractToolCallEnds(events: readonly SessionEvent[]): ToolCallEnd[] {\n const out: ToolCallEnd[] = [];\n for (const event of events) {\n if (event.type !== 'tool_call_end') continue;\n out.push({\n name: event.name,\n id: event.id,\n durationMs: event.durationMs,\n ok: event.ok ?? true,\n outputBytes: event.outputBytes ?? event.outputSize,\n outputTokens: event.outputTokens,\n outputLines: event.outputLines,\n });\n }\n return out;\n}\n", "import { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { SecretScrubber } from '../../types/secret-scrubber.js';\nimport type { SessionEvent, SessionSummary } from '../../types/session.js';\nimport { scrubPersistedSessionEvent } from '../session-read-scrubber.js';\nimport { sessionContentPreview, userInputTitle } from '../session-helpers.js';\n\nexport async function summarizeSessionFile(opts: {\n id: string;\n file: string;\n mtime: string;\n secretScrubber: SecretScrubber;\n}): Promise<SessionSummary> {\n return summarizeSessionEventSequence({\n id: opts.id,\n events: iterateSessionEvents(opts.file, opts.secretScrubber),\n mtime: opts.mtime,\n });\n}\n\nexport async function summarizeSessionEvents(opts: {\n id: string;\n events: Iterable<SessionEvent>;\n mtime: string;\n}): Promise<SessionSummary> {\n return summarizeSessionEventSequence(opts);\n}\n\nasync function summarizeSessionEventSequence(opts: {\n id: string;\n events: Iterable<SessionEvent> | AsyncIterable<SessionEvent>;\n mtime: string;\n}): Promise<SessionSummary> {\n const { id, events, mtime } = opts;\n try {\n let title = '(empty session)';\n let startedAt = new Date(0).toISOString();\n let endedAt: string | undefined;\n let model = 'unknown';\n let provider = 'unknown';\n let tokenIn = 0;\n let tokenOut = 0;\n let iterationCount = 0;\n let toolCallCount = 0;\n let toolErrorCount = 0;\n let fileChangeCount = 0;\n let messageCount = 0;\n let lastUserMessage: string | undefined;\n let lastUserMessageMs = Number.NEGATIVE_INFINITY;\n // Seed lastActivityAt from mtime when startedAt is still the epoch\n // default (empty session or no session_start event), so the summary\n // shows the file's real last-modified time instead of 1970-01-01.\n let lastActivityAt = startedAt;\n let lastActivityMs = Date.parse(startedAt);\n if (lastActivityMs === 0 || !Number.isFinite(lastActivityMs)) {\n const mtimeMs = Date.parse(mtime);\n if (Number.isFinite(mtimeMs) && mtimeMs > 0) {\n lastActivityAt = mtime;\n lastActivityMs = mtimeMs;\n }\n }\n const toolBreakdown: Record<string, number> = {};\n let outcome: SessionSummary['outcome'];\n let lastEventType: SessionEvent['type'] | undefined;\n let hasError = false;\n let sawStart = false;\n\n for await (const e of events) {\n lastEventType = e.type;\n const eventActivityMs = Date.parse(e.ts);\n if (Number.isFinite(eventActivityMs) && (!Number.isFinite(lastActivityMs) || eventActivityMs > lastActivityMs)) {\n lastActivityAt = e.ts;\n lastActivityMs = eventActivityMs;\n }\n if (e.type === 'session_start') {\n if (!sawStart) {\n sawStart = true;\n startedAt = e.ts;\n model = e.model ?? 'unknown';\n provider = e.provider ?? 'unknown';\n }\n } else if (e.type === 'session_end') {\n endedAt = e.ts;\n } else if (e.type === 'user_input') {\n if (title === '(empty session)') title = userInputTitle(e.content);\n const userMessageMs = Date.parse(e.ts);\n if (!Number.isFinite(userMessageMs) || userMessageMs >= lastUserMessageMs) {\n lastUserMessage = sessionContentPreview(e.content);\n if (Number.isFinite(userMessageMs)) lastUserMessageMs = userMessageMs;\n }\n messageCount++;\n } else if (e.type === 'llm_response') {\n messageCount++;\n tokenIn += e.usage.input ?? 0;\n tokenOut += e.usage.output ?? 0;\n } else if (e.type === 'in_flight_start') iterationCount++;\n else if (e.type === 'tool_call_start') {\n toolCallCount++;\n toolBreakdown[e.name] = (toolBreakdown[e.name] ?? 0) + 1;\n } else if (e.type === 'tool_result' && e.isError) toolErrorCount++;\n else if (e.type === 'file_snapshot') fileChangeCount += e.files.length;\n else if (e.type === 'error' || e.type === 'provider_error') hasError = true;\n }\n\n if (lastEventType === 'session_end') {\n outcome = 'completed';\n } else if (lastEventType === 'in_flight_start') {\n outcome = 'aborted';\n } else if (hasError) {\n outcome = 'error';\n }\n\n return {\n id,\n title,\n startedAt,\n endedAt,\n model,\n provider,\n tokenTotal: tokenIn + tokenOut,\n lastActivityAt,\n messageCount,\n ...(lastUserMessage !== undefined ? { lastUserMessage } : {}),\n iterationCount: iterationCount > 0 ? iterationCount : undefined,\n toolCallCount: toolCallCount > 0 ? toolCallCount : undefined,\n toolErrorCount: toolErrorCount > 0 ? toolErrorCount : undefined,\n fileChangeCount: fileChangeCount > 0 ? fileChangeCount : undefined,\n toolBreakdown: Object.keys(toolBreakdown).length > 0 ? toolBreakdown : {},\n outcome,\n };\n } catch {\n return {\n id,\n title: '(damaged)',\n startedAt: mtime,\n model: 'unknown',\n provider: 'unknown',\n tokenTotal: 0,\n lastActivityAt: mtime,\n messageCount: 0,\n };\n }\n}\n\nexport async function* iterateSessionEvents(\n file: string,\n secretScrubber: SecretScrubber,\n): AsyncGenerator<SessionEvent> {\n const stream = createReadStream(file, { encoding: 'utf8' });\n const lines = createInterface({ input: stream, crlfDelay: Infinity });\n try {\n for await (const line of lines) {\n if (!line.trim()) continue;\n try {\n const parsed: unknown = JSON.parse(line);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n typeof (parsed as { type?: unknown | undefined }).type === 'string' &&\n typeof (parsed as { ts?: unknown | undefined }).ts === 'string'\n ) {\n yield scrubPersistedSessionEvent(parsed as SessionEvent, secretScrubber);\n }\n } catch {\n // skip malformed JSON\n }\n }\n } finally {\n lines.close();\n stream.destroy();\n }\n}\n", "import * as fsp from 'node:fs/promises';\nimport type { SecretScrubber } from '../../types/secret-scrubber.js';\nimport type { SessionSummary } from '../../types/session.js';\nimport { iterateSessionEvents } from './summary-builder.js';\nimport type { SessionFileRef } from './types.js';\n\nexport async function readSessionSummaryHeader(\n ref: SessionFileRef,\n secretScrubber: SecretScrubber,\n): Promise<SessionSummary | null> {\n let mtime = new Date(0).toISOString();\n try {\n const stat = await fsp.stat(ref.filePath);\n if (!stat.isFile()) return damagedSummary(ref.id, stat.mtime.toISOString());\n mtime = stat.mtime.toISOString();\n } catch {\n return null;\n }\n\n try {\n for await (const event of iterateSessionEvents(ref.filePath, secretScrubber)) {\n if (event.type === 'session_start') {\n return {\n id: ref.id,\n title: '(empty session)',\n startedAt: event.ts,\n model: event.model ?? 'unknown',\n provider: event.provider ?? 'unknown',\n tokenTotal: 0,\n };\n }\n }\n return emptySessionSummary(ref.id, new Date(0).toISOString());\n } catch {\n return damagedSummary(ref.id, mtime);\n }\n}\n\nfunction emptySessionSummary(id: string, startedAt: string): SessionSummary {\n return {\n id,\n title: '(empty session)',\n startedAt,\n model: 'unknown',\n provider: 'unknown',\n tokenTotal: 0,\n };\n}\n\nfunction damagedSummary(id: string, startedAt: string): SessionSummary {\n return {\n id,\n title: '(damaged)',\n startedAt,\n model: 'unknown',\n provider: 'unknown',\n tokenTotal: 0,\n };\n}\n", "import * as fsp from 'node:fs/promises';\nimport { atomicWrite } from '../../utils/atomic-write.js';\nimport { compareSessionSummaries } from '../session-summary.js';\nimport { mapWithConcurrency } from '../storage-concurrency.js';\nimport type {\n DirectorySummaryCandidate,\n SessionFileRef,\n ShardManifestEntry,\n} from './types.js';\nimport type { SessionSummary } from '../../types/session.js';\n\nexport interface ReadOrBuildShardManifestOptions {\n shardKey: string;\n manifestPath: string;\n concurrency: number;\n collectSessionFilesInShard(shardKey: string): Promise<SessionFileRef[]>;\n readSummaryManifest(id: string): Promise<SessionSummary | null>;\n summaryHeaderFor(ref: SessionFileRef): Promise<SessionSummary | null>;\n summaryFor(id: string): Promise<SessionSummary>;\n}\n\nexport async function readOrBuildShardManifestEntry(\n opts: ReadOrBuildShardManifestOptions,\n): Promise<ShardManifestEntry> {\n try {\n const raw = await fsp.readFile(opts.manifestPath, 'utf8');\n const parsed = JSON.parse(raw) as ShardManifestEntry;\n return {\n summaries: Array.isArray(parsed.summaries) ? parsed.summaries : [],\n ids: Array.isArray(parsed.ids) ? parsed.ids : [],\n };\n } catch {\n // build below\n }\n\n const refs = await opts.collectSessionFilesInShard(opts.shardKey);\n const candidates = await mapWithConcurrency(\n refs,\n opts.concurrency,\n async (ref): Promise<DirectorySummaryCandidate | null> => {\n const manifest = await opts.readSummaryManifest(ref.id);\n if (manifest) return { summary: manifest, needsBackfill: false };\n const summary = await opts.summaryHeaderFor(ref);\n if (!summary) return null;\n const hydrated = await opts.summaryFor(summary.id).catch(() => summary);\n return { summary: hydrated, needsBackfill: false };\n },\n );\n const summaries = candidates\n .filter((candidate): candidate is DirectorySummaryCandidate => candidate !== null)\n .map((candidate) => candidate.summary);\n summaries.sort(compareSessionSummaries);\n const entry: ShardManifestEntry = { summaries, ids: summaries.map((summary) => summary.id) };\n await atomicWrite(opts.manifestPath, JSON.stringify(entry), { mode: 0o600 }).catch(\n () => undefined,\n );\n return entry;\n}\n", "import type { SessionSummary } from '../types/session.js';\n\nexport interface SessionFilterCriteria {\n since?: string | undefined;\n until?: string | undefined;\n provider?: string | undefined;\n model?: string | undefined;\n minTokens?: number | undefined;\n titleContains?: string | undefined;\n}\n\nexport function compareSessionSummaries(a: SessionSummary, b: SessionSummary): number {\n const aActivity = a.lastActivityAt ?? a.endedAt ?? a.startedAt;\n const bActivity = b.lastActivityAt ?? b.endedAt ?? b.startedAt;\n if (aActivity < bActivity) return 1;\n if (aActivity > bActivity) return -1;\n if (a.startedAt < b.startedAt) return 1;\n if (a.startedAt > b.startedAt) return -1;\n return a.id.localeCompare(b.id);\n}\n\nexport function matchesSessionFilter(summary: SessionSummary, criteria: SessionFilterCriteria): boolean {\n if (criteria.since && summary.startedAt < criteria.since) return false;\n if (criteria.until && summary.startedAt > criteria.until) return false;\n if (criteria.provider && summary.provider !== criteria.provider) return false;\n if (criteria.model && summary.model !== criteria.model) return false;\n if (criteria.minTokens !== undefined && summary.tokenTotal < criteria.minTokens) return false;\n if (criteria.titleContains) {\n const needle = criteria.titleContains.toLocaleLowerCase();\n if (!summary.title.toLocaleLowerCase().includes(needle)) return false;\n }\n return true;\n}\n", "import * as fsp from 'node:fs/promises';\nimport type { SecretScrubber } from '../../types/secret-scrubber.js';\nimport type { SessionEvent } from '../../types/session.js';\nimport { scrubPersistedSessionEvent } from '../session-read-scrubber.js';\n\nexport async function searchSessionEvents(args: {\n file: string;\n secretScrubber: SecretScrubber;\n predicate: (event: SessionEvent, eventIndex: number, ts: string) => boolean;\n limit?: number | undefined;\n signal?: AbortSignal | undefined;\n}): Promise<Array<{ event: SessionEvent; eventIndex: number; ts: string }>> {\n const { file, secretScrubber, predicate, limit, signal } = args;\n const out: Array<{ event: SessionEvent; eventIndex: number; ts: string }> = [];\n\n let stat: import('node:fs').Stats;\n try {\n stat = await fsp.stat(file);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n if (stat.size === 0) return [];\n\n let fh: fsp.FileHandle | undefined;\n try {\n fh = await fsp.open(file, 'r');\n const CHUNK = 64 * 1024;\n const buf = Buffer.alloc(CHUNK);\n let leftover = '';\n let eventIndex = 0;\n for (let position = 0; ; position += buf.byteLength) {\n if (signal?.aborted) {\n const reason = signal.reason ?? new DOMException('Aborted', 'AbortError');\n throw reason;\n }\n const { bytesRead } = await fh.read(buf, 0, CHUNK, position);\n if (bytesRead === 0) break;\n const text = leftover + buf.subarray(0, bytesRead).toString('utf8');\n const parts = text.split('\\n');\n leftover = parts.pop() ?? '';\n for (const line of parts) {\n if (!line) continue;\n const ev = parseSessionEventLine(line, secretScrubber);\n if (!ev) continue;\n if (predicate(ev, eventIndex, ev.ts)) {\n out.push({ event: ev, eventIndex, ts: ev.ts });\n if (limit !== undefined && out.length >= limit) return out;\n }\n eventIndex++;\n }\n }\n const trailing = parseSessionEventLine(leftover.trim() ? leftover : '', secretScrubber);\n if (trailing && predicate(trailing, eventIndex, trailing.ts)) {\n out.push({ event: trailing, eventIndex, ts: trailing.ts });\n }\n return out;\n } finally {\n if (fh) await fh.close().catch(() => undefined);\n }\n}\n\nfunction parseSessionEventLine(\n line: string,\n secretScrubber: SecretScrubber,\n): SessionEvent | null {\n if (!line) return null;\n try {\n const parsed: unknown = JSON.parse(line);\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n typeof (parsed as { type?: unknown }).type !== 'string' ||\n typeof (parsed as { ts?: unknown }).ts !== 'string'\n ) {\n return null;\n }\n return scrubPersistedSessionEvent(parsed as SessionEvent, secretScrubber);\n } catch {\n return null;\n }\n}\n", "import * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { EventBus } from '../kernel/events.js';\nimport type { ContentBlock } from '../types/blocks.js';\nimport type { Logger } from '../types/logger.js';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\n\n/**\n * The persisted form of a single queued user message. The TUI's\n * in-memory QueueItem has a render id; that's pure UI bookkeeping, so\n * we drop it when serializing \u2014 fresh ids are assigned on rehydrate.\n */\nexport interface PersistedQueueItem {\n displayText: string;\n blocks: ContentBlock[];\n /** When true, the item will be refined via model.refine before entering the agent context. */\n shouldRefine?: boolean | undefined;\n}\n\n/** Hard memory/disk budgets shared by QueueStore and the TUI reducer. */\nexport const QUEUE_MAX_ITEMS = 100;\nexport const QUEUE_MAX_BYTES = 16 * 1024 * 1024;\nexport const QUEUE_MAX_ITEM_BYTES = 8 * 1024 * 1024;\n\nfunction serializedBytes(value: unknown): number {\n try {\n return Buffer.byteLength(JSON.stringify(value), 'utf8');\n } catch {\n return Number.POSITIVE_INFINITY;\n }\n}\n\n/**\n * Keep the FIFO prefix that fits the queue budget. Callers can compare the\n * returned length with the input length to detect a rejected append.\n */\nexport function retainPersistedQueueItems(\n items: readonly PersistedQueueItem[],\n): PersistedQueueItem[] {\n const retained: PersistedQueueItem[] = [];\n let bytes = 2; // JSON array brackets\n for (const item of items) {\n if (retained.length >= QUEUE_MAX_ITEMS) break;\n const itemBytes = serializedBytes(item);\n if (!Number.isFinite(itemBytes) || itemBytes > QUEUE_MAX_ITEM_BYTES) break;\n const nextBytes = bytes + itemBytes + (retained.length > 0 ? 1 : 0);\n if (nextBytes > QUEUE_MAX_BYTES) break;\n retained.push(item);\n bytes = nextBytes;\n }\n return retained;\n}\n\n/**\n * Side-file storage for a session's pending input queue. Lives at\n * `<sessionDir>/queue.json` next to the attachment spool. Reads are\n * tolerant (missing/malformed file \u2192 empty array); writes are atomic\n * (tmp + rename) so a crash mid-write can never leave a partial file\n * the next launch would choke on.\n *\n * The contract is \"snapshot replacement\": every mutation hands the\n * full queue and we rewrite the file. The queue is small (rarely more\n * than a handful of messages), so this is cheaper than delta logging\n * and avoids the replay complexity.\n */\nexport class QueueStore {\n private readonly file: string;\n // Use `| undefined` (not `?`) so exactOptionalPropertyTypes doesn't\n // reject assigning an optional constructor parameter to these fields.\n private readonly events: EventBus | undefined;\n private readonly traceId: string | undefined;\n private readonly logger: Logger | undefined;\n\n constructor(opts: {\n dir: string;\n events?: EventBus;\n traceId?: string;\n logger?: Logger | undefined;\n }) {\n this.file = path.join(opts.dir, 'queue.json');\n this.events = opts.events;\n this.traceId = opts.traceId;\n this.logger = opts.logger;\n }\n\n private logWarn(msg: string, ctx?: Record<string, unknown>): void {\n if (this.logger) {\n this.logger.warn(msg, ctx);\n } else {\n console.warn(JSON.stringify({ ...ctx, message: msg, timestamp: new Date().toISOString() }));\n }\n }\n\n async write(items: PersistedQueueItem[]): Promise<void> {\n const t0 = Date.now();\n if (items.length === 0) {\n // Empty queue \u2192 remove the file rather than write `[]`. Keeps\n // a clean idle state on disk and makes `read()` cheaper.\n await this.clear();\n return;\n }\n try {\n const retained = retainPersistedQueueItems(items);\n if (retained.length !== items.length) {\n this.logWarn(\n 'Queue snapshot exceeded its memory budget; oversized tail was not persisted',\n {\n event: 'queue_store.budget_exceeded',\n path: this.file,\n suppliedItems: items.length,\n retainedItems: retained.length,\n maxItems: QUEUE_MAX_ITEMS,\n maxBytes: QUEUE_MAX_BYTES,\n },\n );\n }\n if (retained.length === 0) {\n await this.clear();\n return;\n }\n await atomicWrite(this.file, JSON.stringify(retained), { mode: 0o600 });\n this.events?.emit('storage.write', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'write',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n } catch (err) {\n this.events?.emit('storage.error', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'write',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: false,\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n this.logWarn('Queue store write failed', {\n event: 'queue_store.write_failed',\n path: this.file,\n message: toErrorMessage(err),\n });\n }\n }\n\n async read(): Promise<PersistedQueueItem[]> {\n const t0 = Date.now();\n let raw: string;\n try {\n const stat = await fsp.stat(this.file);\n if (stat.size > QUEUE_MAX_BYTES) {\n this.logWarn('Queue file exceeds the safe read budget; ignoring it', {\n event: 'queue_store.read_budget_exceeded',\n path: this.file,\n bytes: stat.size,\n maxBytes: QUEUE_MAX_BYTES,\n });\n this.events?.emit('storage.read', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'read',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'size_limit_exceeded',\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n return [];\n }\n raw = await fsp.readFile(this.file, 'utf8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n this.events?.emit('storage.read', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'read',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n return [];\n }\n this.events?.emit('storage.error', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'read',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: true,\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n this.logWarn('Queue store read failed', {\n event: 'queue_store.read_failed',\n path: this.file,\n message: toErrorMessage(err),\n });\n return [];\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n this.events?.emit('storage.read', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'read',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse_failed',\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n return [];\n }\n if (!Array.isArray(parsed)) {\n this.events?.emit('storage.read', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'read',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'invalid_schema',\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n return [];\n }\n this.events?.emit('storage.read', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'read',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n const out: PersistedQueueItem[] = [];\n let outBytes = 2;\n for (const v of parsed) {\n if (!isPersistedQueueItem(v)) continue;\n if (out.length >= QUEUE_MAX_ITEMS) break;\n const itemBytes = serializedBytes(v);\n if (!Number.isFinite(itemBytes) || itemBytes > QUEUE_MAX_ITEM_BYTES) break;\n const nextBytes = outBytes + itemBytes + (out.length > 0 ? 1 : 0);\n if (nextBytes > QUEUE_MAX_BYTES) break;\n out.push(v);\n outBytes = nextBytes;\n }\n return out;\n }\n\n async clear(): Promise<void> {\n const t0 = Date.now();\n try {\n await fsp.unlink(this.file);\n this.events?.emit('storage.write', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'clear',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') return;\n this.events?.emit('storage.error', {\n sessionId: this.traceId ?? '~boot~',\n store: 'queue',\n filePath: this.file,\n operation: 'clear',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: true,\n ...(this.traceId !== undefined && { traceId: this.traceId }),\n });\n // Best-effort: a permission/lock error during clear is rare and\n // the queue slash command is non-critical. Warn so it's observable\n // but don't throw so the slash command doesn't crash.\n this.logWarn('Queue store clear failed', {\n event: 'queue_store.clear_failed',\n path: this.file,\n message: (err as Error).message,\n });\n }\n }\n}\n\nfunction isPersistedQueueItem(v: unknown): v is PersistedQueueItem {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n return typeof o['displayText'] === 'string' && Array.isArray(o['blocks']);\n}\n", "import { randomBytes } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport type {\n AddAttachmentInput,\n Attachment,\n AttachmentKind,\n AttachmentRef,\n AttachmentStore,\n} from '../types/attachment.js';\nimport type { ContentBlock } from '../types/blocks.js';\n\nexport interface AttachmentStoreOptions {\n /**\n * Directory for spooling payloads larger than `spoolThresholdBytes`.\n * When omitted, all payloads stay in memory.\n */\n spoolDir?: string | undefined;\n spoolThresholdBytes?: number | undefined;\n}\n\nconst DEFAULT_SPOOL_THRESHOLD = 256 * 1024; // 256 KB\n// Two placeholder shapes:\n// - seq-keyed `[<kind> #<seq>\u2026]` \u2014 kind is `pasted` / `image` / `file`. A\n// cosmetic suffix after the seq (e.g. `, 123 lines`) is tolerated so the\n// TUI can render `[pasted #1, 123 lines]` while still resolving by seq.\n// - path-keyed `[file:<path>]` \u2014 resolves to the most recent file ref whose\n// stored path matches, so the TUI can show a human-readable file chip.\nconst PLACEHOLDER_RE = /\\[(pasted|image|file) #(\\d+)[^\\]]*\\]|\\[file:([^\\]]+)\\]/g;\n\n/**\n * In-memory attachment store with optional disk spool. Placeholder syntax\n * is `[<kind> #<seq>]` (seq-keyed) or `[file:<path>]` (path-keyed) where kind\n * is `pasted` / `image` / `file`. Unknown placeholders are passed through\n * as-is so users can write that literal text without losing it.\n */\nexport class DefaultAttachmentStore implements AttachmentStore {\n private readonly items = new Map<string, Attachment>();\n private readonly refs: AttachmentRef[] = [];\n private nextSeq: Record<AttachmentKind, number> = { text: 0, image: 0, file: 0 };\n private readonly spoolDir: string | undefined;\n private readonly spoolThreshold: number;\n\n constructor(opts: AttachmentStoreOptions = {}) {\n this.spoolDir = opts.spoolDir;\n this.spoolThreshold = opts.spoolThresholdBytes ?? DEFAULT_SPOOL_THRESHOLD;\n }\n\n async add(input: AddAttachmentInput): Promise<AttachmentRef> {\n const seq = ++this.nextSeq[input.kind];\n const id = `${kindPrefix(input.kind)}-${seq}-${randomBytes(3).toString('hex')}`;\n const bytes = Buffer.byteLength(input.data, input.kind === 'image' ? 'base64' : 'utf8');\n let spooledPath: string | undefined;\n let data: string | undefined = input.data;\n if (this.spoolDir && bytes >= this.spoolThreshold) {\n await fsp.mkdir(this.spoolDir, { recursive: true });\n spooledPath = path.join(this.spoolDir, `${id}.bin`);\n // atomicWrite: torn spool would silently corrupt the attachment;\n // the user would see garbled output the next time it's expanded.\n await atomicWrite(spooledPath, input.data, {\n encoding: input.kind === 'image' ? 'base64' : 'utf8',\n });\n data = undefined;\n }\n const att: Attachment = {\n id,\n kind: input.kind,\n meta: input.meta ?? {},\n data,\n path: spooledPath,\n bytes,\n createdAt: new Date().toISOString(),\n };\n this.items.set(id, att);\n const ref: AttachmentRef = { id, kind: input.kind, seq, meta: att.meta };\n this.refs.push(ref);\n return ref;\n }\n\n async get(id: string): Promise<Attachment | undefined> {\n return this.items.get(id);\n }\n\n list(): AttachmentRef[] {\n return [...this.refs];\n }\n\n async expand(text: string): Promise<ContentBlock[]> {\n const matches = [...text.matchAll(PLACEHOLDER_RE)];\n if (matches.length === 0) return text ? [{ type: 'text', text }] : [];\n const blocks: ContentBlock[] = [];\n let lastIndex = 0;\n for (const m of matches) {\n const idx = m.index ?? 0;\n const before = text.slice(lastIndex, idx);\n if (before) blocks.push({ type: 'text', text: before });\n let ref: AttachmentRef | undefined;\n if (m[3] !== undefined) {\n // Path-keyed `[file:<path>]` \u2014 most recent matching file ref wins.\n const wantPath = m[3];\n ref = findLast(this.refs, (r) => r.kind === 'file' && refPath(r) === wantPath);\n } else {\n const kind = prefixToKind(m[1] as string);\n const seq = Number(m[2]);\n ref = this.refs.find((r) => r.kind === kind && r.seq === seq);\n }\n const att = ref ? this.items.get(ref.id) : undefined;\n if (!att) {\n blocks.push({ type: 'text', text: m[0] });\n } else {\n blocks.push(await this.toBlock(att));\n }\n lastIndex = idx + m[0].length;\n }\n const tail = text.slice(lastIndex);\n if (tail) blocks.push({ type: 'text', text: tail });\n return mergeAdjacentText(blocks);\n }\n\n async clear(): Promise<void> {\n // Unlink any spooled files so we don't leak disk space.\n if (this.spoolDir) {\n const toDelete: string[] = [];\n for (const att of this.items.values()) {\n if (att.path) toDelete.push(att.path);\n }\n /* v8 ignore next -- best-effort: unlink of a just-spooled file does not reject */\n await Promise.all(toDelete.map((p) => fsp.unlink(p).catch(() => undefined)));\n }\n this.items.clear();\n this.refs.length = 0;\n this.nextSeq = { text: 0, image: 0, file: 0 };\n }\n\n private async toBlock(att: Attachment): Promise<ContentBlock> {\n if (att.kind === 'image') {\n const data =\n att.data ?? (att.path ? await fsp.readFile(att.path, { encoding: 'base64' }) : '');\n return {\n type: 'image',\n source: {\n type: 'base64',\n media_type: att.meta.mediaType ?? 'image/png',\n data,\n },\n };\n }\n const raw = att.data ?? (att.path ? await fsp.readFile(att.path, 'utf8') : '');\n const label = att.meta.filename ? `<file path=\"${att.meta.filename}\">` : '<pasted>';\n const close = att.meta.filename ? '</file>' : '</pasted>';\n return { type: 'text', text: `${label}\\n${raw}\\n${close}` };\n }\n}\n\nfunction kindPrefix(kind: AttachmentKind): string {\n return kind === 'text' ? 'pasted' : kind;\n}\n\nfunction prefixToKind(prefix: string): AttachmentKind {\n if (prefix === 'pasted') return 'text';\n if (prefix === 'image') return 'image';\n return 'file';\n}\n\n/** Path a file ref was registered under, for `[file:<path>]` lookup. */\nfunction refPath(ref: AttachmentRef): string | undefined {\n return ref.meta.filename ?? ref.meta.label;\n}\n\n/** Last element matching the predicate (Node < 20 lacks Array.findLast). */\nfunction findLast<T>(arr: readonly T[], pred: (v: T) => boolean): T | undefined {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (pred(arr[i] as T)) return arr[i];\n }\n return undefined;\n}\n\nfunction mergeAdjacentText(blocks: ContentBlock[]): ContentBlock[] {\n const out: ContentBlock[] = [];\n for (const b of blocks) {\n const prev = out[out.length - 1];\n if (b.type === 'text' && prev && prev.type === 'text') {\n prev.text += b.text;\n } else {\n out.push(b);\n }\n }\n return out;\n}\n", "import { randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { MemoryEntry, MemoryScope } from '../types/memory.js';\nimport { MEMORY_TYPE_LABELS, type MemoryPriority, type MemoryType } from '../types/memory.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport type { WstackPaths } from '../utils/wstack-paths.js';\n\n// \u2500\u2500 Backend interface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemoryBackend {\n readonly kind: string;\n remember(scope: MemoryScope, entry: MemoryEntry, filePath: string): Promise<void>;\n forget(scope: MemoryScope, query: string, filePath: string): Promise<number>;\n readAll(scope: MemoryScope, filePath: string): Promise<string>;\n list(scope: MemoryScope, filePath: string, limit?: number | undefined): Promise<MemoryEntry[]>;\n search(scope: MemoryScope, query: string, filePath: string, limit?: number | undefined): Promise<MemoryEntry[]>;\n /** Find memories related to the given text via graph traversal. Optional \u2014 falls back to search. */\n findRelated?(scope: MemoryScope, filePath: string, text: string, limit: number): Promise<MemoryEntry[]>;\n clear(scope: MemoryScope, filePath: string): Promise<void>;\n consolidate(scope: MemoryScope, filePath: string): Promise<number>;\n}\n\n// \u2500\u2500 Entry serialization format \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Full format:\n// - [ISO] [TYPE|PRIORITY] mem_<id> text content #tag1 #tag2\n//\n// Examples:\n// - [2026-06-07T...] mem_1234_abcd Project uses pnpm #pnpm #build\n// - [2026-06-07T...] [convention|high] mem_5678_ef01 Use conventional commits #git #commit\n//\n// Old format (backward compatible):\n// - [ISO] mem_<id> text content\n\nconst TYPE_PRIORITY_RE = /^\\[(\\w+)\\|(\\w+)\\]\\s+/;\nconst TAG_RE = /#([\\w-]+)/g;\nconst MAX_MEMORY_CONSOLIDATE_BACKUPS = 5;\n\nfunction formatMetadata(entry: MemoryEntry): string {\n const parts: string[] = [];\n if (entry.type && entry.priority) {\n parts.push(`[${entry.type}|${entry.priority}]`);\n } else if (entry.type) {\n parts.push(`[${entry.type}]`);\n } else if (entry.priority) {\n parts.push(`[${entry.priority}]`);\n }\n if (entry.tags && entry.tags.length > 0) {\n parts.push(entry.tags.map((t) => `#${t}`).join(' '));\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nfunction lineToEntry(line: string, scope: MemoryScope): MemoryEntry | null {\n const trimmed = line.trim();\n if (!trimmed.startsWith('- [')) return null;\n\n // Parse timestamp: `- [ISO] ...`\n const tsMatch = trimmed.match(/^-\\s*\\[([^\\]]+)\\]/);\n if (!tsMatch) return null;\n const ts = tsMatch[1] ?? '';\n\n let rest = trimmed.slice(tsMatch[0].length).trim();\n\n // Parse optional type|priority: `[convention|high]` or `[fact]`\n let type: MemoryType | undefined;\n let priority: MemoryPriority | undefined;\n const tpMatch = rest.match(TYPE_PRIORITY_RE);\n if (tpMatch) {\n const a = tpMatch[1] ?? '';\n const b = tpMatch[2] ?? '';\n if (isMemoryType(a)) {\n type = a;\n priority = isPriority(b) ? b : undefined;\n } else if (isPriority(a)) {\n priority = a;\n }\n rest = rest.slice(tpMatch[0].length).trim();\n }\n\n // Parse optional entry ID: `mem_<ts>_<rand>`\n const idMatch = rest.match(/^mem_\\d+_\\w+\\s+/);\n let text: string;\n if (idMatch) {\n text = rest.slice(idMatch[0].length).trim();\n } else {\n text = rest.trim();\n }\n\n // Extract #tags from text\n const tags: string[] = [];\n let tagMatch: RegExpExecArray | null;\n TAG_RE.lastIndex = 0;\n // biome-ignore lint/suspicious/noAssignInExpressions: standard regex loop\n while ((tagMatch = TAG_RE.exec(text)) !== null) {\n tags.push(tagMatch[1] ?? '');\n }\n // Remove tags from display text\n const cleanText = text.replace(TAG_RE, '').replace(/\\s{2,}/g, ' ').trim();\n\n if (!cleanText) return null;\n\n return {\n scope,\n text: cleanText,\n ts,\n type,\n priority,\n tags: tags.length > 0 ? tags : undefined,\n };\n}\n\nfunction isMemoryType(s: string): s is MemoryType {\n return s in MEMORY_TYPE_LABELS;\n}\n\nfunction isPriority(s: string): s is MemoryPriority {\n return s === 'critical' || s === 'high' || s === 'medium' || s === 'low';\n}\n\n// \u2500\u2500 Inverted index for fast search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface IndexedEntry {\n entry: MemoryEntry;\n /** Lower-cased words extracted from text. */\n words: string[];\n /** Lower-cased tags. */\n tags: string[];\n}\n\ninterface InvertedIndex {\n /** word -> entry indices */\n wordMap: Map<string, number[]>;\n /** tag -> entry indices */\n tagMap: Map<string, number[]>;\n entries: IndexedEntry[];\n /** Last known file mtime for cache invalidation. */\n mtimeMs: number;\n}\n\n// Exported for the perf microbenchmark (tests/perf/memory-search.bench.ts),\n// which exercises the O(1) exact-lookup fast path against a large vocabulary.\n// Not part of the public API surface \u2014 internal to memory search.\nexport function buildInvertedIndex(entries: MemoryEntry[]): InvertedIndex {\n const wordMap = new Map<string, number[]>();\n const tagMap = new Map<string, number[]>();\n const indexed: IndexedEntry[] = new Array(entries.length);\n\n for (let i = 0; i < entries.length; i++) {\n const e = entries[i]!;\n const words = e.text.toLowerCase().split(/\\s+/).filter((w) => w.length > 0);\n const tags = (e.tags ?? []).map((t) => t.toLowerCase());\n indexed[i] = { entry: e, words, tags };\n\n for (const w of words) {\n const arr = wordMap.get(w);\n if (arr) arr.push(i);\n else wordMap.set(w, [i]);\n }\n for (const t of tags) {\n const arr = tagMap.get(t);\n if (arr) arr.push(i);\n else tagMap.set(t, [i]);\n }\n }\n\n return { wordMap, tagMap, entries: indexed, mtimeMs: 0 };\n}\n\n/**\n * Needles shorter than this skip the O(vocabulary) substring fallback. A\n * 1\u20132 char needle (e.g. \"a\", \"go\") otherwise forces a full-vocabulary walk\n * AND matches almost everything via `n.includes(word)`, so it's both the\n * slowest and least selective case. Exact-match lookups still run for any\n * length, so short whole-word queries (\"go\", \"ci\") keep working.\n */\nconst MIN_SUBSTRING_NEEDLE_LEN = 3;\n\n// Exported alongside buildInvertedIndex for the perf microbenchmark.\nexport function searchIndex(\n index: InvertedIndex,\n query: string,\n limit?: number,\n): MemoryEntry[] {\n const needles = query.toLowerCase().split(/\\s+/).filter((n) => n.length > 0);\n if (needles.length === 0) return [];\n\n const scores = new Map<number, number>();\n\n for (const n of needles) {\n // Fast path: exact word/tag hit via O(1) Map.get. This is the dominant\n // case (whole-word queries) and avoids walking the whole vocabulary.\n let matched = false;\n\n const wordExact = index.wordMap.get(n);\n if (wordExact) {\n for (const idx of wordExact) scores.set(idx, (scores.get(idx) ?? 0) + 1);\n matched = true;\n }\n const tagExact = index.tagMap.get(n);\n if (tagExact) {\n for (const idx of tagExact) scores.set(idx, (scores.get(idx) ?? 0) + 2);\n matched = true;\n }\n\n // Bounded substring fallback: only when the needle had no exact hit and is\n // long enough to be selective. Preserves partial-match recall (\"perf\" \u2192\n // \"performance\", \"rebuilding\" \u2192 stored word \"build\") without paying the\n // full-vocabulary scan on every whole-word query.\n if (matched || n.length < MIN_SUBSTRING_NEEDLE_LEN) continue;\n\n for (const [word, indices] of index.wordMap) {\n if (word.includes(n) || n.includes(word)) {\n for (const idx of indices) {\n scores.set(idx, (scores.get(idx) ?? 0) + 1);\n }\n }\n }\n for (const [tag, indices] of index.tagMap) {\n if (tag.includes(n) || n.includes(tag)) {\n for (const idx of indices) {\n scores.set(idx, (scores.get(idx) ?? 0) + 2);\n }\n }\n }\n }\n\n if (scores.size === 0) return [];\n\n const scored = Array.from(scores.entries());\n scored.sort((a, b) => b[1] - a[1]);\n\n const result: MemoryEntry[] = [];\n const max = limit ? Math.min(limit, scored.length) : scored.length;\n for (let i = 0; i < max; i++) {\n result.push(index.entries[scored[i]![0]]!.entry);\n }\n return result;\n}\n\n// \u2500\u2500 File-based backend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface FileMemoryBackendOptions {\n paths: WstackPaths;\n}\n\nexport class FileMemoryBackend implements MemoryBackend {\n readonly kind = 'file';\n private readonly files: Record<MemoryScope, string>;\n /** Cache of parsed entries per file path. */\n private readonly entryCache = new Map<string, MemoryEntry[]>();\n /** Inverted index per file path. */\n private readonly indexCache = new Map<string, InvertedIndex>();\n /** File mtime cache for invalidation. */\n private readonly mtimeCache = new Map<string, number>();\n\n constructor(opts: FileMemoryBackendOptions) {\n this.files = {\n 'project-agents': opts.paths.inProjectAgentsFile,\n 'project-memory': opts.paths.projectMemory,\n 'user-memory': opts.paths.globalMemory,\n };\n }\n\n private resolveFile(filePath: string, scope: MemoryScope): string {\n return filePath || this.files[scope];\n }\n\n private async getMtime(file: string): Promise<number> {\n try {\n const stat = await fs.stat(file);\n return stat.mtimeMs;\n } catch {\n return 0;\n }\n }\n\n private invalidateCache(file: string): void {\n this.entryCache.delete(file);\n this.indexCache.delete(file);\n this.mtimeCache.delete(file);\n }\n\n /**\n * Load (and cache) the parsed entries for a file. Callers that have already\n * stat'd the file this tick (e.g. `getIndex`) can pass the known `mtime` to\n * avoid a redundant `fs.stat` \u2014 otherwise it's fetched here.\n */\n private async loadEntries(\n file: string,\n scope: MemoryScope,\n mtime?: number,\n ): Promise<MemoryEntry[]> {\n const resolvedMtime = mtime ?? (await this.getMtime(file));\n const cachedMtime = this.mtimeCache.get(file);\n if (cachedMtime === resolvedMtime && this.entryCache.has(file)) {\n return this.entryCache.get(file)!;\n }\n\n const raw = await this.readAll(scope, file);\n if (!raw.trim()) {\n this.entryCache.set(file, []);\n this.mtimeCache.set(file, resolvedMtime);\n return [];\n }\n\n const entries = parseEntries(raw, scope);\n this.entryCache.set(file, entries);\n this.mtimeCache.set(file, resolvedMtime);\n return entries;\n }\n\n private async getIndex(file: string, scope: MemoryScope): Promise<InvertedIndex> {\n const mtime = await this.getMtime(file);\n const cached = this.indexCache.get(file);\n if (cached && cached.mtimeMs === mtime) {\n return cached;\n }\n\n // Reuse the mtime we just fetched \u2014 loadEntries would otherwise stat again.\n const entries = await this.loadEntries(file, scope, mtime);\n const index = buildInvertedIndex(entries);\n index.mtimeMs = mtime;\n this.indexCache.set(file, index);\n return index;\n }\n\n async remember(scope: MemoryScope, entry: MemoryEntry, filePath: string): Promise<void> {\n const file = this.resolveFile(filePath, scope);\n const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;\n const meta = formatMetadata(entry);\n const line = `- [${entry.ts}] ${id}${meta} ${entry.text.replace(/\\n/g, ' ')}\\n`;\n\n // The legacy implementation read and atomically rewrote the entire\n // Markdown file for every new memory. Besides O(file size) I/O per append,\n // concurrent callers could overwrite one another. Serialize writers and\n // append only the new line; inspect one trailing byte solely to preserve a\n // valid line boundary for hand-edited files that lack a final newline.\n await withFileLock(file, async () => {\n const handle = await fs.open(file, 'a+');\n try {\n const stat = await handle.stat();\n let prefix = '';\n if (stat.size === 0) {\n prefix = '# Agent Memory\\n\\n';\n } else {\n const tail = Buffer.allocUnsafe(1);\n const { bytesRead } = await handle.read(tail, 0, 1, stat.size - 1);\n if (bytesRead === 1 && tail[0] !== 0x0a) prefix = '\\n';\n }\n await handle.appendFile(`${prefix}${line}`, 'utf8');\n // Match atomicWrite's durability guarantee without rewriting the old\n // bytes. fsync is best-effort because memory persistence is non-fatal.\n await handle.sync().catch(() => undefined);\n } finally {\n await handle.close();\n }\n });\n this.invalidateCache(file);\n }\n\n async forget(scope: MemoryScope, query: string, filePath: string): Promise<number> {\n const file = this.resolveFile(filePath, scope);\n return withFileLock(file, async () => {\n let existing: string;\n try { existing = await fs.readFile(file, 'utf8'); } catch { return 0; /* best-effort */ }\n\n const needle = query.toLowerCase();\n const idMatcher = /mem_\\d+_\\w+/;\n let removed = 0;\n const lines = existing.split('\\n').filter((line) => {\n const trimmed = line.trim();\n if (!trimmed.startsWith('- ')) return true;\n if (idMatcher.test(query)) {\n const entryIdMatch = /mem_\\d+_\\w+/.exec(trimmed);\n if (entryIdMatch && entryIdMatch[0] === query) { removed++; return false; }\n }\n if (trimmed.toLowerCase().includes(needle)) { removed++; return false; }\n return true;\n });\n if (removed > 0) {\n if (lines.length === 0 || (lines.length === 1 && !lines[0]?.trim())) {\n await atomicWrite(file, '');\n } else {\n await atomicWrite(file, lines.join('\\n'));\n }\n }\n this.invalidateCache(file);\n return removed;\n });\n }\n\n async readAll(scope: MemoryScope, filePath: string): Promise<string> {\n const file = this.resolveFile(filePath, scope);\n try { return await fs.readFile(file, 'utf8'); } catch { return ''; /* best-effort */ }\n }\n\n async list(scope: MemoryScope, filePath: string, limit?: number): Promise<MemoryEntry[]> {\n const file = this.resolveFile(filePath, scope);\n const entries = await this.loadEntries(file, scope);\n return limit ? entries.slice(0, limit) : entries;\n }\n\n async search(scope: MemoryScope, query: string, filePath: string, limit?: number): Promise<MemoryEntry[]> {\n const file = this.resolveFile(filePath, scope);\n const index = await this.getIndex(file, scope);\n return searchIndex(index, query, limit);\n }\n\n async clear(scope: MemoryScope, filePath: string): Promise<void> {\n const file = this.resolveFile(filePath, scope);\n await atomicWrite(file, '');\n this.invalidateCache(file);\n }\n\n async consolidate(scope: MemoryScope, filePath: string): Promise<number> {\n const file = this.resolveFile(filePath, scope);\n let existing: string;\n try { existing = await fs.readFile(file, 'utf8'); } catch { return 0; /* best-effort */ }\n\n const seen = new Set<string>();\n let removed = 0;\n const lines = existing.split('\\n').filter((line) => {\n const trimmed = line.trim();\n if (!trimmed.startsWith('- ')) return true;\n // Normalize: strip timestamp, ID, type|priority, tags\n const norm = trimmed\n .replace(/\\[[^\\]]+\\]/g, '')\n .replace(/\\bmem_\\d+_\\w+\\s*/, '')\n .replace(/#[\\w-]+/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .toLowerCase();\n if (seen.has(norm)) { removed++; return false; }\n seen.add(norm);\n return true;\n });\n\n const next = lines.join('\\n');\n const backup = `${file}.bak.${Date.now()}`;\n try {\n await fs.copyFile(file, backup);\n await pruneConsolidateBackups(file);\n } catch { /* best-effort */ }\n /* v8 ignore next -- best-effort: atomicWrite failure during consolidate is non-fatal */\n try { await atomicWrite(file, next); } catch { return 0; /* best-effort */ }\n this.invalidateCache(file);\n return removed;\n }\n}\n\nasync function pruneConsolidateBackups(file: string): Promise<void> {\n const dir = path.dirname(file);\n const base = path.basename(file);\n const prefix = `${base}.bak.`;\n const backups = (await fs.readdir(dir))\n .filter((name) => name.startsWith(prefix))\n .sort()\n .reverse();\n\n await Promise.all(\n backups.slice(MAX_MEMORY_CONSOLIDATE_BACKUPS).map(async (name) => {\n try {\n await fs.unlink(path.join(dir, name));\n } catch {\n // best-effort\n }\n }),\n );\n}\n\n// \u2500\u2500 Entry parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function parseEntries(raw: string, scope: MemoryScope = 'project-memory'): MemoryEntry[] {\n const entries: MemoryEntry[] = [];\n for (const line of raw.split('\\n')) {\n const entry = lineToEntry(line, scope);\n if (entry) entries.push(entry);\n }\n return entries.reverse(); // newest first\n}\n", "export type MemoryScope = 'project-agents' | 'project-memory' | 'user-memory';\n\n// \u2500\u2500 Memory categories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type MemoryType =\n | 'fact'\n | 'decision'\n | 'convention'\n | 'preference'\n | 'reference'\n | 'anti_pattern';\n\nexport const MEMORY_TYPE_LABELS: Record<MemoryType, string> = {\n fact: 'Fact',\n decision: 'Decision',\n convention: 'Convention',\n preference: 'Preference',\n reference: 'Reference',\n anti_pattern: 'Anti-pattern',\n};\n\nexport type MemoryPriority = 'critical' | 'high' | 'medium' | 'low';\n\nexport interface MemoryEntry {\n scope: MemoryScope;\n text: string;\n ts: string;\n /** Category \u2014 helps the agent decide whether to inject or ignore. */\n type?: MemoryType | undefined;\n /** Free-form tags for grouping (e.g. [\"build\", \"pnpm\", \"typescript\"]). */\n tags?: string[] | undefined;\n /** Priority \u2014 critical entries are always injected; low may be skipped. */\n priority?: MemoryPriority | undefined;\n /** Session or agent that created this entry. */\n source?: string | undefined;\n /** 0.0\u20131.0 confidence. Low-confidence entries are injected less often. */\n confidence?: number | undefined;\n /** ISO timestamp of last access (read or injection into context). */\n lastAccessed?: string | undefined;\n}\n\n// \u2500\u2500 Memory events \u2014 emitted by SageStore so plugins can react \u2500\u2500\n\nexport interface MemoryRememberedPayload {\n scope: MemoryScope;\n text: string;\n ts: string;\n type?: MemoryType | undefined;\n tags?: string[] | undefined;\n priority?: MemoryPriority | undefined;\n}\n\nexport interface MemoryForgottenPayload {\n scope: MemoryScope;\n query: string;\n removed: number;\n}\n\nexport interface MemoryClearedPayload {\n /** Scope that was cleared, or undefined when all scopes were cleared. */\n scope?: MemoryScope | undefined;\n}\n\nexport interface MemoryConsolidatedPayload {\n scope: MemoryScope;\n /** Entries removed by deduplication. */\n removed: number;\n}\n\n// \u2500\u2500 Relevance scoring \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Context used to score memory relevance for context injection.\n * Passed by the system prompt builder.\n */\nexport interface MemoryRelevanceContext {\n /** Current user message or task description. */\n currentTask: string;\n /** Active skills in this session (e.g. [\"typescript-strict\", \"git-flow\"]). */\n activeSkills?: string[] | undefined;\n /** Active mode (e.g. \"Teach\", \"Brief\", \"Code Reviewer\"). */\n activeMode?: string | undefined;\n /** Available tools \u2014 memories referencing relevant tools score higher. */\n toolNames?: string[] | undefined;\n}\n\nexport interface ScoredEntry extends MemoryEntry {\n score: number;\n matchReason: string;\n}\n\n// \u2500\u2500 Store interface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface MemoryStore {\n readAll(): Promise<string>;\n read(scope: MemoryScope): Promise<string>;\n remember(\n text: string,\n scope?: MemoryScope,\n metadata?: Omit<Partial<MemoryEntry>, 'scope' | 'text' | 'ts'>,\n ): Promise<void>;\n forget(query: string, scope?: MemoryScope): Promise<number>;\n consolidate(scope: MemoryScope): Promise<void>;\n clear(scope?: MemoryScope): Promise<void>;\n /** List entries, newest first. */\n list(scope?: MemoryScope, limit?: number): Promise<MemoryEntry[]>;\n /** Search by content (substring or semantic). */\n search(query: string, scope?: MemoryScope, limit?: number): Promise<MemoryEntry[]>;\n /** Access the backend for advanced queries. */\n getBackend?(): unknown;\n /** Graph-based related memory traversal. */\n findRelated?(text: string, scope?: MemoryScope, limit?: number): Promise<MemoryEntry[]>;\n /**\n * Score and rank memories by relevance to the current context.\n * Returns only entries that meet a relevance threshold.\n */\n scoreRelevant?(\n ctx: MemoryRelevanceContext,\n scope?: MemoryScope,\n limit?: number,\n ): Promise<ScoredEntry[]>;\n /**\n * Run memory hygiene: verify anchors, mark stale entries, archive\n * low-confidence/old memories. Optional \u2014 only SAGE stores\n * implement this. Declared on the interface so callers can invoke\n * it without a type-erasing cast.\n */\n hygiene?(opts?: {\n retentionDays?: number | undefined;\n sessionRetentionDays?: number | undefined;\n archiveLowConfidenceAfterDays?: number | undefined;\n archiveUnusedAfterDays?: number | undefined;\n unusedMinInjections?: number | undefined;\n purgeDeletedAfterDays?: number | undefined;\n nearDedup?: boolean | undefined;\n verify?: boolean | undefined;\n }): Promise<unknown>;\n /**\n * Attach a trace ID to this store so that all subsequent `storage.*`\n * events include it for observability correlation. Mutates the store\n * in place and returns the same instance (convenience chaining).\n */\n withTraceId(traceId: string): MemoryStore;\n}\n\n/**\n * A typed key for an optional memory feature. Implementations expose features\n * through keys instead of forcing hosts to inspect concrete store methods.\n */\nexport interface MemoryCapability<T> {\n readonly id: string;\n /**\n * Type-level brand to prevent structural assignment between capabilities\n * with different generic parameters. Exists at runtime as an identity\n * function (assigned by `defineMemoryCapability`) but is never inspected\n * by any code path \u2014 capability lookup is keyed by `id` only.\n */\n readonly __memoryCapabilityType?: ((value: T) => T) | undefined;\n}\n\nexport function defineMemoryCapability<T>(id: string): MemoryCapability<T> {\n return Object.freeze({\n id,\n /** Identity function that makes the brand a real runtime value so\n * `defineMemoryCapability<A>('x')` and `defineMemoryCapability<B>('x')`\n * produce structurally distinct objects. Capability lookup is still\n * keyed by `id` only \u2014 the brand prevents accidental cross-type casts\n * at the type level. */\n __memoryCapabilityType: (v: T) => v,\n });\n}\n\nexport interface MemoryHealth {\n status: 'ready' | 'degraded' | 'unavailable';\n backend: string;\n details?: Readonly<Record<string, unknown>> | undefined;\n}\n\n/**\n * Host-facing memory boundary. Basic legacy operations remain available during\n * migration; optional graph/admin/SAGE behavior is capability-based.\n */\nexport interface MemoryPort extends MemoryStore {\n initialize(): Promise<void>;\n getCapability<T>(capability: MemoryCapability<T>): T | undefined;\n health(): Promise<MemoryHealth>;\n dispose(): Promise<void>;\n withTraceId(traceId: string): MemoryPort;\n}\n", "import * as fs from 'node:fs/promises';\nimport type { MemoryEntry, MemoryScope } from '../types/memory.js';\nimport { type FileMemoryBackendOptions, FileMemoryBackend } from './memory-backend.js';\nimport type { MemoryBackend } from './memory-backend.js';\n\n// \u2500\u2500 Graph node and edge types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface GraphNode {\n id: string;\n entry: MemoryEntry;\n firstSeen: string;\n count: number;\n /** Extracted metadata for fast lookup. */\n type?: MemoryEntry['type'] | undefined;\n tags?: string[] | undefined;\n priority?: MemoryEntry['priority'] | undefined;\n}\n\ninterface GraphEdge {\n from: string;\n to: string;\n /** Why these nodes are related. */\n relation: 'co_occurring' | 'similar' | 'same_turn' | 'explicit';\n weight: number;\n ts: string;\n}\n\n// \u2500\u2500 Backend \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface GraphMemoryBackendOptions extends FileMemoryBackendOptions {\n /**\n * Path to the graph metadata file (edges + node metadata).\n * Defaults to `<projectDir>/memory-graph.json`.\n */\n graphPath?: string | undefined;\n}\n\n/**\n * @deprecated Migrate to `SageStore` from the SAGE package,\n * which provides the same `MemoryBackend` operations (remember, forget, search,\n * consolidate, list, clear) plus graph support via its integrated\n * `SageGraph` implementation.\n * This legacy backend stores entries as markdown bullets in a flat file with a\n * separate graph.json; SageStore stores structured records in JSONL (or\n * SQLite) with a dedicated edges.jsonl supporting 12 relation types (supersedes,\n * contradicts, about_file, etc.) and mutation-locked append.\n * Migration is NOT drop-in \u2014 migrate to SageStore and use its\n * integrated graph features instead.\n *\n * Graph-based memory backend that tracks relationships between entries.\n * Builds on top of FileMemoryBackend \u2014 entries are still persisted as\n * markdown bullets, but the graph layer adds:\n *\n * - Co-occurrence edges (entries from the same remember() batch)\n * - Content similarity edges (simple Jaccard on word overlap)\n * - Turn-based edges (entries created in the same LLM turn)\n * - Graph traversal queries (find related memories)\n *\n * The graph metadata persists to `<projectDir>/memory-graph.json`.\n */\nexport class GraphMemoryBackend implements MemoryBackend {\n readonly kind = 'graph';\n\n private readonly file: FileMemoryBackend;\n private readonly graphFile: string;\n\n // In-memory graph state \u2014 lazily loaded, saved on mutation.\n private nodes = new Map<string, GraphNode>();\n private edges: GraphEdge[] = [];\n private loadedScope: MemoryScope | null = null;\n private loaded = false;\n\n /**\n * Inverted index for O(1) term \u2192 node lookup during search.\n * Built incrementally on remember/forget, rebuilt on loadGraph if absent.\n * Maps lowercase term (min 3 chars) \u2192 Set of node IDs containing that term.\n */\n private invertedIndex = new Map<string, Set<string>>();\n\n /** Minimum term length to index \u2014 avoids noise from 1-2 char fragments. */\n private static readonly MIN_TERM_LEN = 3;\n\n /**\n * Promise that resolves when the current in-flight _saveGraph completes.\n * Tests call flush() to await this before deleting the backend or its temp dir.\n * Each save operation chains onto the previous one so concurrent saves are serialised.\n */\n private _saveDone: Promise<void> = Promise.resolve();\n\n constructor(opts: GraphMemoryBackendOptions) {\n this.file = new FileMemoryBackend({ paths: opts.paths });\n this.graphFile = opts.graphPath ?? `${opts.paths.projectDir}/memory-graph.json`;\n }\n\n // \u2500\u2500 Backend interface \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async remember(scope: MemoryScope, entry: MemoryEntry, filePath: string): Promise<void> {\n await this.file.remember(scope, entry, filePath);\n await this.loadGraph(scope);\n\n const nodeId = this.nodeId(entry);\n const existing = this.nodes.get(nodeId);\n if (existing) {\n existing.count++;\n existing.entry = entry;\n existing.type = entry.type;\n existing.tags = entry.tags;\n existing.priority = entry.priority;\n // Update inverted index for the modified node's text and tags\n this.updateNodeIndex(nodeId, entry);\n } else {\n this.nodes.set(nodeId, {\n id: nodeId,\n entry,\n firstSeen: entry.ts,\n count: 1,\n type: entry.type,\n tags: entry.tags,\n priority: entry.priority,\n });\n\n // Index the new node in the inverted index\n this.indexNode(nodeId, entry);\n\n // Create similarity edges \u2014 but only against the last SIMILARITY_WINDOW most\n // recent nodes instead of the full in-memory set. This converts O(N\u00B2) on\n // every remember() into O(K) where K is a small constant (100), making\n // remember() O(1) amortized regardless of how many entries are stored.\n const SIMILARITY_WINDOW = 100;\n const recentNodes = [...this.nodes.values()].slice(-SIMILARITY_WINDOW);\n for (const other of recentNodes) {\n if (other.id === nodeId) continue;\n const sim = wordOverlap(entry.text, other.entry.text);\n const tagSim = sharedTags(entry.tags ?? [], other.tags ?? []);\n const weight = Math.max(sim, tagSim * 0.5);\n if (weight > 0.15) {\n this.edges.push({\n from: nodeId,\n to: other.id,\n relation: sim >= tagSim ? 'similar' : 'same_turn',\n weight,\n ts: entry.ts,\n });\n }\n }\n }\n\n // Await the graph save to ensure flush() sees the file on disk before\n // cleanup. Unlike memory-consolidator (LLM call, 15s), this is a fast fs operation\n // and the caller's event loop is already freed by not awaiting file.remember().\n this._saveDone = this._saveGraph(scope);\n await this._saveDone;\n }\n\n async forget(scope: MemoryScope, query: string, filePath: string): Promise<number> {\n const removed = await this.file.forget(scope, query, filePath);\n if (removed > 0) {\n await this.loadGraph(scope);\n // Remove nodes whose entry text matches the query\n const n = query.toLowerCase();\n const toRemove: string[] = [];\n for (const [id, node] of this.nodes) {\n if (node.entry.text.toLowerCase().includes(n)) {\n toRemove.push(id);\n }\n }\n for (const id of toRemove) {\n // Remove from inverted index before deleting the node\n this.removeNodeFromIndex(id, this.nodes.get(id)?.entry);\n this.nodes.delete(id);\n }\n this.edges = this.edges.filter((e) => !toRemove.includes(e.from) && !toRemove.includes(e.to));\n this._saveDone = this._saveGraph(scope);\n await this._saveDone;\n }\n return removed;\n }\n\n async readAll(scope: MemoryScope, filePath: string): Promise<string> {\n return this.file.readAll(scope, filePath);\n }\n\n async list(scope: MemoryScope, filePath: string, limit?: number): Promise<MemoryEntry[]> {\n await this.loadGraph(scope);\n // Merge: file entries are canonical, graph adds metadata and dedup\n const fileEntries = await this.file.list(scope, filePath);\n const nodeMap = new Map(this.nodes.entries());\n\n // Enrich file entries with graph metadata\n const enriched = fileEntries.map((fe) => {\n const nodeId = this.nodeId(fe);\n const node = nodeMap.get(nodeId);\n if (node) {\n return {\n ...fe,\n type: node.type ?? fe.type,\n tags: node.tags ?? fe.tags,\n priority: node.priority ?? fe.priority,\n };\n }\n return fe;\n });\n\n // Add graph-only nodes not in file (shouldn't happen normally, but safety)\n const fileIds = new Set(fileEntries.map((e) => this.nodeId(e)));\n for (const [id, node] of this.nodes) {\n if (!fileIds.has(id)) {\n enriched.push(node.entry);\n }\n }\n\n enriched.sort((a, b) => b.ts.localeCompare(a.ts));\n return limit ? enriched.slice(0, limit) : enriched;\n }\n\n async search(scope: MemoryScope, query: string, _filePath: string, limit?: number): Promise<MemoryEntry[]> {\n await this.loadGraph(scope);\n const needle = query.toLowerCase().split(/\\s+/).filter((t) => t.length >= GraphMemoryBackend.MIN_TERM_LEN);\n\n // Use inverted index to find candidate nodes \u2014 O(K * avg_results) instead of O(N)\n const candidates = new Map<string, number>();\n for (const term of needle) {\n const nodeIds = this.invertedIndex.get(term);\n if (!nodeIds) continue;\n for (const nodeId of nodeIds) {\n const node = this.nodes.get(nodeId);\n if (!node || node.entry.scope !== scope) continue;\n // Count how many terms matched this node\n candidates.set(nodeId, (candidates.get(nodeId) ?? 0) + 1);\n }\n }\n\n // Also include high-priority entries even without lexical matches (priority boost > 0)\n // This preserves the original behavior where priority/count could surface entries\n // that don't match any query terms.\n for (const [nodeId, node] of this.nodes) {\n if (node.entry.scope !== scope) continue;\n if (candidates.has(nodeId)) continue; // Already in candidates\n // Include if priority boost > 0 (critical=3, high=2)\n if (node.priority === 'critical' || node.priority === 'high') {\n candidates.set(nodeId, 0); // 0 lexical matches but priority boost applies\n }\n }\n\n // Score candidates: text match (1pt/term), tag match (2pts/term), priority, count\n const scored: { entry: MemoryEntry; score: number }[] = [];\n for (const [nodeId] of candidates) {\n const node = this.nodes.get(nodeId)!;\n let score = 0;\n // Text match score: 1pt per matching term\n score += (candidates.get(nodeId) ?? 0) * 1;\n // Tag match score: 2pts per term that appears in any tag\n for (const term of needle) {\n if (node.entry.tags?.some((t) => t.toLowerCase().includes(term))) score += 2;\n }\n if (node.priority === 'critical') score += 3;\n else if (node.priority === 'high') score += 2;\n score += node.count * 0.5;\n scored.push({ entry: node.entry, score });\n }\n\n scored.sort((a, b) => b.score - a.score);\n const matched = scored.map((s) => s.entry);\n return limit ? matched.slice(0, limit) : matched;\n }\n\n async clear(scope: MemoryScope, filePath: string): Promise<void> {\n await this.file.clear(scope, filePath);\n this.nodes.clear();\n this.edges = [];\n this.invertedIndex = new Map();\n this.loadedScope = scope;\n this.loaded = true;\n // Write empty graph\n try { await fs.unlink(this.graphFile); } catch { /* ok */ }\n }\n\n async consolidate(scope: MemoryScope, filePath: string): Promise<number> {\n return this.file.consolidate(scope, filePath);\n }\n\n // \u2500\u2500 Graph-specific queries \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Find memories related to the given entry, ordered by edge weight.\n */\n async findRelated(scope: MemoryScope, _filePath: string, entryText: string, limit = 5): Promise<MemoryEntry[]> {\n await this.loadGraph(scope);\n const targetId = this.nodeId({ scope, text: entryText, ts: '' });\n const related = this.edges\n .filter((e) => e.from === targetId || e.to === targetId)\n .sort((a, b) => b.weight - a.weight)\n .slice(0, limit);\n\n const result: MemoryEntry[] = [];\n for (const edge of related) {\n const otherId = edge.from === targetId ? edge.to : edge.from;\n const node = this.nodes.get(otherId);\n if (node) result.push(node.entry);\n }\n return result;\n }\n\n /**\n * Get all edges for visualization or traversal.\n */\n getGraph(): { nodes: GraphNode[]; edges: GraphEdge[] } {\n return {\n nodes: [...this.nodes.values()],\n edges: [...this.edges],\n };\n }\n\n // \u2500\u2500 Persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private nodeId(entry: MemoryEntry): string {\n // Stable ID from scope + normalized text\n const norm = entry.text.toLowerCase().trim().replace(/\\s+/g, ' ');\n return `${entry.scope ?? 'mem'}::${simpleHash(norm)}`;\n }\n\n private async loadGraph(scope: MemoryScope): Promise<void> {\n if (this.loaded && this.loadedScope === scope) return;\n try {\n const raw = await fs.readFile(this.graphFile, 'utf8');\n const data: { nodes: Array<[string, GraphNode]>; edges: GraphEdge[] } = JSON.parse(raw);\n this.nodes = new Map(data.nodes);\n this.edges = data.edges;\n } catch {\n this.nodes = new Map();\n this.edges = [];\n }\n // Build inverted index from loaded nodes\n this.buildInvertedIndex();\n this.loadedScope = scope;\n this.loaded = true;\n }\n\n /** Fire-and-forget graph persistence. Named _saveGraph to signal it must not be awaited. */\n private async _saveGraph(scope: MemoryScope): Promise<void> {\n this.loadedScope = scope;\n this.loaded = true;\n try {\n const data = {\n nodes: [...this.nodes.entries()],\n edges: this.edges,\n };\n const dir = this.graphFile.substring(0, this.graphFile.lastIndexOf('/'));\n await fs.mkdir(dir, { recursive: true });\n // Atomic write via temp file\n const tmp = `${this.graphFile}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(data));\n await fs.rename(tmp, this.graphFile);\n } catch {\n // best-effort \u2014 graph is an enhancement, not critical\n }\n }\n\n /**\n * Wait for all in-flight _saveGraph operations to complete.\n * Call this before deleting the backend or its temp directory.\n */\n async flush(): Promise<void> {\n await this._saveDone;\n }\n\n // \u2500\u2500 Inverted Index Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Build the inverted index from all currently loaded nodes.\n * Called on loadGraph() to reconstruct the index after loading from disk.\n */\n private buildInvertedIndex(): void {\n this.invertedIndex = new Map();\n for (const [nodeId, node] of this.nodes) {\n for (const term of this.extractTerms(node.entry)) {\n let nodeIds = this.invertedIndex.get(term);\n if (!nodeIds) {\n nodeIds = new Set();\n this.invertedIndex.set(term, nodeIds);\n }\n nodeIds.add(nodeId);\n }\n }\n }\n\n /**\n * Index a node in the inverted index. Adds the node's ID to all term entries.\n */\n private indexNode(nodeId: string, entry: MemoryEntry): void {\n for (const term of this.extractTerms(entry)) {\n let nodeIds = this.invertedIndex.get(term);\n if (!nodeIds) {\n nodeIds = new Set();\n this.invertedIndex.set(term, nodeIds);\n }\n nodeIds.add(nodeId);\n }\n }\n\n /**\n * Remove a node's terms from the inverted index before deleting it.\n * Used in forget() and when updating existing nodes.\n */\n private removeNodeFromIndex(nodeId: string, entry?: MemoryEntry): void {\n if (!entry) return;\n for (const term of this.extractTerms(entry)) {\n const nodeIds = this.invertedIndex.get(term);\n if (nodeIds) {\n nodeIds.delete(nodeId);\n if (nodeIds.size === 0) {\n this.invertedIndex.delete(term);\n }\n }\n }\n }\n\n /**\n * Update an existing node's entries in the inverted index.\n * Removes old terms and adds new ones.\n */\n private updateNodeIndex(nodeId: string, entry: MemoryEntry): void {\n // Remove old entries for this node\n const existing = this.nodes.get(nodeId);\n if (existing) {\n this.removeNodeFromIndex(nodeId, existing.entry);\n }\n // Add new entries\n this.indexNode(nodeId, entry);\n }\n\n /**\n * Extract searchable terms from a memory entry.\n * Returns lowercase words from text (min length) and full tag strings.\n */\n private extractTerms(entry: MemoryEntry): string[] {\n const terms: string[] = [];\n // Extract words from text (only terms >= MIN_TERM_LEN to avoid noise)\n const words = entry.text.toLowerCase().split(/\\s+/);\n for (const word of words) {\n if (word.length >= GraphMemoryBackend.MIN_TERM_LEN) {\n terms.push(word);\n }\n }\n // Index full tags as complete strings (not individual words)\n if (entry.tags) {\n for (const tag of entry.tags) {\n const lower = tag.toLowerCase();\n if (lower.length >= GraphMemoryBackend.MIN_TERM_LEN && !terms.includes(lower)) {\n terms.push(lower);\n }\n }\n }\n return terms;\n }\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Jaccard-style word overlap between two strings. 0.0 = no overlap, 1.0 = identical. */\nfunction wordOverlap(a: string, b: string): number {\n const wordsA = new Set(a.toLowerCase().split(/\\s+/).filter((w) => w.length > 2));\n const wordsB = new Set(b.toLowerCase().split(/\\s+/).filter((w) => w.length > 2));\n if (wordsA.size === 0 || wordsB.size === 0) return 0;\n let intersection = 0;\n for (const w of wordsA) {\n if (wordsB.has(w)) intersection++;\n }\n return intersection / Math.max(wordsA.size, wordsB.size);\n}\n\n/** Tag overlap ratio \u2014 how many tags two nodes share. */\nfunction sharedTags(a: string[], b: string[]): number {\n if (a.length === 0 || b.length === 0) return 0;\n const setB = new Set(b);\n let shared = 0;\n for (const t of a) if (setB.has(t)) shared++;\n return shared / Math.max(a.length, b.length);\n}\n\n/** Fast non-crypto hash for stable node IDs. */\nfunction simpleHash(s: string): string {\n let h = 0;\n for (let i = 0; i < s.length; i++) {\n h = ((h << 5) - h + s.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(36);\n}\n", "import * as path from 'node:path';\nimport type { RunResult } from '../core/agent-types.js';\nimport type { Context } from '../core/context.js';\nimport type { OneShotOrchestrator } from '../execution/one-shot-llm.js';\nimport type { AfterRunHook, AgentExtension } from '../extension/extension-points.js';\nimport type { MemoryEntry, MemoryStore } from '../types/memory.js';\nimport type { Provider } from '../types/provider.js';\nimport {\n readBundledInstructionText,\n renderInstructionTemplate,\n} from '../utils/instruction-file.js';\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Minimal interface for writing to SAGE without importing\n * @wrongstack/sage (core cannot depend on sage).\n * Tools like the consolidator use this to route adds through\n * rememberSage rather than the legacy MemoryStore.remember(),\n * making them visible to searchSage/turn-middleware.\n */\ntype ConsolidatorSageKind =\n | 'fact'\n | 'decision'\n | 'convention'\n | 'preference'\n | 'anti_pattern'\n | 'warning'\n | 'workflow'\n | 'bug_root_cause'\n | 'file_note'\n | 'symbol_note'\n | 'command_note';\n\ninterface ConsolidatorMemoryAnchor {\n type: 'file' | 'directory' | 'symbol' | 'package' | 'command' | 'test' | 'git';\n path?: string | undefined;\n symbol?: string | undefined;\n command?: string | undefined;\n}\n\ninterface ConsolidatorSageRecord {\n text: string;\n scope?: string | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n}\n\nexport interface ConsolidatorSage {\n rememberSage(input: {\n text: string;\n scope?: string | undefined;\n kind?: ConsolidatorSageKind | undefined;\n tags?: string[] | undefined;\n priority?: string | undefined;\n importance?: number | undefined;\n confidence?: number | undefined;\n persistence?: 'long_lived' | undefined;\n anchors?: ConsolidatorMemoryAnchor[] | undefined;\n sources?: Array<{ type: string; sessionId?: string | undefined }> | undefined;\n }): Promise<unknown>;\n listSage?(statuses?: Array<'active'>): Promise<unknown[]>;\n searchSage?(\n query: string,\n opts?: { limit?: number; scope?: 'project'; includeStatuses?: Array<'active'> },\n ): Promise<unknown[]>;\n}\n\nfunction isSageRecord(value: unknown): value is ConsolidatorSageRecord {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { text?: unknown }).text === 'string'\n );\n}\n\nfunction toPromptEntries(records: unknown[], limit: number): MemoryEntry[] {\n return records\n .filter(isSageRecord)\n .filter((record) => record.scope === 'project')\n .slice(0, limit)\n .map((record) => ({\n scope: 'project-memory',\n text: record.text,\n ts: record.updatedAt ?? record.createdAt ?? '',\n }));\n}\n\nfunction toSageKind(type: string | undefined): ConsolidatorSageKind {\n switch (type) {\n case 'decision':\n case 'convention':\n case 'preference':\n case 'anti_pattern':\n case 'warning':\n case 'workflow':\n case 'bug_root_cause':\n case 'file_note':\n case 'symbol_note':\n case 'command_note':\n return type;\n case 'reference':\n return 'file_note';\n default:\n return 'fact';\n }\n}\n\nfunction importanceFromPriority(priority: string | undefined): number {\n switch (priority) {\n case 'critical':\n return 0.95;\n case 'high':\n return 0.8;\n case 'medium':\n return 0.55;\n case 'low':\n return 0.25;\n default:\n return 0.6;\n }\n}\n\nexport interface ConsolidationOp {\n /**\n * Add-only by design. The consolidator runs unattended after every\n * successful session, so LLM-issued delete/edit ops used to give an\n * unsupervised model a substring-matched deletion path over the whole\n * memory store (see the 2026-07 mass-deletion postmortem). Removals and\n * corrections belong to explicit review flows (memory_delete /\n * memory_update / ReviewQueue), never to this hook.\n */\n action: 'add';\n /** The fact to remember. */\n text?: string | undefined;\n /** Memory type for categorization. */\n type?: string | undefined;\n /** Tags for grouping. */\n tags?: string[] | undefined;\n /** Priority level. */\n priority?: string | undefined;\n /** Confidence that the statement is durable and supported by the evidence. */\n confidence?: number | undefined;\n /** Concrete retrieval anchors grounded in files, symbols, packages, tests, or commands touched. */\n anchors?: ConsolidatorMemoryAnchor[] | undefined;\n}\n\ninterface ConsolidationResponse {\n operations: ConsolidationOp[];\n summary?: string | undefined;\n}\n\nexport interface MemoryConsolidatorOptions {\n memoryStore: MemoryStore;\n /**\n * Optional SAGE writer. When provided, the consolidator routes\n * adds through `rememberSage()` and reads existing entries for dedup\n * context via `searchSage()` instead of the legacy `MemoryStore`.\n * This makes consolidator-sourced memories visible to the turn-middleware\n * (searchSage, retrieveForPath) which only queries SAGE.\n */\n Sage?: ConsolidatorSage | undefined;\n /**\n * Provider used for the consolidation LLM call. Uses the session's\n * provider by default.\n */\n provider?: Provider | undefined;\n /**\n * Model override for the consolidation call. Uses the session's model\n * by default. A smaller/faster model is recommended (e.g. deepseek-chat, haiku, flash).\n */\n model?: string | undefined;\n /**\n * Minimum session iterations before consolidation fires.\n * Sessions shorter than this are skipped (default 2).\n */\n minIterations?: number | undefined;\n /**\n * Maximum memory entries to include in the prompt as context.\n */\n maxExistingEntries?: number | undefined;\n /**\n * OneShotOrchestrator for the consolidation LLM call. When set, uses\n * it instead of the direct provider.complete() path, gaining fallback\n * chain support and cheap-model defaulting.\n */\n oneShotOrchestrator?: OneShotOrchestrator | undefined;\n}\n\n// \u2500\u2500 Prompt \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction buildConsolidationPrompt(\n finalText: string,\n iterations: number,\n existingEntries: MemoryEntry[],\n evidence: string,\n): string {\n const existingBlock =\n existingEntries.length > 0\n ? `\\n\\nExisting memory entries:\\n${existingEntries\n .map((e) => `- [${e.ts.slice(0, 10)}] ${e.text}`)\n .join('\\n')}`\n : '';\n\n return renderInstructionTemplate(readBundledInstructionText('llm/memory-consolidator.md'), {\n iterations: String(iterations),\n summary: finalText.slice(0, 3000),\n evidence,\n existingEntries: existingBlock,\n });\n}\n\nfunction relativeEvidencePath(projectRoot: string, filePath: string): string {\n const relative = path.relative(projectRoot, filePath);\n return relative && !relative.startsWith('..') && !path.isAbsolute(relative)\n ? relative.replaceAll('\\\\', '/')\n : filePath.replaceAll('\\\\', '/');\n}\n\nfunction safeCommandEvidence(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const command = value.trim().replace(/\\s+/g, ' ').slice(0, 300);\n if (!command) return undefined;\n if (/(?:password|passwd|secret|token|api[_-]?key|authorization|bearer)\\s*[:=]/i.test(command)) {\n return '[redacted sensitive command]';\n }\n return command;\n}\n\nfunction buildSessionEvidence(ctx: Context): string {\n const projectRoot = ctx.projectRoot ?? ctx.cwd ?? '';\n const read = [...(ctx.readFiles ?? [])]\n .slice(-20)\n .map((file) => relativeEvidencePath(projectRoot, file));\n const written = [...(ctx.writtenFiles ?? [])]\n .slice(-20)\n .map((file) => relativeEvidencePath(projectRoot, file));\n const commands = (ctx.sideEffects ?? []).slice(-10).map((effect) => ({\n tool: effect.toolName,\n risk: effect.risk,\n command: safeCommandEvidence(effect.input['command']),\n outcome: effect.outcome?.slice(0, 160),\n }));\n const completedTodos = (ctx.todos ?? [])\n .filter((todo) => todo.status === 'completed')\n .slice(-10)\n .map((todo) => todo.content.slice(0, 240));\n\n return JSON.stringify(\n {\n projectRoot: projectRoot.replaceAll('\\\\', '/'),\n readFiles: read,\n writtenFiles: written,\n commands,\n completedTodos,\n kanban: ctx.currentKanbanTaskId\n ? { boardId: ctx.currentKanbanBoardId, taskId: ctx.currentKanbanTaskId }\n : undefined,\n },\n null,\n 2,\n ).slice(0, 6000);\n}\n\nfunction normalizeConsolidatorAnchors(value: unknown): ConsolidatorMemoryAnchor[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const allowed = new Set<ConsolidatorMemoryAnchor['type']>([\n 'file',\n 'directory',\n 'symbol',\n 'package',\n 'command',\n 'test',\n 'git',\n ]);\n const anchors: ConsolidatorMemoryAnchor[] = [];\n for (const raw of value.slice(0, 5)) {\n if (!raw || typeof raw !== 'object') continue;\n const candidate = raw as Record<string, unknown>;\n if (!allowed.has(candidate['type'] as ConsolidatorMemoryAnchor['type'])) continue;\n const anchor: ConsolidatorMemoryAnchor = {\n type: candidate['type'] as ConsolidatorMemoryAnchor['type'],\n };\n if (typeof candidate['path'] === 'string' && candidate['path'].trim()) {\n anchor.path = candidate['path'].trim().slice(0, 500);\n }\n if (typeof candidate['symbol'] === 'string' && candidate['symbol'].trim()) {\n anchor.symbol = candidate['symbol'].trim().slice(0, 300);\n }\n if (typeof candidate['command'] === 'string' && candidate['command'].trim()) {\n anchor.command = safeCommandEvidence(candidate['command']);\n }\n anchors.push(anchor);\n }\n return anchors.length > 0 ? anchors : undefined;\n}\n\nfunction normalizeConfidence(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.max(0.5, Math.min(1, value))\n : 0.78;\n}\n\n// \u2500\u2500 Consolidator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class SessionMemoryConsolidator implements AgentExtension {\n name = 'session-memory-consolidator';\n owner = 'core';\n\n private readonly memoryStore: MemoryStore;\n private readonly Sage?: ConsolidatorSage | undefined;\n private readonly provider?: Provider | undefined;\n private readonly model?: string | undefined;\n private readonly minIterations: number;\n private readonly maxExistingEntries: number;\n private readonly oneShotOrchestrator?: OneShotOrchestrator | undefined;\n\n constructor(opts: MemoryConsolidatorOptions) {\n this.memoryStore = opts.memoryStore;\n this.Sage = opts.Sage;\n this.provider = opts.provider;\n this.model = opts.model;\n // Every meaningful run is eligible. The LLM still returns an empty\n // operation list when the turn contains no durable project knowledge.\n this.minIterations = opts.minIterations ?? 1;\n this.maxExistingEntries = opts.maxExistingEntries ?? 15;\n this.oneShotOrchestrator = opts.oneShotOrchestrator;\n }\n\n afterRun: AfterRunHook = async (ctx: Context, result: RunResult) => {\n // Only consolidate successful sessions with meaningful output\n if (result.status !== 'done') return;\n if (!result.finalText || result.finalText.trim().length < 20) return;\n if (result.iterations < this.minIterations) return;\n\n const provider = this.provider ?? ctx.provider;\n if (!provider?.complete) return;\n\n // Capture narrowed values for the fire-and-forget closure.\n const _finalText: string = result.finalText;\n const _iterations: number = result.iterations;\n const _model: string | undefined = this.model ?? ctx.model;\n const _sessionId: string = ctx.session.id;\n const _evidence = buildSessionEvidence(ctx);\n\n // Await the write boundary. A fire-and-forget consolidator could be killed\n // when the process/session closed immediately after a response, making\n // supposedly durable memory depend on teardown timing.\n try {\n // Load existing memory through the same backend contract used for writes.\n // SQLite-shaped SAGE stores intentionally do not implement the\n // legacy MemoryStore.list() interface.\n let existingEntries: MemoryEntry[];\n if (this.Sage) {\n const records = this.Sage.listSage\n ? await this.Sage.listSage(['active'])\n : ((await this.Sage.searchSage?.('', {\n limit: this.maxExistingEntries,\n scope: 'project',\n includeStatuses: ['active'],\n })) ?? []);\n existingEntries = toPromptEntries(records, this.maxExistingEntries);\n } else {\n existingEntries = await this.memoryStore.list('project-memory', this.maxExistingEntries);\n }\n const prompt = buildConsolidationPrompt(_finalText, _iterations, existingEntries, _evidence);\n\n // Call the LLM with a focused, one-shot prompt\n let text: string;\n if (this.oneShotOrchestrator) {\n const oneShotResult = await this.oneShotOrchestrator.call({\n system: prompt,\n userPrompt: 'Review the session and return memory operations as JSON.',\n model: _model ?? 'deepseek-chat',\n maxTokens: 500,\n timeoutMs: 15_000,\n });\n text = oneShotResult.text;\n } else {\n // Legacy path: direct provider.complete()\n const signal = AbortSignal.timeout(15_000);\n const response = await provider.complete(\n {\n model: _model,\n system: [{ type: 'text', text: prompt }],\n messages: [\n { role: 'user', content: 'Review the session and return memory operations as JSON.' },\n ],\n maxTokens: 500,\n },\n { signal },\n );\n text = response.content\n .filter((b): b is { type: 'text'; text: string } => b.type === 'text')\n .map((b) => b.text)\n .join('')\n .trim();\n }\n if (!text) return;\n\n // Extract JSON from possible markdown wrapper\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) return;\n\n const parsed: ConsolidationResponse = JSON.parse(jsonMatch[0]);\n if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;\n\n // Apply operations (add-only: anything else the model emits is ignored)\n let added = 0;\n\n for (const op of parsed.operations.slice(0, 5)) {\n try {\n if (!op || typeof op !== 'object' || op.action !== 'add') continue;\n if (typeof op.text !== 'string' || !op.text.trim()) continue;\n const memoryText = op.text.trim();\n\n if (this.Sage) {\n // Translate the legacy prompt enum before crossing the SAGE\n // memory boundary; reference is represented as file_note there.\n await this.Sage.rememberSage({\n text: memoryText,\n scope: 'project',\n kind: toSageKind(op.type),\n tags: op.tags,\n importance: importanceFromPriority(op.priority),\n confidence: normalizeConfidence(op.confidence),\n persistence: 'long_lived',\n anchors: normalizeConsolidatorAnchors(op.anchors),\n sources: [{ type: 'session', sessionId: _sessionId }],\n });\n } else {\n // Legacy path: write to the markdown-based MemoryStore.\n await this.memoryStore.remember(memoryText, undefined, {\n type: op.type as MemoryEntry['type'],\n tags: op.tags,\n priority: op.priority as MemoryEntry['priority'],\n });\n }\n added++;\n } catch {\n // One malformed or backend-rejected item must not discard later\n // valid additions from the same consolidation batch.\n }\n }\n\n if (added > 0) {\n // Log to stderr so it surfaces in the terminal\n process.stderr.write(`[memory] Session consolidation: ${added} added\\n`);\n }\n } catch {\n // Best-effort: extension failures never fail an otherwise successful run.\n }\n };\n}\n", "import type { Config, ConfigStore } from '../types/config.js';\nimport { ConfigError, ERROR_CODES } from '../types/errors.js';\nimport { toErrorMessage } from '../utils/error.js';\n\n\n/**\n * Strip fields that originated from environment variables so they are never\n * persisted back to disk. This prevents an env-sourced secret (e.g.\n * WRONGSTACK_API_KEY) from being accidentally written to a profile config.\n */\nfunction stripEphemeralFields(cfg: Partial<Config>): Partial<Config> {\n const env = (cfg as Partial<Config & { _envSource?: Set<string> | undefined}>)._envSource;\n if (!env?.size) return cfg;\n const out: Partial<Config> = { ...cfg };\n for (const field of env) {\n delete (out as Record<string, unknown>)[field];\n }\n delete (out as Record<string, unknown>)._envSource;\n return out;\n}\n\n/**\n * Reference implementation of `ConfigStore`. Stores a single frozen Config\n * and notifies watchers synchronously on every update. Updates use a deep\n * clone so callers can mutate their `partial` argument freely without\n * tainting state.\n *\n * For the CLI: instantiate once at boot, pass the store (not the Config)\n * to subsystems that care about runtime changes (provider switching,\n * extension reload).\n */\nexport class DefaultConfigStore implements ConfigStore {\n private current: Readonly<Config>;\n private watchers = new Set<(next: Readonly<Config>, prev: Readonly<Config>) => void>();\n\n constructor(initial: Config) {\n this.current = deepFreeze(structuredClone(initial));\n }\n\n get(): Readonly<Config> {\n return this.current;\n }\n\n getSection<K extends keyof Config>(key: K): Readonly<Config[K]> {\n return this.current[key] as Readonly<Config[K]>;\n }\n\n getExtension(pluginName: string): Readonly<Record<string, unknown>> {\n const ext = this.current.extensions?.[pluginName];\n return ext ? (ext as Readonly<Record<string, unknown>>) : FROZEN_EMPTY;\n }\n\n update(partial: Partial<Config>): Readonly<Config> {\n // Strip env-sourced fields before persisting to prevent secrets leaking\n // from in-memory env-derived config values into the on-disk config file.\n const scrubbed = stripEphemeralFields(partial);\n // Shallow merge \u2014 top-level fields replace, nested objects do too unless\n // the caller passes a fully-formed sub-object. That matches the JSON\n // config user mental model (replace `tools.maxIterations` by passing\n // the whole `tools` block, or by patching `extensions.<name>`).\n const next = deepFreeze(structuredClone({ ...this.current, ...scrubbed })) as Readonly<Config>;\n\n if (next.version !== 1) {\n throw new ConfigError({\n message: `ConfigStore.update: version must remain 1, got ${String(next.version)}`,\n code: ERROR_CODES.CONFIG_INVALID,\n context: { field: 'version', actual: next.version },\n });\n }\n\n const prev = this.current;\n this.current = next;\n // Notify watchers AFTER mutating `current` so re-entrant watcher reads\n // see the new state. Watcher exceptions are caught individually so one\n // misbehaving subscriber can't block the others.\n for (const w of this.watchers) {\n try {\n w(next, prev);\n } catch (err) {\n // A plugin watcher that crashes on /model switch or similar would\n // otherwise leave the system in a quietly-inconsistent state. We\n // still don't propagate (one bad subscriber must not break the\n // others), but we surface the error so it's discoverable.\n console.error(JSON.stringify({\n level: 'error',\n event: 'config_store.watcher_threw',\n message: toErrorMessage(err),\n timestamp: new Date().toISOString(),\n }));\n }\n }\n return next;\n }\n\n watch(cb: (next: Readonly<Config>, prev: Readonly<Config>) => void): () => void {\n this.watchers.add(cb);\n return () => this.watchers.delete(cb);\n }\n}\n\nconst FROZEN_EMPTY: Readonly<Record<string, unknown>> = Object.freeze({});\n\nfunction deepFreeze<T>(obj: T): T {\n /* v8 ignore start -- defensive: callers (and the recursion guard below) only pass unfrozen objects */\n if (obj === null || typeof obj !== 'object') return obj;\n if (Object.isFrozen(obj)) return obj;\n /* v8 ignore stop */\n for (const key of Object.keys(obj as object)) {\n const v = (obj as Record<string, unknown>)[key];\n if (v !== null && typeof v === 'object' && !Object.isFrozen(v)) {\n deepFreeze(v);\n }\n }\n return Object.freeze(obj);\n}\n", "/**\n * provider-config-watcher \u2014 live hot-reload of provider credentials.\n *\n * Provider API keys are read from `config.json` once at boot and baked into\n * the constructed `Provider`. When a user adds/removes/replaces a key in\n * another process (a second terminal running `wstack auth`, or either WebUI\n * provider panel), a *running* session would otherwise keep using the\n * boot-time key until restart.\n *\n * This watcher closes that gap: it tails the global `config.json`, re-reads\n * and decrypts its `providers` map on change, and hands a fresh snapshot to\n * the caller, who updates in-memory config and rebuilds the active provider.\n *\n * It mirrors {@link module:hq/auth-store.watchHqAuthFile}:\n * - Watches the *directory* (not the file) so atomic tmp+rename writes\n * (see {@link module:utils/atomic-write.atomicWrite}) surface reliably.\n * - Debounces events (most writers do a tmp+rename dance emitting several).\n * - Reads leniently \u2014 a missing/corrupt/torn file yields a warning, never a\n * throw, and the watcher stays live for the next valid write.\n *\n * A `lastSerialized` guard suppresses the callback when the on-disk providers\n * map is byte-for-byte identical to the last one we surfaced. This makes the\n * watcher a no-op for self-induced writes (e.g. the same process's `/model`\n * switch or a WebUI key save), so there is no rebuild storm.\n *\n * `fs.watch` is best-effort across platforms; on some network filesystems\n * events may not fire and the operator must restart to pick up changes.\n *\n * @module storage/provider-config-watcher\n */\nimport * as syncFs from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport { decryptConfigSecrets } from '../security/config-secrets.js';\nimport type { ProviderConfig } from '../types/config.js';\nimport type { SecretVault } from '../types/secret-vault.js';\nimport { normalizeInlineProviderModels } from './config-loader/inline-provider-models.js';\n\n/**\n * A decrypted snapshot of the credential-bearing slice of `config.json`.\n * `providers` is always present (empty object when the file has none);\n * `apiKey`/`baseUrl` are the top-level fallbacks a provider inherits when its\n * saved entry omits them.\n */\nexport interface ProviderConfigSnapshot {\n providers: Record<string, ProviderConfig>;\n /** True when the config file actually had a `providers` key. The watcher\n * callback uses this to avoid clearing in-memory providers during hot-reload\n * when the profile config file simply doesn't define any \u2014 without this\n * guard, reading a profile config that has no `providers` key would set\n * `appConfig.providers = {}` and wipe all configured providers. */\n snapshotHasProviders: boolean;\n apiKey?: string;\n baseUrl?: string;\n /** Display language (Config.uiLocale), surfaced so a cross-process change\n * (e.g. the desktop shell) propagates to running webui instances. */\n uiLocale?: string;\n /** Live routing fields \u2014 surfaced so a cross-process WebUI /setmodel or\n * profile-reorder propagates to running workers without restart. */\n fallbackModels?: string[];\n fallbackProfiles?: Record<string, string[]>;\n favoriteModels?: string[];\n favoriteModelsOnly?: boolean;\n modelMatrix?: Record<string, unknown>;\n fallbackAuto?: boolean;\n modelAvailabilitySchedule?: import('../core/model-availability-calendar.js').ModelBlackoutRule[];\n}\n\nexport interface WatchProviderConfigOptions {\n /** Surface non-fatal read/parse/decrypt issues. */\n warn?: (msg: string) => void;\n /** Coalesce bursts of fs events (default 200ms). */\n debounceMs?: number;\n}\n\n/**\n * Read and decrypt the credential slice of a config file. Returns `undefined`\n * when the file is missing or unparseable (the `warn` callback carries the\n * detail); never throws for routine I/O.\n *\n * Exported so multi-layer watchers (e.g. `provider-runtime-setup.ts`) can\n * re-read every active config layer when any layer changes, then merge them\n * with correct precedence before applying.\n */\nexport async function readProviderSnapshot(\n configPath: string,\n vault: SecretVault,\n warn?: (msg: string) => void,\n): Promise<ProviderConfigSnapshot | undefined> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n warn?.(`Could not read ${configPath}: ${(err as Error).message}`);\n }\n return undefined;\n }\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n // A torn read mid-rename can surface as invalid JSON; treat as transient.\n warn?.(`Config at ${configPath} is not valid JSON: ${(err as Error).message}`);\n return undefined;\n }\n normalizeInlineProviderModels(parsed, (message, context) => {\n const suffix = context ? ` (${JSON.stringify(context)})` : '';\n warn?.(`${message}${suffix}`);\n });\n const decrypted = decryptConfigSecrets(parsed, vault, warn ? { warn } : {}) as {\n providers?: Record<string, ProviderConfig>;\n apiKey?: string;\n baseUrl?: string;\n uiLocale?: string;\n fallbackModels?: string[];\n fallbackProfiles?: Record<string, string[]>;\n favoriteModels?: string[];\n favoriteModelsOnly?: boolean;\n modelMatrix?: Record<string, unknown>;\n fallbackAuto?: boolean;\n modelAvailabilitySchedule?: import('../core/model-availability-calendar.js').ModelBlackoutRule[];\n };\n const snapshot: ProviderConfigSnapshot = {\n providers: decrypted.providers ?? {},\n snapshotHasProviders: decrypted.providers !== undefined,\n };\n if (typeof decrypted.apiKey === 'string') snapshot.apiKey = decrypted.apiKey;\n if (typeof decrypted.baseUrl === 'string') snapshot.baseUrl = decrypted.baseUrl;\n if (typeof decrypted.uiLocale === 'string' && decrypted.uiLocale)\n snapshot.uiLocale = decrypted.uiLocale;\n if (Array.isArray(decrypted.fallbackModels)) snapshot.fallbackModels = decrypted.fallbackModels;\n if (decrypted.fallbackProfiles) snapshot.fallbackProfiles = decrypted.fallbackProfiles;\n if (Array.isArray(decrypted.favoriteModels)) snapshot.favoriteModels = decrypted.favoriteModels;\n if (typeof decrypted.favoriteModelsOnly === 'boolean')\n snapshot.favoriteModelsOnly = decrypted.favoriteModelsOnly;\n if (decrypted.modelMatrix) snapshot.modelMatrix = decrypted.modelMatrix;\n if (typeof decrypted.fallbackAuto === 'boolean') snapshot.fallbackAuto = decrypted.fallbackAuto;\n if (Array.isArray(decrypted.modelAvailabilitySchedule))\n snapshot.modelAvailabilitySchedule = decrypted.modelAvailabilitySchedule;\n return snapshot;\n}\n\n/** Stable serialization for the no-op guard (key order independent). */\nfunction serializeSnapshot(s: ProviderConfigSnapshot): string {\n return JSON.stringify({\n providers: s.providers,\n apiKey: s.apiKey ?? null,\n baseUrl: s.baseUrl ?? null,\n uiLocale: s.uiLocale ?? null,\n fallbackModels: s.fallbackModels ?? null,\n fallbackProfiles: s.fallbackProfiles ?? null,\n favoriteModels: s.favoriteModels ?? null,\n favoriteModelsOnly: s.favoriteModelsOnly ?? null,\n modelMatrix: s.modelMatrix ?? null,\n fallbackAuto: s.fallbackAuto ?? null,\n modelAvailabilitySchedule: s.modelAvailabilitySchedule ?? null,\n });\n}\n\n/**\n * Watch `configPath` for credential changes and invoke `onChange` with a\n * freshly-decrypted snapshot whenever the `providers`/`apiKey`/`baseUrl` slice\n * actually changes. Returns a `close()` that stops watching.\n *\n * The initial on-disk state is read once to seed the no-op guard but does NOT\n * fire `onChange` \u2014 only subsequent changes do.\n */\nexport function watchProviderConfig(\n configPath: string,\n vault: SecretVault,\n onChange: (snapshot: ProviderConfigSnapshot) => void,\n opts: WatchProviderConfigOptions = {},\n): { close: () => void } {\n const debounceMs = opts.debounceMs ?? 200;\n const warn = opts.warn;\n const base = path.basename(configPath);\n let timer: ReturnType<typeof setTimeout> | undefined;\n let closed = false;\n let lastSerialized: string | undefined;\n\n // Seed the guard with current on-disk content so the first *change* fires\n // rather than re-applying what we already have.\n void readProviderSnapshot(configPath, vault, warn).then((seed) => {\n if (!closed && seed && lastSerialized === undefined) {\n lastSerialized = serializeSnapshot(seed);\n }\n });\n\n let watcher: syncFs.FSWatcher;\n try {\n // Watch the directory (not the file) so atomic-rename events surface\n // reliably across platforms.\n watcher = syncFs.watch(path.dirname(configPath), { recursive: false });\n } catch (err) {\n warn?.(`Provider config watcher could not start: ${(err as Error).message}`);\n return { close: () => {} };\n }\n\n const trigger = (): void => {\n if (closed) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n void readProviderSnapshot(configPath, vault, warn).then(\n (next) => {\n if (closed || !next) return;\n const serialized = serializeSnapshot(next);\n if (serialized === lastSerialized) return; // no-op / self-write\n lastSerialized = serialized;\n onChange(next);\n },\n () => {\n // readProviderSnapshot never rejects for routine I/O \u2014 guard only.\n },\n );\n }, debounceMs);\n };\n\n watcher.on('change', (eventType: string, filename: string | Buffer | null) => {\n const name = typeof filename === 'string' ? filename : '';\n if (eventType === 'rename' || eventType === 'change') {\n if (!name || name === base) trigger();\n }\n });\n\n watcher.on('error', (err: Error) => {\n warn?.(`Provider config watcher error: ${err.message}`);\n });\n\n return {\n close: () => {\n closed = true;\n if (timer) clearTimeout(timer);\n watcher.close();\n },\n };\n}\n", "import type { SecretVault } from '../types/secret-vault.js';\n\n/**\n * Config-walking secret helpers, split out of secret-vault.ts so that\n * consumers which only need the walk logic (config loader, provider-config\n * watcher) don't pull the AES-GCM vault implementation \u2014 and its node:crypto\n * imports \u2014 into their bundle chunk. Everything here operates purely through\n * the `SecretVault` interface.\n */\n\n/**\n * Walk a Config-shaped object and decrypt any apiKey-like fields in place,\n * returning a new object. Used by the config loader so the rest of the\n * system never has to know about the wire format.\n *\n * @param warn \u2014 callback for decryption warnings. Defaults to `console.warn`\n * for backward compatibility; pass `logger.warn` when a structured logger\n * is available (preferred in long-running/server contexts).\n */\nexport function decryptConfigSecrets<T>(\n cfg: T,\n vault: SecretVault,\n opts?: { warn?: (msg: string) => void },\n): T {\n const warn = opts?.warn ?? ((msg: string) => console.warn(msg));\n // A single corrupted/malformed encrypted field should not kill the entire\n // config load. Swallow per-field decrypt errors (zero the field so callers\n // see \"missing key\" instead of holding ciphertext) and surface a warning.\n return walk(cfg, vault, (v, key) => {\n try {\n return vault.decrypt(v);\n } catch (err) {\n warn(\n `[secret-vault] Failed to decrypt \"${key}\": ${err instanceof Error ? err.message : err}`,\n );\n return '';\n }\n });\n}\n\nexport function encryptConfigSecrets<T>(\n cfg: T,\n vault: SecretVault,\n _opts?: { warn?: (msg: string) => void },\n): T {\n return walk(cfg, vault, (v) => vault.encrypt(v));\n}\n\nfunction walk<T>(node: T, vault: SecretVault, transform: (s: string, key: string) => string): T {\n if (node === null || node === undefined) return node;\n if (typeof node !== 'object') return node;\n if (Array.isArray(node)) {\n return node.map((item) => walk(item, vault, transform)) as never as T;\n }\n const out: Record<string, unknown> = Object.create(null);\n for (const [k, v] of Object.entries(node as Record<string, unknown>)) {\n if (typeof v === 'string' && isSecretField(k)) {\n out[k] = transform(v, k);\n } else if (typeof v === 'object' && v !== null) {\n out[k] = walk(v, vault, transform);\n } else {\n out[k] = v;\n }\n }\n return out as T;\n}\n\n/**\n * A key is treated as secret-bearing if its name (case-insensitive) contains\n * one of these tokens. Captures common variants like `apiKey`, `authToken`,\n * `refreshToken`, `sessionKey`, `password`, `client_secret`, `bearer`, etc.\n * Use a named field with `isSecret: false` annotation if you must opt out \u2014\n * see `NON_SECRET_OVERRIDES` below.\n */\nconst SECRET_KEY_PATTERN =\n /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key|token\\b)/i;\n\n// Field names that contain the literal substring \"key\" but are not secrets.\n// Keep this list short; the substring rule itself is intentionally narrow.\nconst NON_SECRET_OVERRIDES = new Set(['publickey', 'public_key']);\n\nexport function isSecretField(name: string): boolean {\n const lc = name.toLowerCase();\n if (NON_SECRET_OVERRIDES.has(lc)) return false;\n return SECRET_KEY_PATTERN.test(lc);\n}\n", "import type { CustomModelDefinition } from '../../types/config.js';\nimport type { Capabilities } from '../../types/provider.js';\nimport { FORBIDDEN_PROTO_KEYS } from '../../utils/deep-merge.js';\n\nexport type InlineProviderModelWarning = (\n message: string,\n context?: Record<string, unknown>,\n) => void;\n\nconst BOOLEAN_CAPABILITIES = [\n 'tools',\n 'parallelTools',\n 'vision',\n 'streaming',\n 'promptCache',\n 'systemPrompt',\n 'jsonMode',\n 'reasoning',\n 'topK',\n 'frequencyPenalty',\n 'presencePenalty',\n 'seed',\n 'structuredOutput',\n 'logprobs',\n 'audio',\n 'multipleCompletions',\n] as const satisfies readonly (keyof Capabilities)[];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction positiveNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : undefined;\n}\n\nfunction readCapabilities(model: Record<string, unknown>): Partial<Capabilities> | undefined {\n const raw = isRecord(model['capabilities']) ? model['capabilities'] : undefined;\n const capabilities: Partial<Capabilities> = {};\n\n if (raw) {\n for (const key of BOOLEAN_CAPABILITIES) {\n const value = raw[key];\n if (typeof value === 'boolean') capabilities[key] = value;\n }\n const cacheControl = raw['cacheControl'];\n if (cacheControl === 'native' || cacheControl === 'auto' || cacheControl === 'none') {\n capabilities.cacheControl = cacheControl;\n }\n }\n\n const limit = isRecord(model['limit']) ? model['limit'] : undefined;\n const maxContext =\n positiveNumber(raw?.['maxContext']) ??\n positiveNumber(model['maxContext']) ??\n positiveNumber(model['contextWindow']) ??\n positiveNumber(limit?.['context']);\n if (maxContext !== undefined) capabilities.maxContext = maxContext;\n\n const capabilityMaxOutput = positiveNumber(raw?.['maxOutput']);\n if (capabilityMaxOutput !== undefined) capabilities.maxOutput = capabilityMaxOutput;\n\n if (typeof model['tool_call'] === 'boolean' && capabilities.tools === undefined) {\n capabilities.tools = model['tool_call'];\n }\n if (typeof model['reasoning'] === 'boolean' && capabilities.reasoning === undefined) {\n capabilities.reasoning = model['reasoning'];\n }\n\n const modalities = isRecord(model['modalities']) ? model['modalities'] : undefined;\n const inputModalities = modalities?.['input'];\n if (Array.isArray(inputModalities) && capabilities.vision === undefined) {\n capabilities.vision = inputModalities.includes('image');\n }\n\n return Object.keys(capabilities).length > 0 ? capabilities : undefined;\n}\n\nfunction inlineDefinition(model: Record<string, unknown>): CustomModelDefinition {\n const definition: CustomModelDefinition = {};\n const name = model['name'];\n if (typeof name === 'string' && name.trim().length > 0) definition.name = name;\n\n const provider = model['provider'];\n if (typeof provider === 'string' && provider.trim().length > 0) {\n definition.provider = provider;\n }\n\n const limit = isRecord(model['limit']) ? model['limit'] : undefined;\n const maxOutput =\n positiveNumber(model['maxOutput']) ??\n positiveNumber(limit?.['output']) ??\n positiveNumber(\n isRecord(model['capabilities']) ? model['capabilities']['maxOutput'] : undefined,\n );\n if (maxOutput !== undefined) definition.maxOutput = maxOutput;\n\n const capabilities = readCapabilities(model);\n if (capabilities) definition.capabilities = capabilities;\n return definition;\n}\n\nfunction mergeDefinitions(\n base: CustomModelDefinition | undefined,\n patch: CustomModelDefinition,\n): CustomModelDefinition {\n return {\n ...base,\n ...patch,\n ...(base?.capabilities || patch.capabilities\n ? { capabilities: { ...base?.capabilities, ...patch.capabilities } }\n : {}),\n };\n}\n\n/**\n * Normalize model.dev-style objects accepted in `providers.<id>.models` into\n * the canonical runtime representation: a string model allowlist plus the\n * provider-local `customModels` metadata map.\n *\n * The function mutates only the provider entries it normalizes. Explicit\n * `customModels` values win field-by-field over metadata derived from inline\n * objects, preserving the existing configuration override contract.\n */\nexport function normalizeInlineProviderModels(\n config: Record<string, unknown>,\n warn?: InlineProviderModelWarning,\n): void {\n const providers = config['providers'];\n if (!isRecord(providers)) return;\n\n for (const [providerId, rawProvider] of Object.entries(providers)) {\n if (!isRecord(rawProvider) || !Array.isArray(rawProvider['models'])) continue;\n\n const ids: string[] = [];\n const seen = new Set<string>();\n const derived = new Map<string, CustomModelDefinition>();\n\n for (const [index, entry] of rawProvider['models'].entries()) {\n let id: string | undefined;\n let definition: CustomModelDefinition | undefined;\n\n if (typeof entry === 'string' && entry.length > 0) {\n id = entry;\n } else if (isRecord(entry)) {\n const rawId = entry['id'];\n if (typeof rawId === 'string' && rawId.trim().length > 0) {\n id = rawId.trim();\n if (!FORBIDDEN_PROTO_KEYS.has(id)) definition = inlineDefinition(entry);\n }\n }\n\n if (!id || FORBIDDEN_PROTO_KEYS.has(id)) {\n warn?.('Ignoring invalid inline provider model entry', {\n event: 'config.invalid_inline_provider_model',\n provider: providerId,\n index,\n });\n continue;\n }\n\n if (!seen.has(id)) {\n seen.add(id);\n ids.push(id);\n }\n if (definition) derived.set(id, mergeDefinitions(derived.get(id), definition));\n }\n\n rawProvider['models'] = ids;\n if (derived.size === 0) continue;\n\n const explicit = isRecord(rawProvider['customModels'])\n ? rawProvider['customModels']\n : undefined;\n const normalized: Record<string, CustomModelDefinition> = Object.create(null) as Record<\n string,\n CustomModelDefinition\n >;\n for (const [id, definition] of derived) normalized[id] = definition;\n if (explicit) {\n for (const [id, value] of Object.entries(explicit)) {\n if (FORBIDDEN_PROTO_KEYS.has(id) || !isRecord(value)) continue;\n normalized[id] = mergeDefinitions(normalized[id], value as CustomModelDefinition);\n }\n }\n rawProvider['customModels'] = normalized;\n }\n}\n", "import * as fs from 'node:fs/promises';\nimport type { EventBus } from '../kernel/events.js';\nimport { decryptConfigSecrets } from '../security/config-secrets.js';\nimport type { Config, ConfigLoader, SyncConfig } from '../types/config.js';\nimport {\n DEFAULT_CONTEXT_WINDOW_MODE_ID,\n isContextWindowModeSelectionId,\n listContextWindowModes,\n normalizeContextWindowModeId,\n} from '../types/context-window.js';\nimport { ConfigError, ERROR_CODES } from '../types/errors.js';\nimport type { Logger } from '../types/logger.js';\nimport type { SecretVault } from '../types/secret-vault.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport { backupConfigFile } from '../utils/config-backup.js';\nimport { type DeepMergeOptions, deepMerge as deepMergeCore } from '../utils/deep-merge.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { safeParse } from '../utils/safe-json.js';\nimport type { WstackPaths } from '../utils/wstack-paths.js';\nimport { safeProfileName } from '../utils/wstack-paths.js';\nimport { isBootstrapOnly } from './config-loader/bootstrap.js';\nimport { fillMissingDefaults, isPlainRecord } from './config-loader/default-repair.js';\nimport { CONFIG_BEHAVIOR_DEFAULTS } from './config-loader/defaults.js';\nimport { storageErrorString } from './config-loader/diagnostics.js';\nimport { ENV_MAP, envBoolOptional, type PartialConfig } from './config-loader/env-overrides.js';\nimport { stripUnsafeInProjectFields } from './config-loader/in-project-policy.js';\nimport { normalizeInlineProviderModels } from './config-loader/inline-provider-models.js';\nimport {\n migrateLegacySuperMemoryKey,\n removeLegacySageEngine,\n} from './config-loader/legacy-sage-migration.js';\nimport { samePath } from './config-loader/path-identity.js';\nimport type {\n ConfigLoaderOptions,\n ConfigSource,\n MemoizedConfigSource,\n} from './config-loader/types.js';\n\nexport {\n type ConfigDefaultRepair,\n type ConfigDefaultRepairReport,\n fillMissingDefaults,\n repairConfigDefaults,\n} from './config-loader/default-repair.js';\nexport { CONFIG_BEHAVIOR_DEFAULTS } from './config-loader/defaults.js';\nexport {\n assertInProjectAllowListComplete,\n stripUnsafeInProjectFields,\n} from './config-loader/in-project-policy.js';\nexport type {\n ConfigLoaderOptions,\n ConfigSource,\n MemoizedConfigSource,\n} from './config-loader/types.js';\n\n/**\n * Config-layer deep merge \u2014 delegates to the shared utility with\n * `arrayMode: 'concat-primitives'` and optional debug logging for\n * non-primitive array replacements.\n */\nfunction deepMerge<T, TPatch extends object>(base: T, patch: TPatch): T {\n const opts: DeepMergeOptions = { arrayMode: 'concat-primitives' };\n if (envBoolOptional(process.env.WRONGSTACK_DEBUG_CONFIG)) {\n opts.onNonPrimitiveArrayReplace = (key, existingLen, patchLen) => {\n console.warn(\n `[config] Non-primitive array for \"${key}\" replaced (global + local config merge). ` +\n `Global entries: ${existingLen}, local entries: ${patchLen}.`,\n );\n };\n }\n return deepMergeCore(\n base as Record<string, unknown>,\n patch as Record<string, unknown>,\n opts,\n ) as T;\n}\n\nexport class DefaultConfigLoader implements ConfigLoader {\n private readonly paths: WstackPaths;\n private readonly strict: boolean;\n private readonly vault: SecretVault | undefined;\n private readonly extraSources: ConfigSource[];\n private readonly events: EventBus | undefined;\n private readonly traceId: string | undefined;\n private readonly logger: Logger | undefined;\n private readonly jsonCache = new Map<string, MemoizedConfigSource>();\n\n constructor(opts: ConfigLoaderOptions) {\n this.paths = opts.paths;\n this.strict = opts.strict ?? false;\n this.vault = opts.vault;\n this.extraSources = opts.sources ?? [];\n this.events = opts.events;\n this.traceId = opts.traceId;\n this.logger = opts.logger;\n }\n\n /**\n * Emit a structured warning. Uses the configured Logger when available;\n * falls back to console.warn(JSON) so warnings are never silently dropped\n * during early boot (before a Logger is constructed).\n */\n private logWarn(msg: string, ctx?: Record<string, unknown>): void {\n if (this.logger) {\n this.logger.warn(msg, ctx);\n } else {\n console.warn(JSON.stringify({ ...ctx, message: msg, timestamp: new Date().toISOString() }));\n }\n }\n\n async load(\n opts: {\n cliFlags?: Partial<Config> | undefined;\n cwd?: string | undefined;\n skipIdentityValidation?: boolean;\n } = {},\n ): Promise<Config> {\n let cfg: PartialConfig = { ...CONFIG_BEHAVIOR_DEFAULTS } as PartialConfig;\n\n // Materialize behavior/settings defaults into the active profile before\n // env vars or CLI flags are applied. The root config remains bootstrap-only.\n await this.ensureGlobalDefaults();\n\n // Layer 2, 3 (profile), 4 & 4b: global bootstrap + profile + project-local +\n // in-project config. The global bootstrap (~/.wrongstack/config.json) is now\n // a thin file containing just version and activeProfile. User settings live in the\n // active profile's config: ~/.wrongstack/profiles/<name>/config.json.\n //\n // inProjectConfig (<project>/.wrongstack/config.json) merges AFTER\n // projectLocalConfig so it takes priority (user-intended > auto-cached).\n //\n // When the project root *is* the user's home (e.g. launching from `~`),\n // `<projectRoot>/.wrongstack/config.json` resolves to the very same file as\n // the trusted global bootstrap. Reading it again as the untrusted in-project\n // layer would strip `provider`/`apiKey`/\u2026 from the user's *own* file and\n // emit a spurious `config.in_project_unsafe_fields_ignored` warning \u2014 even\n // though the trusted global layer already merged those fields. Skip the\n // in-project read entirely on collision so trust isn't applied to a file\n // the user fully controls.\n const inProjectCollides =\n samePath(this.paths.inProjectConfig, this.paths.globalConfig) ||\n samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);\n const [bootstrap, local, inProject] = await Promise.all([\n this.readJson(this.paths.globalConfig),\n this.readJson(this.paths.projectLocalConfig),\n inProjectCollides\n ? Promise.resolve({} as PartialConfig)\n : this.readJson(this.paths.inProjectConfig),\n ]);\n // The root config is bootstrap metadata, never a settings layer. Even if\n // another process accidentally writes settings into it, only these two\n // keys may affect the runtime. Legacy settings are migrated separately,\n // and only while the selected profile file does not yet exist.\n const bootstrapMetadata = {\n ...(typeof (bootstrap as { version?: unknown }).version === 'number'\n ? { version: (bootstrap as { version: number }).version }\n : {}),\n ...(typeof (bootstrap as { activeProfile?: unknown }).activeProfile === 'string'\n ? { activeProfile: safeProfileName((bootstrap as { activeProfile: string }).activeProfile) }\n : {}),\n } as PartialConfig;\n cfg = deepMerge(cfg, bootstrapMetadata);\n\n // Layer 2b: active profile config.\n // Extract the active profile name from the bootstrap config (default: 'default').\n const profileName = safeProfileName(\n (bootstrap as { activeProfile?: string | undefined }).activeProfile,\n );\n const profileCfg = await this.readJson(this.paths.profileConfig(profileName));\n if (Object.keys(profileCfg).length > 0) {\n // Bootstrap metadata is authoritative only in the root config. Ignore\n // stale copies in profile files so a profile cannot select another profile.\n const profileSettings = { ...profileCfg } as Record<string, unknown>;\n delete profileSettings['version'];\n delete profileSettings['activeProfile'];\n cfg = deepMerge(cfg, profileSettings as PartialConfig);\n }\n\n // Bootstrap metadata is authoritative only in the root config. A stale\n // project-private override must not make later persistence target a\n // different profile than the one whose settings were loaded above.\n const localSettings = { ...local } as Record<string, unknown>;\n delete localSettings['version'];\n delete localSettings['activeProfile'];\n cfg = deepMerge(cfg, localSettings as PartialConfig);\n\n // The in-project config is repo-committed and therefore attacker-\n // controllable. Strip credential/endpoint/code-execution fields before\n // merging so a malicious repo cannot redirect the provider endpoint\n // (API-key exfiltration) or auto-run an MCP server / hook (RCE) on launch.\n cfg = deepMerge(\n cfg,\n stripUnsafeInProjectFields(\n inProject,\n this.paths.inProjectConfig,\n this.logger ? (msg: string) => this.logger!.warn(msg) : undefined,\n ),\n );\n\n // Layer 4: env vars\n for (const [key, fn] of Object.entries(ENV_MAP)) {\n const v = process.env[key];\n if (v) fn(cfg, v);\n }\n\n // Layer 5: extra sources \u2014 sorted by priority (lowest first).\n // When priorities tie, sort by name for deterministic order.\n const sorted = [...this.extraSources].sort((a, b) => {\n const pd = (a.priority ?? 50) - (b.priority ?? 50);\n if (pd !== 0) return pd;\n return a.name.localeCompare(b.name);\n });\n for (const src of sorted) {\n try {\n const patch = await src.read();\n if (patch && Object.keys(patch).length > 0) {\n cfg = deepMerge(cfg, patch);\n }\n } catch (err) {\n // Best-effort: skip failing sources so one bad source doesn't block boot.\n this.logWarn('Config source load failed', {\n event: 'config.source_load_failed',\n source: src.name,\n message: toErrorMessage(err),\n });\n }\n }\n\n // Layer 6: CLI flags\n if (opts.cliFlags) {\n cfg = deepMerge(cfg, opts.cliFlags);\n }\n\n // Normalize model.dev-style inline provider model objects only after all\n // config layers and CLI flags have merged. Downstream routing keeps its\n // canonical `models: string[]` contract; metadata lands in `customModels`.\n normalizeInlineProviderModels(cfg as Record<string, unknown>, (message, context) =>\n this.logWarn(message, context),\n );\n\n // Decrypt apiKey-like fields if a vault is configured.\n if (this.vault) {\n cfg = decryptConfigSecrets(cfg, this.vault);\n }\n\n // Multi-key resolution: when a provider has `apiKeys[]` configured,\n // mirror the active entry into `apiKey` so downstream construction\n // code (provider registry, wire adapters) needs no changes. Honors\n // `activeKey` (by label), else falls back to the first entry. A\n // pre-existing `apiKey` set by env/CLI flags wins so an explicit\n // override still beats the saved list.\n if (cfg.providers) {\n for (const pcfg of Object.values(cfg.providers)) {\n if (!pcfg || typeof pcfg !== 'object') continue;\n const rawKeys = (pcfg as { apiKeys?: unknown | undefined }).apiKeys;\n if (!Array.isArray(rawKeys) || rawKeys.length === 0) continue;\n // Each apiKeys entry came from arbitrary JSON. Filter to entries\n // that actually have a string apiKey + label so a malformed array\n // (null entry, missing field) doesn't crash the .find / chosen.apiKey\n // path below.\n const keys = rawKeys.filter(\n (k): k is { label: string; apiKey: string } =>\n !!k &&\n typeof k === 'object' &&\n typeof (k as { label?: unknown | undefined }).label === 'string' &&\n typeof (k as { apiKey?: unknown | undefined }).apiKey === 'string',\n );\n if (keys.length === 0) continue;\n const existing = (pcfg as { apiKey?: string | undefined }).apiKey;\n if (existing && existing.length > 0) continue;\n const activeLabel = (pcfg as { activeKey?: string | undefined }).activeKey;\n const chosen = activeLabel\n ? (keys.find((k) => k.label === activeLabel) ?? keys[0])\n : keys[0];\n if (chosen?.apiKey) {\n (pcfg as { apiKey?: string | undefined }).apiKey = chosen.apiKey;\n }\n }\n }\n\n // `Sage.storage.engine` used to select JSONL. SQLite is now the\n // sole backend, so ignore the retired key from every config layer.\n removeLegacySageEngine(cfg as Record<string, unknown>);\n this.validateBehavior(cfg);\n if (this.strict && !opts.skipIdentityValidation) {\n this.validateIdentity(cfg);\n }\n // In strict mode, validateIdentity has confirmed provider/model are set;\n // it's safe to assert the full Config contract. In non-strict mode the\n // caller (e.g. early-boot wizard) accepts a Partial and constructs the\n // provider later, so we deliberately return without the cast.\n return Object.freeze(cfg) as Config;\n }\n\n /** Check whether a config object contains only the two bootstrap keys. */\n private static isBootstrapOnly(config: Record<string, unknown>): boolean {\n return isBootstrapOnly(config);\n }\n\n private async ensureGlobalDefaults(): Promise<void> {\n const fp = this.paths.globalConfig;\n const t0 = Date.now();\n try {\n await withFileLock(fp, async () => {\n let parsed: Record<string, unknown>;\n let fileExisted = true;\n try {\n const raw = await fs.readFile(fp, 'utf8');\n const result = safeParse<unknown>(raw);\n if (!result.ok || !isPlainRecord(result.value)) {\n return;\n }\n parsed = result.value;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.events?.emit('storage.error', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'ensure_defaults',\n outcome: 'failure',\n error: storageErrorString(err),\n recoverable: false,\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n this.logWarn('Config defaults read failed', {\n event: 'config.defaults_read_failed',\n path: fp,\n message: toErrorMessage(err),\n });\n return;\n }\n fileExisted = false;\n parsed = {};\n }\n\n // Do not silently normalize an explicit unsupported bootstrap schema.\n // Leave it intact so load() can surface the version error to the user.\n if (parsed['version'] !== undefined && parsed['version'] !== 1) return;\n\n const rawProfileName = (parsed as { activeProfile?: string | undefined }).activeProfile;\n const profileName = safeProfileName(rawProfileName);\n const profileFp = this.paths.profileConfig(profileName);\n\n // Run before trimming so a genuinely legacy flat config can be moved\n // into a missing profile. Once a profile file exists, root-level\n // settings are ignored rather than merged into the active profile.\n await this.ensureProfileConfig(profileFp, parsed, fileExisted);\n\n // Bootstrap config now only stores version + activeProfile.\n // Full behavior defaults go into the profile config instead.\n const bootstrap: Record<string, unknown> = {\n version: 1,\n activeProfile: profileName,\n };\n\n let needsBootstrapWrite = false;\n // If version is missing or wrong, write the bootstrap.\n if ((parsed as { version?: number | undefined }).version !== 1) {\n needsBootstrapWrite = true;\n }\n // If activeProfile is missing from the existing parsed config, flag it.\n if (parsed.activeProfile === undefined || parsed.activeProfile !== profileName) {\n needsBootstrapWrite = true;\n }\n // If the bootstrap has extra keys beyond version+activeProfile, trim it.\n if (!DefaultConfigLoader.isBootstrapOnly(parsed)) {\n needsBootstrapWrite = true;\n }\n\n if (needsBootstrapWrite) {\n // Back up the current root config before overwriting\n await backupConfigFile(fp, this.paths);\n await atomicWrite(fp, JSON.stringify(bootstrap, null, 2), { mode: 0o600 });\n this.events?.emit('storage.write', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'ensure_defaults',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n }\n });\n } catch (err) {\n this.events?.emit('storage.error', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'ensure_defaults',\n outcome: 'failure',\n error: storageErrorString(err),\n recoverable: false,\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n this.logWarn('Config defaults write failed', {\n event: 'config.defaults_write_failed',\n path: fp,\n message: toErrorMessage(err),\n });\n }\n }\n\n /**\n * Ensure a profile config file exists at the given path.\n * On first boot: migrate content from the old flat global config, or seed\n * with behavior defaults. Subsequent boots: fill any missing keys from\n * CONFIG_BEHAVIOR_DEFAULTS (keeping user settings intact).\n *\n * Once a profile file exists, settings found in the root bootstrap are\n * ignored. This prevents stale or accidentally-written root values from\n * becoming a hidden settings source alongside profiles.\n */\n private async ensureProfileConfig(\n profileFp: string,\n oldGlobalParsed: Record<string, unknown>,\n oldFileExisted: boolean,\n ): Promise<void> {\n const t0 = Date.now();\n\n // No nested file lock: the profile write is idempotent (same content from\n // all writers) and atomicWrite handles concurrent writes atomically on\n // every platform. Using withFileLock here caused a second lock acquisition\n // inside the outer ensureGlobalDefaults() lock \u2014 under high concurrency\n // (50+ peers) the nested lock could time out, silently aborting the\n // migration and leaving the bootstrap overstuffed forever.\n let parsed: Record<string, unknown>;\n let existed = true;\n try {\n const raw = await fs.readFile(profileFp, 'utf8');\n const result = safeParse<unknown>(raw);\n if (!result.ok || !isPlainRecord(result.value)) {\n this.logWarn('Profile config parse failed \u2014 falling back to defaults', {\n event: 'config.profile_parse_failed',\n path: profileFp,\n });\n parsed = {};\n } else {\n parsed = result.value;\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.logWarn('Profile config read failed', {\n event: 'config.profile_read_failed',\n path: profileFp,\n message: toErrorMessage(err),\n });\n return;\n }\n existed = false;\n parsed = {};\n }\n\n const removedLegacyEngine = removeLegacySageEngine(parsed);\n\n // Migration from old flat config to profile: If the old global config\n // existed and had any setting at all (can be just `version` + one user\n // setting like `mcpServers`), promote them into the new profile config.\n //\n let seed: Record<string, unknown>;\n if (!existed && oldFileExisted && Object.keys(oldGlobalParsed).length >= 1) {\n // Copy old global content into the profile, excluding bootstrap-only fields.\n seed = { ...oldGlobalParsed };\n delete seed['activeProfile'];\n removeLegacySageEngine(seed);\n // Fill in any missing behavior defaults.\n const filled = fillMissingDefaults(seed, CONFIG_BEHAVIOR_DEFAULTS as Record<string, unknown>);\n seed = filled.value;\n } else {\n // Normal boot: fill missing defaults on existing (or empty) profile.\n const filled = fillMissingDefaults(\n parsed,\n CONFIG_BEHAVIOR_DEFAULTS as Record<string, unknown>,\n );\n if (!filled.changed && !removedLegacyEngine) return; // Nothing to write\n seed = filled.value;\n }\n\n await backupConfigFile(profileFp, this.paths);\n await atomicWrite(profileFp, JSON.stringify(seed, null, 2), { mode: 0o600 });\n this.events?.emit('storage.write', {\n sessionId: '~config~',\n store: 'config',\n filePath: profileFp,\n operation: 'ensure_profile_defaults',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n }\n\n /**\n * Persist sync config to the active profile's sync.json, with the token encrypted\n * by the vault (if provided). The file is isolated from the main config\n * hierarchy to prevent accidental commits.\n */\n async persistSyncConfig(cfg: SyncConfig): Promise<void> {\n let toWrite = { ...cfg };\n if (this.vault && toWrite.githubToken && !toWrite.githubToken.startsWith('enc:')) {\n // Re-encrypt if plaintext (e.g. came from in-memory configStore update\n // rather than direct /sync enable call). Idempotent for already-encrypted.\n toWrite = { ...toWrite, githubToken: this.vault.encrypt(toWrite.githubToken) };\n }\n const fp = this.paths.syncConfig;\n const t0 = Date.now();\n try {\n await atomicWrite(fp, JSON.stringify(toWrite, null, 2), { mode: 0o600 });\n this.events?.emit('storage.write', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'persist_sync',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n } catch (err) {\n this.events?.emit('storage.error', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'persist_sync',\n outcome: 'failure',\n error: storageErrorString(err),\n recoverable: false,\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n /**\n * Read the active profile's sync.json (encrypted GitHub token storage) and decrypt\n * the token if a vault is available. Returns null if the file doesn't exist.\n * This is separate from main config loading because sync.json is intentionally\n * isolated \u2014 it should never be part of project-local or env-driven config.\n */\n async loadSyncConfig(): Promise<SyncConfig | null> {\n const fp = this.paths.syncConfig;\n const t0 = Date.now();\n try {\n const raw = await fs.readFile(fp, 'utf8');\n const parsed = safeParse<SyncConfig>(raw);\n if (!parsed.ok || !parsed.value) {\n this.events?.emit('storage.read', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'load_sync',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse error or empty file',\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return null;\n }\n\n // Decrypt the token if vault is available (field name matches secret pattern)\n if (this.vault) {\n const decrypted = decryptConfigSecrets({ sync: parsed.value } as PartialConfig, this.vault);\n const result = (decrypted as { sync: SyncConfig }).sync ?? null;\n this.events?.emit('storage.read', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'load_sync',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return result;\n }\n this.events?.emit('storage.read', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'load_sync',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return parsed.value;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n // Non-ENOENT failures (EACCES, ENOSPC, etc.) \u2014 emit storage.read failure, then return null\n this.events?.emit('storage.read', {\n sessionId: '~config~',\n store: 'config',\n filePath: fp,\n operation: 'load_sync',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: storageErrorString(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n this.logWarn('Config sync load failed', {\n event: 'config.sync_load_failed',\n message: toErrorMessage(err),\n });\n return null;\n }\n }\n\n private async readJson(file: string): Promise<PartialConfig> {\n const t0 = Date.now();\n let mtimeMs: number | null = null;\n try {\n const stat = await fs.stat(file);\n mtimeMs = stat.mtimeMs;\n const cached = this.jsonCache.get(file);\n if (cached && cached.mtimeMs === mtimeMs) {\n return structuredClone(cached.value);\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n this.jsonCache.set(file, { mtimeMs: null, value: {} });\n return {};\n }\n this.events?.emit('storage.read', {\n sessionId: '~config~',\n store: 'config',\n filePath: file,\n operation: 'read_json',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: storageErrorString(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n this.logWarn('Config read failed (stat)', {\n event: 'config.read_failed',\n path: file,\n message: toErrorMessage(err),\n });\n return {};\n }\n\n let raw: string;\n try {\n raw = await fs.readFile(file, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n this.events?.emit('storage.read', {\n sessionId: '~config~',\n store: 'config',\n filePath: file,\n operation: 'read_json',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: storageErrorString(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n this.logWarn('Config read failed (read)', {\n event: 'config.read_failed',\n path: file,\n message: toErrorMessage(err),\n });\n }\n this.jsonCache.set(file, { mtimeMs: null, value: {} });\n return {};\n }\n const parsed = safeParse<PartialConfig>(raw);\n if (!parsed.ok || !parsed.value) {\n this.events?.emit('storage.read', {\n sessionId: '~config~',\n store: 'config',\n filePath: file,\n operation: 'read_json',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse error or empty file',\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n this.logWarn('Config parse failed \u2014 falling back to defaults', {\n event: 'config.parse_failed',\n path: file,\n });\n return {};\n }\n // Migrate the pre-SAGE `superMemory` key per SOURCE, before the layered\n // merge: after merging, `Sage` always exists (behavior defaults), so a\n // post-merge migration could only ever see defaults win and would drop the\n // user's legacy settings.\n migrateLegacySuperMemoryKey(parsed.value as Record<string, unknown>);\n this.jsonCache.set(file, { mtimeMs, value: structuredClone(parsed.value) });\n return parsed.value;\n }\n\n private validateBehavior(cfg: PartialConfig): void {\n /* v8 ignore start -- defensive: config defaults always seed version:1 before validation */\n if (cfg.version === undefined)\n throw new ConfigError({\n message: 'Config: missing version field',\n code: ERROR_CODES.CONFIG_INVALID,\n context: { field: 'version' },\n });\n /* v8 ignore stop */\n if (cfg.version !== 1)\n throw new ConfigError({\n message: `Config: unsupported version ${cfg.version}`,\n code: ERROR_CODES.CONFIG_INVALID,\n context: { field: 'version', actual: cfg.version },\n });\n const c = cfg.context;\n if (!c)\n throw new ConfigError({\n message: 'Config: missing context section',\n code: ERROR_CODES.CONFIG_INVALID,\n context: { field: 'context' },\n });\n // A user-edited config.json can land strings here (\"0.6\") and slip past\n // truthiness checks; the `>=` comparison then coerces silently and the\n // threshold ordering check passes for nonsense values. Validate types\n // explicitly so misconfigs surface here, not as confusing failures deep\n // in the auto-compaction logic.\n const fields: Array<keyof typeof c> = ['warnThreshold', 'softThreshold', 'hardThreshold'];\n for (const f of fields) {\n const v = c[f];\n if (typeof v !== 'number' || !Number.isFinite(v)) {\n throw new ConfigError({\n message: `Config: context.${String(f)} must be a finite number (got ${typeof v})`,\n code: ERROR_CODES.CONFIG_INVALID,\n context: { field: `context.${String(f)}`, actualType: typeof v },\n });\n }\n }\n if (c.warnThreshold >= c.softThreshold || c.softThreshold >= c.hardThreshold) {\n throw new ConfigError({\n message: 'Config: context thresholds must satisfy warn < soft < hard',\n code: ERROR_CODES.CONFIG_INVALID,\n context: { warn: c.warnThreshold, soft: c.softThreshold, hard: c.hardThreshold },\n });\n }\n if (c.mode !== undefined && !isContextWindowModeSelectionId(c.mode)) {\n // An unknown mode (typo or value from an older/renamed scheme) should not\n // brick the CLI \u2014 unlike the numeric thresholds above there is a safe\n // default. Warn and fall back rather than throwing.\n const known = listContextWindowModes()\n .map((m) => m.id)\n .join(', ');\n this.logWarn(\n `Ignoring unknown context.mode \"${c.mode}\" \u2014 falling back to \"${DEFAULT_CONTEXT_WINDOW_MODE_ID}\"`,\n { event: 'config.unknown_context_mode', mode: c.mode, known },\n );\n c.mode = DEFAULT_CONTEXT_WINDOW_MODE_ID;\n } else if (c.mode !== undefined) {\n c.mode = normalizeContextWindowModeId(c.mode) ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;\n }\n }\n\n private validateIdentity(cfg: PartialConfig): void {\n if (!cfg.provider) {\n throw new ConfigError({\n message: 'Config: no provider configured. Run `wstack init` or set WRONGSTACK_PROVIDER.',\n code: ERROR_CODES.CONFIG_INVALID,\n context: { field: 'provider' },\n });\n }\n if (!cfg.model) {\n throw new ConfigError({\n message: 'Config: no model configured. Run `wstack init` or set WRONGSTACK_MODEL.',\n code: ERROR_CODES.CONFIG_INVALID,\n context: { field: 'model' },\n });\n }\n }\n}\n", "import { expectDefined } from '../utils/expect-defined.js';\n\nexport type ContextWindowModeId = 'balanced' | 'frugal' | 'deep';\nexport type DeprecatedContextWindowModeId = 'archival';\nexport type ContextWindowModeSelectionId = ContextWindowModeId | DeprecatedContextWindowModeId;\n\nexport type ContextWindowAggressiveOn = 'hard' | 'soft' | 'warn';\n\nexport interface ContextWindowThresholds {\n warn: number;\n soft: number;\n hard: number;\n}\n\nexport interface ContextWindowMode {\n id: ContextWindowModeId;\n name: string;\n description: string;\n thresholds: ContextWindowThresholds;\n aggressiveOn: ContextWindowAggressiveOn;\n preserveK: number;\n eliseThreshold: number;\n targetLoad: number;\n}\n\nexport interface ContextWindowPolicy extends ContextWindowMode {}\n\nexport interface ContextWindowConfigLike {\n mode?: ContextWindowModeSelectionId | string | undefined;\n warnThreshold?: number | undefined;\n softThreshold?: number | undefined;\n hardThreshold?: number | undefined;\n preserveK?: number | undefined;\n eliseThreshold?: number | undefined;\n targetLoad?: number | undefined;\n}\n\nexport const DEFAULT_CONTEXT_WINDOW_MODE_ID: ContextWindowModeId = 'balanced';\n\nexport const DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES: Readonly<\n Record<DeprecatedContextWindowModeId, ContextWindowModeId>\n> = Object.freeze({\n archival: 'balanced',\n});\n\nexport const CONTEXT_WINDOW_MODES: readonly ContextWindowMode[] = Object.freeze([\n {\n id: 'balanced',\n name: 'Balanced',\n description: 'Default rolling compaction: recent work stays verbatim, old tool output is trimmed.',\n thresholds: { warn: 0.55, soft: 0.7, hard: 0.85 },\n aggressiveOn: 'soft',\n preserveK: 8,\n eliseThreshold: 1200,\n targetLoad: 0.65,\n },\n {\n id: 'frugal',\n name: 'Frugal',\n description: 'Token-saver mode: compacts early and keeps a tight verbatim tail.',\n thresholds: { warn: 0.4, soft: 0.55, hard: 0.7 },\n aggressiveOn: 'warn',\n preserveK: 4,\n eliseThreshold: 500,\n targetLoad: 0.45,\n },\n {\n id: 'deep',\n name: 'Deep',\n description: 'Long-reasoning mode: delays compaction and keeps more recent turns intact.',\n thresholds: { warn: 0.72, soft: 0.86, hard: 0.96 },\n aggressiveOn: 'hard',\n preserveK: 18,\n eliseThreshold: 5000,\n targetLoad: 0.82,\n },\n]);\n\nexport function listContextWindowModes(): ContextWindowMode[] {\n return CONTEXT_WINDOW_MODES.map((m) => ({ ...m, thresholds: { ...m.thresholds } }));\n}\n\nexport function normalizeContextWindowModeId(\n id: string | null | undefined,\n): ContextWindowModeId | null {\n if (!id) return null;\n if (isContextWindowModeId(id)) return id;\n return DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES[id as DeprecatedContextWindowModeId] ?? null;\n}\n\nexport function isDeprecatedContextWindowModeId(\n id: string,\n): id is DeprecatedContextWindowModeId {\n return Object.hasOwn(DEPRECATED_CONTEXT_WINDOW_MODE_ALIASES, id);\n}\n\nexport function isContextWindowModeSelectionId(\n id: string,\n): id is ContextWindowModeSelectionId {\n return isContextWindowModeId(id) || isDeprecatedContextWindowModeId(id);\n}\n\nexport function getContextWindowMode(id: string | null | undefined): ContextWindowMode | null {\n const normalized = normalizeContextWindowModeId(id);\n if (!normalized) return null;\n const mode = CONTEXT_WINDOW_MODES.find((m) => m.id === normalized);\n return mode ? { ...mode, thresholds: { ...mode.thresholds } } : null;\n}\n\nexport function isContextWindowModeId(id: string): id is ContextWindowModeId {\n return CONTEXT_WINDOW_MODES.some((m) => m.id === id);\n}\n\nexport function resolveContextWindowPolicy(\n config: ContextWindowConfigLike = {},\n overrideMode?: string | null | undefined,\n): ContextWindowPolicy {\n const requested = overrideMode ?? config.mode ?? DEFAULT_CONTEXT_WINDOW_MODE_ID;\n const mode = getContextWindowMode(requested) ?? expectDefined(getContextWindowMode(DEFAULT_CONTEXT_WINDOW_MODE_ID));\n\n return {\n ...mode,\n thresholds: {\n warn: config.warnThreshold ?? mode.thresholds.warn,\n soft: config.softThreshold ?? mode.thresholds.soft,\n hard: config.hardThreshold ?? mode.thresholds.hard,\n },\n preserveK: config.preserveK ?? mode.preserveK,\n eliseThreshold: config.eliseThreshold ?? mode.eliseThreshold,\n targetLoad: config.targetLoad ?? mode.targetLoad,\n };\n}\n\nexport function formatContextWindowModeList(activeId?: string | null): string {\n const active = normalizeContextWindowModeId(activeId);\n return CONTEXT_WINDOW_MODES.map((m) => {\n const marker = m.id === active ? '*' : ' ';\n return `${marker} ${m.id.padEnd(9)} ${m.name} - ${m.description}`;\n }).join('\\n');\n}\n", "const BOOTSTRAP_KEYS = new Set(['version', 'activeProfile']);\n\nexport function isBootstrapOnly(config: Record<string, unknown>): boolean {\n return Object.keys(config).every((k) => BOOTSTRAP_KEYS.has(k));\n}\n", "/**\n * Shared configuration constants used across execution, storage, CLI, and WebUI.\n * Centralized here to avoid cross-domain import cycles.\n */\n\n/** Default tools config \u2014 mirrors values baked into CONFIG_BEHAVIOR_DEFAULTS. */\nexport const DEFAULT_TOOLS_CONFIG = Object.freeze({\n defaultExecutionStrategy: 'smart',\n maxIterations: 100,\n iterationTimeoutMs: 300_000,\n maxToolTimeoutMs: 300_000,\n sessionTimeoutMs: 1_800_000,\n perIterationOutputCapBytes: 100_000,\n descriptionMode: Object.freeze({}) as Record<string, 'extend' | 'simple' | undefined>,\n disabledTools: Object.freeze([]) as readonly string[],\n autoExtendLimit: true,\n restrictToProjectRoot: true,\n loopDetection: Object.freeze({\n mode: 'steer-then-cut',\n steerThreshold: 3,\n cutThreshold: 5,\n windowSize: 12,\n callRepeatThreshold: 4,\n }) as Readonly<{\n mode: 'steer-then-cut' | 'cut' | 'off';\n steerThreshold: number;\n cutThreshold: number;\n windowSize: number;\n callRepeatThreshold: number;\n }>,\n});\n\n/** Default context config \u2014 mirrors CONFIG_BEHAVIOR_DEFAULTS.context. */\nexport const DEFAULT_CONTEXT_CONFIG = Object.freeze({\n warnThreshold: 0.55,\n softThreshold: 0.7,\n hardThreshold: 0.85,\n targetLoad: 0.65,\n preserveK: 8,\n eliseThreshold: 1200,\n});\n\n/** Default autonomy config \u2014 auto-proceed delay etc. */\nexport const DEFAULT_AUTONOMY_CONFIG = Object.freeze({\n autoProceedDelayMs: 45_000,\n});\n\n/**\n * Default process circuit-breaker config. Protection is OFF by default \u2014 the\n * breaker only gates `bash`/`exec` once the user opts in via `/settings breaker on`.\n * The auto kill/reset delay is only consulted when protection is enabled.\n */\nexport const DEFAULT_CIRCUIT_BREAKER_CONFIG = Object.freeze({\n enabled: false,\n autoKillResetMs: 60_000,\n});\n\n/** Default session logging / audit configuration. */\nexport const DEFAULT_SESSION_LOGGING_CONFIG = Object.freeze({\n auditLevel: 'standard' as const,\n sampling: {\n toolProgress: {\n sampleRate: 8,\n },\n },\n});\n\n/** Default retention window for local session pruning. */\nexport const DEFAULT_SESSION_PRUNE_DAYS = 30;\n", "import { DEFAULT_TUI_THINKING_WORD, type Config } from '../../types/config.js';\nimport { DEFAULT_CONTEXT_WINDOW_MODE_ID } from '../../types/context-window.js';\nimport {\n DEFAULT_AUTONOMY_CONFIG,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n DEFAULT_CONTEXT_CONFIG,\n DEFAULT_SESSION_LOGGING_CONFIG,\n DEFAULT_TOOLS_CONFIG,\n} from '../../types/default-config.js';\n\n/**\n * Defaults express *behavior*, not identity. Provider and model are NOT\n * hardcoded \u2014 they must be resolved at runtime from config + env + the\n * ModelsRegistry. A bare Config returned by this loader will throw when\n * the agent tries to construct a provider, with a message that points\n * users at `wstack init`.\n */\nexport const CONFIG_BEHAVIOR_DEFAULTS: Omit<Config, 'provider' | 'model'> = {\n version: 1,\n context: {\n mode: DEFAULT_CONTEXT_WINDOW_MODE_ID,\n warnThreshold: DEFAULT_CONTEXT_CONFIG.warnThreshold,\n softThreshold: DEFAULT_CONTEXT_CONFIG.softThreshold,\n hardThreshold: DEFAULT_CONTEXT_CONFIG.hardThreshold,\n targetLoad: DEFAULT_CONTEXT_CONFIG.targetLoad,\n autoCompact: true,\n preserveK: DEFAULT_CONTEXT_CONFIG.preserveK,\n eliseThreshold: DEFAULT_CONTEXT_CONFIG.eliseThreshold,\n strategy: 'hybrid',\n },\n tools: {\n defaultExecutionStrategy: DEFAULT_TOOLS_CONFIG.defaultExecutionStrategy,\n maxIterations: DEFAULT_TOOLS_CONFIG.maxIterations,\n iterationTimeoutMs: DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,\n maxToolTimeoutMs: DEFAULT_TOOLS_CONFIG.maxToolTimeoutMs,\n sessionTimeoutMs: DEFAULT_TOOLS_CONFIG.sessionTimeoutMs,\n perIterationOutputCapBytes: DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,\n descriptionMode: DEFAULT_TOOLS_CONFIG.descriptionMode,\n disabledTools: DEFAULT_TOOLS_CONFIG.disabledTools as string[],\n autoExtendLimit: DEFAULT_TOOLS_CONFIG.autoExtendLimit,\n restrictToProjectRoot: DEFAULT_TOOLS_CONFIG.restrictToProjectRoot,\n loopDetection: DEFAULT_TOOLS_CONFIG.loopDetection,\n },\n log: { level: 'info' },\n features: {\n mcp: true,\n plugins: true,\n memory: true,\n modelsRegistry: true,\n skills: true,\n prompts: true,\n // 'auto' \u2192 resolveTokenSavingTier picks a concrete tier from the model's\n // context window ONCE per session (cache-safe): lean prompt on small\n // windows (<32k medium, <128k light) where the fixed identity+tool prose\n // is a big fraction; minimal trimming on >=128k so modern large-window\n // models still get cost savings without capability loss. Explicit tiers\n // are respected verbatim.\n tokenSavingMode: 'auto',\n allowOutsideProjectRoot: true,\n },\n Sage: {\n enabled: true,\n storage: {\n projectLocal: true,\n directory: '.wrongstack/memories',\n },\n inject: {\n // Keep the ordinary turn prompt clean. Project memory is surfaced\n // on-demand beside relevant tool results, where the path/query that\n // caused retrieval is known and the hint can be independently capped.\n turnContext: false,\n toolResults: true,\n // Off: task text searches for what the operator is doing, not for the\n // file the tool touched, and every match it adds is unrelated to the\n // result it lands on.\n taskAware: false,\n maxHintsPerTool: 8,\n maxCharsPerTool: 2800,\n maxTurnMemories: 8,\n maxCharsPerTurn: 2400,\n minScore: 0.72,\n minImportance: 0.5,\n // 0 = once per session. Re-showing a memory the model has already been\n // given spends context and adds nothing.\n repeatCooldownMs: 0,\n },\n hygiene: {\n autoAfterSession: true,\n autoOnFileChange: true,\n retentionDays: 90,\n archiveLowConfidenceAfterDays: 30,\n },\n embeddings: {\n enabled: false,\n },\n },\n skills: { readClaudeSkills: true },\n mcpServers: {},\n fallbackAuto: true,\n maxConcurrent: 4,\n // YOLO opt-in: users who want auto-approve behaviour set `yolo: true`.\n // Off by default so prompt injection in repository content, fetched\n // content, or mailbox messages cannot induce arbitrary shell calls or\n // write operations without human confirmation.\n yolo: false,\n nextPrediction: false,\n hints: true,\n debugStream: false,\n configScope: 'global',\n indexing: {\n onSessionStart: true,\n onEdit: true,\n watchExternal: true,\n debounceMs: 400,\n },\n session: { ...DEFAULT_SESSION_LOGGING_CONFIG },\n autonomy: {\n // 'auto' by default: the agent self-drives (picks the top next-step after\n // each turn). This is a startup default, not a runtime flip \u2014 the\n // autoProceedMaxIterations cap + autoProceedDelayMs cooldown + Ctrl+C /\n // [GOAL_COMPLETE] stops remain in force. Explicit 'off'\n // in a config file is preserved (opt-out respected).\n defaultMode: 'auto',\n autoProceedDelayMs: DEFAULT_AUTONOMY_CONFIG.autoProceedDelayMs,\n autoProceedMaxIterations: 50,\n autonomyNextPrompt: 'auto {{suggestion}}',\n terminalTitleAnimation: true,\n // Mirrored from the top-level yolo default so the autonomy subsystem\n // (which reads autonomy.yolo) stays consistent with config.yolo.\n yolo: false,\n fleetChatVerbosity: 'off',\n chime: false,\n confirmExit: true,\n mouseMode: false,\n enhance: true,\n enhanceDelayMs: 60_000,\n enhanceLanguage: 'original',\n statuslineMode: 'detailed',\n thinkingWord: DEFAULT_TUI_THINKING_WORD,\n showAgentSwarmPanel: true,\n },\n circuitBreaker: { ...DEFAULT_CIRCUIT_BREAKER_CONFIG },\n modelRuntime: {\n // `effort` is intentionally undefined by default. Leaving it unset lets\n // each model use its provider-recommended reasoning effort (or none at\n // all) instead of forcing an opinionated value that may be unsupported,\n // silently omitted, and surfaced as a per-request warning. Users who\n // want a specific effort can opt in via `/settings` or the WebUI panel.\n reasoning: { mode: 'auto' },\n cache: { ttl: '1h' },\n },\n systemPrompt: { variant: 'default' },\n};\n", "import { ConfigError, ERROR_CODES } from '../../types/errors.js';\nimport { CONFIG_BEHAVIOR_DEFAULTS } from './defaults.js';\n\nexport function isPlainRecord(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) === Object.prototype\n );\n}\n\nfunction cloneJsonValue<T>(value: T): T {\n return structuredClone(value);\n}\n\n/**\n * Add-only default filling: copies missing keys from `defaults` into `target`\n * without removing or replacing any existing values. Recursively fills nested\n * plain objects. Safe for the normal boot path where a user's explicit config\n * must never be overwritten.\n *\n * For the add+replace variant (which also replaces type-incompatible values)\n * see `repairConfigDefaults`.\n */\nexport function fillMissingDefaults(\n target: Record<string, unknown>,\n defaults: Record<string, unknown>,\n): { value: Record<string, unknown>; changed: boolean } {\n const value = cloneJsonValue(target);\n const changed = fillMissingDefaultsInPlace(value, defaults);\n return { value, changed };\n}\n\nfunction fillMissingDefaultsInPlace(\n target: Record<string, unknown>,\n defaults: Record<string, unknown>,\n): boolean {\n let changed = false;\n for (const [key, defaultValue] of Object.entries(defaults)) {\n if (!Object.hasOwn(target, key)) {\n target[key] = cloneJsonValue(defaultValue);\n changed = true;\n continue;\n }\n const current = target[key];\n if (isPlainRecord(current) && isPlainRecord(defaultValue)) {\n changed = fillMissingDefaultsInPlace(current, defaultValue) || changed;\n }\n }\n return changed;\n}\n\nexport interface ConfigDefaultRepair {\n path: string;\n action: 'added' | 'replaced';\n}\n\nexport interface ConfigDefaultRepairReport {\n fixed: Record<string, unknown>;\n changes: ConfigDefaultRepair[];\n changed: boolean;\n}\n\n/**\n * Materialize the canonical behavior defaults into a persisted profile config.\n * Add+replace variant: missing fields are added AND values whose JSON shape is\n * incompatible with the default are replaced (not just skipped). This is useful\n * for the config-doctor repair path where a user-edited file may have wrong\n * types. For the normal boot path (add-only) see `fillMissingDefaults`.\n *\n * Identity fields (`provider`, `model`) are intentionally not invented.\n *\n * NOTE: This function assumes JSON-roundtripped input. Non-JSON values such\n * as `Buffer` or `Uint8Array` are objects and may not be correctly detected\n * as type-incompatible with primitive defaults.\n */\nexport function repairConfigDefaults(input: Record<string, unknown>): ConfigDefaultRepairReport {\n const fixed = cloneJsonValue(input);\n const changes: ConfigDefaultRepair[] = [];\n\n const MAX_REPAIR_DEPTH = 20;\n\n const repair = (\n target: Record<string, unknown>,\n defaults: Record<string, unknown>,\n prefix: string,\n depth: number = 0,\n ): void => {\n if (depth > MAX_REPAIR_DEPTH) {\n throw new ConfigError({\n message:\n `Config repair exceeded maximum depth (${MAX_REPAIR_DEPTH}) at \"${prefix}\". ` +\n 'This likely indicates a circular reference or deeply nested user-provided config.',\n code: ERROR_CODES.CONFIG_INVALID,\n context: { depth, prefix },\n });\n }\n for (const [key, defaultValue] of Object.entries(defaults)) {\n const path = prefix ? `${prefix}.${key}` : key;\n if (!Object.hasOwn(target, key)) {\n target[key] = cloneJsonValue(defaultValue);\n changes.push({ path, action: 'added' });\n continue;\n }\n\n const current = target[key];\n if (isPlainRecord(defaultValue)) {\n if (isPlainRecord(current)) {\n repair(current, defaultValue, path, depth + 1);\n } else {\n target[key] = cloneJsonValue(defaultValue);\n changes.push({ path, action: 'replaced' });\n }\n continue;\n }\n\n const defaultIsArray = Array.isArray(defaultValue);\n // Array element shape is NOT validated here \u2014 if an array default's\n // element type changes in a future version, existing arrays with the\n // old element type will be kept even though their elements no longer\n // match the new default element type. This is acceptable because:\n // (a) config changes that widen element types are extremely rare, and\n // (b) the config-doctor is a best-effort repair, not a type checker.\n const compatible = defaultIsArray\n ? Array.isArray(current)\n : !Array.isArray(current) && typeof current === typeof defaultValue;\n if (!compatible) {\n target[key] = cloneJsonValue(defaultValue);\n changes.push({ path, action: 'replaced' });\n }\n }\n };\n\n repair(fixed, CONFIG_BEHAVIOR_DEFAULTS as Record<string, unknown>, '');\n return { fixed, changes, changed: changes.length > 0 };\n}\n", "/**\n * Surface the OS error code (EACCES, ENOSPC, ...) alongside the message in\n * storage.* event payloads. Codes are stable and locale-independent.\n */\nexport function storageErrorString(err: unknown): string {\n if (err instanceof Error) {\n const code = (err as NodeJS.ErrnoException).code;\n return code ? `${code}: ${err.message}` : err.message;\n }\n /* v8 ignore next -- defensive: callers only pass fs Error instances */\n return String(err);\n}\n", "import type { Config } from '../../types/config.js';\n\nfunction envBool(v: string): boolean {\n return !/^(0|false|no|off)$/i.test(v.trim());\n}\n\nexport function envBoolOptional(v: string | undefined): boolean {\n return v !== undefined && envBool(v);\n}\n\nconst LOG_LEVELS = new Set<Config['log']['level']>(['error', 'warn', 'info', 'debug', 'trace']);\n\nfunction envLogLevel(v: string): Config['log']['level'] {\n return LOG_LEVELS.has(v as Config['log']['level']) ? (v as Config['log']['level']) : 'info';\n}\n\nconst defaultIndexing = {\n onSessionStart: true,\n onEdit: true,\n watchExternal: true,\n debounceMs: 400,\n} as const;\n\nexport type PartialConfig = Partial<Config> & {\n providers?: Record<\n string,\n { apiKey?: string | undefined; baseUrl?: string | undefined; type?: string | undefined }\n >;\n /** Fields that came from environment variables - must not be persisted. */\n _envSource?: Set<string> | undefined;\n};\n\nexport const ENV_MAP: Record<string, (cfg: PartialConfig, val: string) => void> = {\n WRONGSTACK_PROVIDER: (c, v) => {\n c.provider = v;\n if (c._envSource === undefined) c._envSource = new Set();\n c._envSource.add('provider');\n },\n WRONGSTACK_MODEL: (c, v) => {\n c.model = v;\n if (c._envSource === undefined) c._envSource = new Set();\n c._envSource.add('model');\n },\n WRONGSTACK_API_KEY: (c, v) => {\n c.apiKey = v;\n if (c._envSource === undefined) c._envSource = new Set();\n c._envSource.add('apiKey');\n },\n WRONGSTACK_BASE_URL: (c, v) => {\n c.baseUrl = v;\n if (c._envSource === undefined) c._envSource = new Set();\n c._envSource.add('baseUrl');\n },\n WRONGSTACK_LOG_LEVEL: (c, v) => {\n /* v8 ignore next -- defensive: config defaults always seed c.log before env handlers run */\n if (!c.log) c.log = { level: 'info' };\n c.log.level = envLogLevel(v);\n },\n WRONGSTACK_INDEX_ON_START: (c, v) => {\n c.indexing = { ...defaultIndexing, ...c.indexing, onSessionStart: envBool(v) };\n },\n WRONGSTACK_INDEX_ON_EDIT: (c, v) => {\n c.indexing = { ...defaultIndexing, ...c.indexing, onEdit: envBool(v) };\n },\n WRONGSTACK_INDEX_WATCH: (c, v) => {\n c.indexing = { ...defaultIndexing, ...c.indexing, watchExternal: envBool(v) };\n },\n};\n", "import type { PartialConfig } from './env-overrides.js';\n\nconst IN_PROJECT_ALLOWED_KEYS: ReadonlySet<string> = new Set([\n 'version',\n 'model',\n 'cwd',\n 'context',\n 'tools',\n 'features',\n 'Sage',\n 'skills',\n 'autonomy',\n 'indexing',\n 'session',\n 'log',\n 'launch',\n 'nextPrediction',\n 'hints',\n 'debugStream',\n 'configScope',\n 'maxConcurrent',\n 'uiLocale',\n 'fallbackModels',\n 'fallbackProfiles',\n 'favoriteModels',\n 'favoriteModelsOnly',\n 'modelAvailabilitySchedule',\n 'fallbackAuto',\n 'fallbackStickiness',\n 'models',\n 'modelMatrix',\n 'circuitBreaker',\n 'adaptiveConcurrency',\n 'modelRuntime',\n]);\n\nconst KNOWN_DENIED_IN_PROJECT: ReadonlyArray<{ key: string; reason: string }> = [\n {\n key: 'activeProfile',\n reason: 'Only the trusted root bootstrap may select the active profile.',\n },\n { key: 'provider', reason: 'Provider id override; can intercept prompts/responses.' },\n { key: 'apiKey', reason: 'Overrides user API key; exfiltrates prompts.' },\n { key: 'baseUrl', reason: 'Redirects provider endpoint; leaks real API key.' },\n { key: 'providers', reason: 'Per-provider apiKey/baseUrl/oauthConfig; same redirect/exfil.' },\n { key: 'mcpServers', reason: 'Arbitrary command/args/env spawned at boot (RCE).' },\n { key: 'hooks', reason: 'Shell command arrays on lifecycle events (RCE).' },\n { key: 'plugins', reason: 'Dynamic npm package load at boot (RCE).' },\n {\n key: 'pluginManager',\n reason: 'Controls which plugin boot states the LLM may mutate; user-owned trust policy.',\n },\n { key: 'sync', reason: 'Carries githubToken credential and target repo.' },\n { key: 'yolo', reason: 'Disables all permission confirmation prompts.' },\n { key: 'extensions', reason: 'Per-plugin config can carry command/credential fields.' },\n { key: 'hq', reason: 'Carries HQ client token credential and endpoint URL.' },\n { key: 'acp', reason: 'Per-agent ACP command/args/env override \u2192 arbitrary command exec (RCE).' },\n {\n key: 'fleet',\n reason:\n 'Fleet supervision knobs: a repo-committed config could enable autonomous subagent spawning/steering/termination and mailbox traffic on the victim machine.',\n },\n {\n key: 'brain',\n reason:\n 'Brain decision-layer knobs: a repo-committed config could raise the autonomy risk ceiling, switch the Brain to headless (removing the human tier), or reroute Brain decisions to an attacker-chosen provider/model.',\n },\n {\n key: 'git',\n reason:\n \"Carries git.identity (GIT_AUTHOR_*/GIT_COMMITTER_* injection): a repo-committed config could spoof the author identity written into the victim's commit history (impersonation).\",\n },\n];\n\nconst KNOWN_CONFIG_TOP_LEVEL_KEYS: ReadonlySet<string> = new Set([\n 'version',\n 'activeProfile',\n 'provider',\n 'model',\n 'apiKey',\n 'baseUrl',\n 'maxConcurrent',\n 'uiLocale',\n 'providers',\n 'models',\n 'modelMatrix',\n 'context',\n 'tools',\n 'mcpServers',\n 'fallbackModels',\n 'fallbackProfiles',\n 'favoriteModels',\n 'favoriteModelsOnly',\n 'modelAvailabilitySchedule',\n 'fallbackAuto',\n 'fallbackStickiness',\n 'hooks',\n 'plugins',\n 'pluginManager',\n 'log',\n 'features',\n 'Sage',\n 'skills',\n 'yolo',\n 'nextPrediction',\n 'cwd',\n 'autonomy',\n 'hints',\n 'debugStream',\n 'configScope',\n 'indexing',\n 'circuitBreaker',\n 'adaptiveConcurrency',\n 'launch',\n 'session',\n 'modelRuntime',\n 'hq',\n 'sync',\n 'extensions',\n 'acp',\n 'fleet',\n 'brain',\n 'git',\n]);\n\nexport function assertInProjectAllowListComplete(): void {\n const missingFromBoth: string[] = [];\n for (const key of KNOWN_CONFIG_TOP_LEVEL_KEYS) {\n if (IN_PROJECT_ALLOWED_KEYS.has(key)) continue;\n const denied = KNOWN_DENIED_IN_PROJECT.find((d) => d.key === key);\n if (!denied) missingFromBoth.push(key);\n }\n const staleDenials = KNOWN_DENIED_IN_PROJECT.filter(\n (d) => !KNOWN_CONFIG_TOP_LEVEL_KEYS.has(d.key),\n ).map((d) => d.key);\n const duplicate = KNOWN_DENIED_IN_PROJECT.filter((d) => IN_PROJECT_ALLOWED_KEYS.has(d.key)).map(\n (d) => d.key,\n );\n\n const problems: string[] = [];\n if (missingFromBoth.length > 0) {\n problems.push(\n `new Config field(s) not classified as allowed or denied for in-project config: ` +\n missingFromBoth.join(', ') +\n '. Add each to IN_PROJECT_ALLOWED_KEYS (if safe) or KNOWN_DENIED_IN_PROJECT (with a reason).',\n );\n }\n if (staleDenials.length > 0) {\n problems.push(\n `KNOWN_DENIED_IN_PROJECT references keys that no longer exist on Config: ` +\n staleDenials.join(', ') +\n '. Remove them or restore the field on Config.',\n );\n }\n if (duplicate.length > 0) {\n problems.push(\n `field(s) appear in BOTH IN_PROJECT_ALLOWED_KEYS and KNOWN_DENIED_IN_PROJECT: ` +\n duplicate.join(', ') +\n '. The allow-list wins at runtime; remove from one of the two.',\n );\n }\n if (problems.length > 0) {\n throw new Error(\n `stripUnsafeInProjectFields drift check failed:\\n - ${problems.join('\\n - ')}`,\n );\n }\n}\n\nlet driftChecked = false;\n\nexport function stripUnsafeInProjectFields(\n inProject: PartialConfig,\n sourcePath: string,\n warn: (msg: string) => void = (msg) => console.warn(msg),\n): PartialConfig {\n if (!driftChecked) {\n assertInProjectAllowListComplete();\n driftChecked = true;\n }\n const stripped: string[] = [];\n const out: PartialConfig = {};\n for (const [k, v] of Object.entries(inProject)) {\n if (IN_PROJECT_ALLOWED_KEYS.has(k)) {\n (out as Record<string, unknown>)[k] = v;\n continue;\n }\n stripped.push(k);\n }\n\n const outTools = (out as Record<string, unknown>)['tools'];\n if (outTools && typeof outTools === 'object') {\n const execCfg = (outTools as Record<string, unknown>)['exec'];\n if (execCfg && typeof execCfg === 'object') {\n const hasAllow = 'allow' in (execCfg as Record<string, unknown>);\n const hasDanger = 'danger' in (execCfg as Record<string, unknown>);\n if (hasAllow || hasDanger) {\n const clonedExec: Record<string, unknown> = { ...(execCfg as Record<string, unknown>) };\n if (hasAllow) {\n delete clonedExec['allow'];\n stripped.push('tools.exec.allow');\n }\n if (hasDanger) {\n delete clonedExec['danger'];\n stripped.push('tools.exec.danger');\n }\n (out as Record<string, unknown>)['tools'] = {\n ...(outTools as Record<string, unknown>),\n exec: clonedExec,\n };\n }\n }\n }\n\n const outSkills = (out as Record<string, unknown>)['skills'];\n if (outSkills && typeof outSkills === 'object') {\n const skillsRec = outSkills as Record<string, unknown>;\n const needsClone = 'extraDirs' in skillsRec || 'registryUrl' in skillsRec;\n if (needsClone) {\n const clonedSkills = { ...skillsRec };\n if ('extraDirs' in clonedSkills) {\n delete clonedSkills['extraDirs'];\n stripped.push('skills.extraDirs');\n }\n if ('registryUrl' in clonedSkills) {\n delete clonedSkills['registryUrl'];\n stripped.push('skills.registryUrl');\n }\n (out as Record<string, unknown>)['skills'] = clonedSkills;\n }\n }\n\n const outSage = (out as Record<string, unknown>)['Sage'];\n if (outSage && typeof outSage === 'object') {\n const storage = (outSage as Record<string, unknown>)['storage'];\n if (\n storage &&\n typeof storage === 'object' &&\n 'directory' in (storage as Record<string, unknown>)\n ) {\n const clonedStorage = { ...(storage as Record<string, unknown>) };\n delete clonedStorage['directory'];\n (out as Record<string, unknown>)['Sage'] = {\n ...(outSage as Record<string, unknown>),\n storage: clonedStorage,\n };\n stripped.push('Sage.storage.directory');\n }\n }\n\n if (stripped.length > 0) {\n warn(\n JSON.stringify({\n level: 'warn',\n event: 'config.in_project_unsafe_fields_ignored',\n path: sourcePath,\n ignoredKeys: stripped,\n message:\n `Ignored ${stripped.length} field(s) from the repo-committed config ` +\n `\"${sourcePath}\": ${stripped.join(', ')}. ` +\n `Only a small allow-list of benign preferences (model, context, tools limits, ` +\n `features, \u2026) may be set by <project>/.wrongstack/config.json. ` +\n `Everything else must live in your active profile config.`,\n timestamp: new Date().toISOString(),\n }),\n );\n }\n return out;\n}\n", "import { isPlainRecord } from './default-repair.js';\n\n/**\n * Migrate the retired `superMemory` config key (pre-SAGE-rename user configs)\n * into `Sage`. Existing `Sage` values win on conflict; top-level sub-keys the\n * user only ever set under `superMemory` are preserved so a rename release\n * does not silently drop their storage/injection/hygiene settings.\n */\nexport function migrateLegacySuperMemoryKey(config: Record<string, unknown>): boolean {\n const legacy = config['superMemory'];\n if (legacy === undefined) return false;\n if (isPlainRecord(legacy)) {\n const current = config['Sage'];\n config['Sage'] = isPlainRecord(current) ? { ...legacy, ...current } : { ...legacy };\n }\n delete config['superMemory'];\n return true;\n}\n\n/** Remove the retired backend selector from legacy/user-supplied configs. */\nexport function removeLegacySageEngine(config: Record<string, unknown>): boolean {\n const migratedKey = migrateLegacySuperMemoryKey(config);\n const Sage = config['Sage'];\n if (!isPlainRecord(Sage)) return migratedKey;\n const storage = Sage['storage'];\n if (!isPlainRecord(storage) || !Object.hasOwn(storage, 'engine')) {\n return migratedKey;\n }\n delete storage['engine'];\n return true;\n}\n", "import * as path from 'node:path';\n\n/**\n * Compare two absolute filesystem paths for identity. Normalizes separators and\n * `.`/`..` segments via `path.resolve`, and folds case on win32 (and darwin,\n * whose default APFS/HFS+ volumes are case-insensitive) so the same file\n * reached through differently-cased drive/dir spellings still compares equal \u2014\n * the same Windows path-casing hazard that bit the core token symbols.\n */\nexport function samePath(a: string, b: string): boolean {\n let ra = path.resolve(a);\n let rb = path.resolve(b);\n if (process.platform === 'win32' || process.platform === 'darwin') {\n ra = ra.toLowerCase();\n rb = rb.toLowerCase();\n }\n return ra === rb;\n}\n", "/**\n * L2-D: Config version migration framework. Pure functions, decoupled\n * from disk/CLI \u2014 caller passes a parsed JSON object and gets back the\n * up-to-date `Config` shape (or a structured error explaining why\n * migration failed).\n *\n * Migrations are registered as `{ from, to, migrate }` triples and run\n * sequentially. Each migration is independently testable. Adding a new\n * version means appending one migration; existing user configs are\n * upgraded in place at load time.\n */\n\nexport interface MigrationContext {\n /**\n * Original on-disk version of the input. Migrations may use this to\n * decide between in-place patches and rewrites.\n */\n fromVersion: number;\n /**\n * Set when the migration writes back to disk. Callers persist the\n * migrated config when this is true so the user doesn't see the same\n * migration banner on every boot.\n */\n shouldPersist: boolean;\n}\n\nexport interface ConfigMigration {\n /** Version of the input this migration accepts. */\n from: number;\n /** Version of the output it produces. */\n to: number;\n /** Pure transform \u2014 no I/O. */\n migrate(input: Record<string, unknown>, ctx: MigrationContext): Record<string, unknown>;\n /** Optional human-readable description for migration logs / banners. */\n describe?: string | undefined;\n}\n\nexport interface MigrationResult {\n /** Final config (still typed as `unknown`-keyed \u2014 caller validates). */\n config: Record<string, unknown>;\n /** Ordered list of `from\u2192to` versions that ran. */\n applied: string[];\n /** True when at least one migration produced changes worth persisting. */\n shouldPersist: boolean;\n}\n\nexport class ConfigMigrationError extends Error {\n readonly fromVersion: number;\n readonly targetVersion: number;\n readonly missingStep: number | null;\n\n constructor(opts: {\n message: string;\n fromVersion: number;\n targetVersion: number;\n missingStep: number | null;\n }) {\n super(opts.message);\n this.name = 'ConfigMigrationError';\n this.fromVersion = opts.fromVersion;\n this.targetVersion = opts.targetVersion;\n this.missingStep = opts.missingStep;\n }\n}\n\n/**\n * Run registered migrations until the input reaches `targetVersion`.\n *\n * Resolution rules:\n * 1. If `input.version === targetVersion`, no migrations run; `shouldPersist`\n * is false.\n * 2. Otherwise walk the migration chain from `input.version` upward,\n * picking the migration whose `from` matches the current version.\n * 3. Stop when `current.version === targetVersion`.\n * 4. If no migration matches at some point, throw `ConfigMigrationError`\n * with the missing step recorded for diagnostics.\n *\n * Migrations may be downward (e.g. for staged rollouts), but `targetVersion`\n * must be reachable strictly via the registered chain \u2014 there's no implicit\n * \"skip\" or transitive resolution.\n */\nexport function runConfigMigrations(\n input: Record<string, unknown>,\n targetVersion: number,\n migrations: readonly ConfigMigration[],\n): MigrationResult {\n const initial = typeof input['version'] === 'number' ? (input['version'] as number) : 1;\n let current: Record<string, unknown> = { ...input };\n let currentVersion = initial;\n const applied: string[] = [];\n let shouldPersist = false;\n\n let guard = 0;\n while (currentVersion !== targetVersion) {\n if (++guard > 100) {\n throw new ConfigMigrationError({\n message: `Config migration looped past 100 steps (from v${initial} toward v${targetVersion})`,\n fromVersion: initial,\n targetVersion,\n missingStep: currentVersion,\n });\n }\n const step = migrations.find((m) => m.from === currentVersion);\n if (!step) {\n throw new ConfigMigrationError({\n message: `No migration registered from config v${currentVersion} (target v${targetVersion}). Update the framework or revert the config file.`,\n fromVersion: initial,\n targetVersion,\n missingStep: currentVersion,\n });\n }\n const ctx: MigrationContext = { fromVersion: currentVersion, shouldPersist: false };\n const next = step.migrate(current, ctx);\n // Ensure the migration set the new version. Be tolerant: if it didn't,\n // patch it in so the chain doesn't infinite-loop on author oversight.\n if (typeof next['version'] !== 'number' || next['version'] !== step.to) {\n next['version'] = step.to;\n }\n current = next;\n currentVersion = step.to;\n applied.push(`v${step.from}\u2192v${step.to}`);\n shouldPersist = shouldPersist || ctx.shouldPersist || step.from < step.to;\n }\n return { config: current, applied, shouldPersist };\n}\n\n/**\n * Default empty migration registry. Real migrations are appended as new\n * Config versions are introduced. Example (when v2 lands):\n *\n * export const CONFIG_MIGRATIONS: readonly ConfigMigration[] = [\n * {\n * from: 1, to: 2, describe: 'rename `apiKey` \u2192 `auth.apiKey`',\n * migrate(cfg) {\n * const apiKey = cfg.apiKey;\n * delete cfg.apiKey;\n * return { ...cfg, auth: { ...(cfg.auth ?? {}), apiKey } };\n * },\n * },\n * ];\n */\nexport const DEFAULT_CONFIG_MIGRATIONS: readonly ConfigMigration[] = [];\n", "import * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { SessionStore } from '../types/session.js';\nimport { ensureDir } from '../utils/atomic-write.js';\nimport { isPidAlive } from '../utils/pid.js';\nimport { sessionContentPreview, userInputTitle } from './session-helpers.js';\n\n/**\n * Per-project lockfile used for crash detection. The CLI writes one of\n * these alongside the session JSONLs (`<projectSessions>/active.json`)\n * when an interactive run starts, and deletes it on clean exit. If we\n * find one on the next launch whose owning PID is dead (or whose host\n * doesn't match), we know the previous run was killed mid-flight and\n * the session it was writing to is a recovery candidate.\n *\n * The lockfile is intentionally per-project (already isolated by\n * `wpaths.projectSessions`), so two TUIs in two different repos do not\n * fight each other.\n */\nexport interface RecoveryLockOptions {\n /** Directory the lockfile lives in. Usually `wpaths.projectSessions`. */\n dir: string;\n /** This process's PID. Default: `process.pid`. */\n pid?: number | undefined;\n /** Hostname recorded for the lock. Default: `os.hostname()`. */\n hostname?: string | undefined;\n /** Locks older than this are considered orphaned (disk wiped, etc.). Default 24h. */\n maxAgeMs?: number | undefined;\n /** Used to check whether the abandoned session was actually closed cleanly. */\n sessionStore?: SessionStore | undefined;\n /**\n * Override the PID-liveness probe. Default: `process.kill(pid, 0)` \u2014\n * succeeds (or throws EPERM) when the PID is alive, throws ESRCH when\n * it is gone. Tests inject a deterministic stub.\n */\n isPidAlive?: ((pid: number) => boolean) | undefined;\n}\n\nexport interface AbandonedSession {\n sessionId: string;\n pid: number;\n startedAt: string;\n /** Lockfile age in ms at the time of the check. */\n ageMs: number;\n /** Number of messages already on disk for this session. */\n messageCount: number;\n /** Auto-derived session title from the first persisted user request. */\n title?: string | undefined;\n /** Latest persisted user request, compacted for the recovery prompt. */\n lastUserMessage?: string | undefined;\n}\n\ninterface LockFile {\n v: 1;\n sessionId: string;\n pid: number;\n hostname: string;\n startedAt: string;\n}\n\nconst LOCK_FILE = 'active.json';\nconst DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n\n/**\n * @deprecated Production TUI/WebUI/SimpleUI hosts use per-session\n * {@link SessionRegistry} ownership. Retained only for API compatibility with\n * older embedders; it must not be used as a project-wide runtime lock.\n */\nexport class RecoveryLock {\n private readonly file: string;\n private readonly pid: number;\n private readonly hostname: string;\n private readonly maxAgeMs: number;\n private readonly sessionStore?: SessionStore | undefined;\n private readonly probe: (pid: number) => boolean;\n\n constructor(opts: RecoveryLockOptions) {\n this.file = path.join(opts.dir, LOCK_FILE);\n this.pid = opts.pid ?? process.pid;\n this.hostname = opts.hostname ?? os.hostname();\n this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;\n this.sessionStore = opts.sessionStore;\n this.probe = opts.isPidAlive ?? defaultIsPidAlive;\n }\n\n /**\n * Examine the lockfile and decide whether it represents an abandoned\n * session. Returns `null` if the file is missing, points to a live\n * instance, references a clean-closed session, is too old, or is\n * malformed. Otherwise returns enough detail to prompt the user.\n *\n * Important: this is a read-only check. We never delete an active\n * lock from here \u2014 if another wstack instance is alive, the caller\n * should bail or run with a fresh session instead.\n */\n async checkAbandoned(): Promise<AbandonedSession | null> {\n const lock = await this.readLock();\n if (!lock) return null;\n\n const ageMs = Date.now() - new Date(lock.startedAt).getTime();\n if (Number.isNaN(ageMs) || ageMs < 0) {\n // Clock skew or corrupted timestamp \u2014 treat as orphan. Clean up the\n // stale lockfile so a subsequent write() with O_EXCL doesn't fail.\n await this.clear().catch(() => undefined);\n return null;\n }\n if (ageMs > this.maxAgeMs) {\n // Lock is older than the max-age window \u2014 the owning process is\n // guaranteed dead (disk wipe, OS reinstall, 24h+ uptime since crash).\n // Clean up the stale file so a subsequent write() doesn't hit EEXIST.\n await this.clear().catch(() => undefined);\n return null;\n }\n\n // PID liveness only meaningful on the same host. Different host\n // means we can't probe \u2014 assume abandoned (the other machine's\n // wstack can't be holding *our* sessions dir unless it was\n // shared via network mount, in which case the user is on their\n // own).\n if (lock.hostname === this.hostname && this.probe(lock.pid)) {\n // Another wstack on this box is actively writing here.\n return null;\n }\n\n let messageCount = 0;\n let title: string | undefined;\n let lastUserMessage: string | undefined;\n if (this.sessionStore) {\n try {\n const data = await this.sessionStore.load(lock.sessionId);\n // Closed means the LAST session_end is not followed by further\n // conversation activity. Legacy /save wrote mid-stream session_end\n // markers \u2014 `some()` would treat a session that crashed AFTER such a\n // marker as cleanly closed and silently skip recovery.\n const lastEnd = data.events.findLastIndex((e) => e.type === 'session_end');\n const closed =\n lastEnd >= 0 &&\n !data.events\n .slice(lastEnd + 1)\n .some(\n (e) =>\n e.type === 'user_input' ||\n e.type === 'llm_response' ||\n e.type === 'in_flight_start',\n );\n if (closed) return null;\n messageCount = data.messages.length;\n for (const event of data.events) {\n if (event.type !== 'user_input') continue;\n if (title === undefined) title = userInputTitle(event.content);\n lastUserMessage = sessionContentPreview(event.content);\n }\n } catch {\n // Lock points to a session that doesn't exist on disk (deleted\n // out from under us). Nothing to recover.\n return null;\n }\n }\n\n return {\n sessionId: lock.sessionId,\n pid: lock.pid,\n startedAt: lock.startedAt,\n ageMs,\n messageCount,\n ...(title !== undefined ? { title } : {}),\n ...(lastUserMessage !== undefined ? { lastUserMessage } : {}),\n };\n }\n\n /**\n * Claim the lock for the given session. Uses exclusive-create (`O_EXCL`)\n * to detect whether another process acquired the lock between our\n * `checkAbandoned()` call and now. If the file already exists, it means\n * another process won the race and we throw instead of silently\n * overwriting their recovery record.\n *\n * The caller MUST have already called `checkAbandoned()` and handled its\n * null return before calling this.\n */\n async write(sessionId: string): Promise<void> {\n await ensureDir(path.dirname(this.file));\n const lock: LockFile = {\n v: 1,\n sessionId,\n pid: this.pid,\n hostname: this.hostname,\n startedAt: new Date().toISOString(),\n };\n // O_EXCL: atomic create \u2014 fails with EEXIST if another process wrote\n // the file between our checkAbandoned() and this write. This prevents\n // two processes that scanned the same stale lock from both believing\n // they hold it. The atomicWrite approach (temp+rename) would silently\n // replace on POSIX, hiding the race.\n try {\n await fsp.writeFile(this.file, JSON.stringify(lock), { flag: 'wx', mode: 0o600 });\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'EEXIST') {\n throw new Error(`Recovery lock already held by another process`);\n }\n /* v8 ignore next -- defensive: an unexpected (non-EEXIST) write failure is rethrown */\n throw err;\n }\n }\n\n /**\n * Release the lock. Idempotent \u2014 silently succeeds if the file is\n * already gone (e.g. someone else cleared it, or the directory was\n * wiped).\n */\n async clear(): Promise<void> {\n try {\n await fsp.unlink(this.file);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') return;\n /* v8 ignore next -- defensive: an unexpected (non-ENOENT) unlink failure is rethrown */\n throw err;\n }\n }\n\n private async readLock(): Promise<LockFile | null> {\n let raw: string;\n try {\n raw = await fsp.readFile(this.file, 'utf8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') return null;\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!isLockFile(parsed)) return null;\n return parsed;\n } catch {\n return null;\n }\n }\n}\n\nfunction isLockFile(v: unknown): v is LockFile {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n return (\n o['v'] === 1 &&\n typeof o['sessionId'] === 'string' &&\n typeof o['pid'] === 'number' &&\n typeof o['hostname'] === 'string' &&\n typeof o['startedAt'] === 'string'\n );\n}\n\n/** Default probe \u2014 the shared helper. Tests inject a deterministic stub. */\nconst defaultIsPidAlive = isPidAlive;\n", "import { DefaultSecretScrubber } from '../security/secret-scrubber.js';\nimport type { ContentBlock } from '../types/blocks.js';\nimport type { SecretScrubber } from '../types/secret-scrubber.js';\nimport type { SessionData, SessionEvent, SessionMetadata, SessionStore } from '../types/session.js';\nimport type {\n DefaultSessionReaderOptions,\n SessionExportOptions,\n SessionQuery,\n SessionReader,\n SessionSearchHit,\n SessionSearchQuery,\n SessionSummaryLite,\n} from '../types/session-reader.js';\nimport { expectDefined } from '../utils/expect-defined.js';\nimport { compileUserRegex } from '../utils/regex-guard.js';\nimport {\n scrubPersistedSessionData,\n scrubPersistedSessionEvent,\n scrubPersistedSessionSummary,\n} from './session-read-scrubber.js';\n\n/**\n * L2-A: read-only view over a `SessionStore` with query, replay, search,\n * and export helpers. Implemented on top of the public `SessionStore`\n * surface so any concrete store can be inspected without re-implementation.\n */\nexport class DefaultSessionReader implements SessionReader {\n private readonly store: SessionStore;\n private readonly secretScrubber: SecretScrubber;\n\n constructor(opts: DefaultSessionReaderOptions) {\n this.store = opts.store;\n this.secretScrubber = opts.secretScrubber ?? new DefaultSecretScrubber();\n }\n\n private async loadCachedSessionData(sessionId: string): Promise<SessionData> {\n return scrubPersistedSessionData(await this.store.load(sessionId), this.secretScrubber);\n }\n\n async query(q: SessionQuery = {}): Promise<SessionSummaryLite[]> {\n // Prefer the store's filtered list when available \u2014 it pushes the\n // filter into the cached index instead of fetching 1000 + linear scan.\n const storeWithFilter = this.store as SessionStore & {\n listFiltered?:\n | ((criteria: {\n since?: string | undefined;\n until?: string | undefined;\n provider?: string | undefined;\n model?: string | undefined;\n minTokens?: number | undefined;\n titleContains?: string | undefined;\n limit?: number | undefined;\n }) => Promise<import('../types/session.js').SessionSummary[]>)\n | undefined;\n };\n let raw: import('../types/session.js').SessionSummary[];\n if (typeof storeWithFilter.listFiltered === 'function') {\n raw = await storeWithFilter.listFiltered({\n since: q.since,\n until: q.until,\n provider: q.provider,\n model: q.model,\n minTokens: q.minTokens,\n titleContains: q.titleContains,\n limit: q.limit,\n });\n } else {\n const fetched = await this.store.list(q.limit ? Math.max(q.limit, 100) : 1000);\n const titleNeedle = q.titleContains?.toLowerCase();\n raw = fetched.filter((s) => {\n if (q.since && s.startedAt < q.since) return false;\n if (q.until && s.startedAt > q.until) return false;\n if (q.provider && s.provider !== q.provider) return false;\n if (q.model && s.model !== q.model) return false;\n if (q.minTokens !== undefined && s.tokenTotal < q.minTokens) return false;\n if (titleNeedle && !s.title.toLowerCase().includes(titleNeedle)) return false;\n return true;\n });\n }\n const out: SessionSummaryLite[] = raw.map((rawSummary) => {\n const s = scrubPersistedSessionSummary(rawSummary, this.secretScrubber);\n return {\n id: s.id,\n title: s.title,\n startedAt: s.startedAt,\n provider: s.provider,\n model: s.model,\n tokenTotal: s.tokenTotal,\n };\n });\n return q.limit ? out.slice(0, q.limit) : out;\n }\n\n async *replay(sessionId: string): AsyncIterable<SessionEvent> {\n const data = await this.loadCachedSessionData(sessionId);\n for (const e of data.events) yield e;\n }\n\n async search(\n q: SessionSearchQuery,\n sessionId?: string | undefined,\n sessionQuery?: SessionQuery,\n ): Promise<SessionSearchHit[]> {\n const limit = q.limit ?? 100;\n const matcher = buildMatcher(q);\n const allowedTypes = q.types ? new Set(q.types) : null;\n\n // Filter sessions BEFORE scanning events \u2014 avoids touching the JSONL\n // for sessions that don't match the time/provider/model criteria.\n let ids: string[];\n if (sessionId) {\n ids = [sessionId];\n } else {\n // Prefer the store's filtered list when available \u2014 avoids fetching\n // 1000 sessions and linear-filtering in-process.\n const storeWithFilter = this.store as SessionStore & {\n listFiltered?:\n | ((criteria: {\n since?: string | undefined;\n until?: string | undefined;\n provider?: string | undefined;\n model?: string | undefined;\n minTokens?: number | undefined;\n titleContains?: string | undefined;\n limit?: number | undefined;\n }) => Promise<import('../types/session.js').SessionSummary[]>)\n | undefined;\n };\n let sessions: import('../types/session.js').SessionSummary[];\n if (typeof storeWithFilter.listFiltered === 'function') {\n sessions = await storeWithFilter.listFiltered({\n since: sessionQuery?.since,\n until: sessionQuery?.until,\n provider: sessionQuery?.provider,\n model: sessionQuery?.model,\n minTokens: sessionQuery?.minTokens,\n titleContains: sessionQuery?.titleContains,\n limit: 1000,\n });\n } else {\n sessions = await this.store.list(1000);\n const titleNeedle = sessionQuery?.titleContains?.toLowerCase();\n sessions = sessions.filter((s) => {\n if (sessionQuery?.since && s.startedAt < sessionQuery.since) return false;\n if (sessionQuery?.until && s.startedAt > sessionQuery.until) return false;\n if (sessionQuery?.provider && s.provider !== sessionQuery.provider) return false;\n if (sessionQuery?.model && s.model !== sessionQuery.model) return false;\n if (sessionQuery?.minTokens !== undefined && s.tokenTotal < sessionQuery.minTokens)\n return false;\n if (titleNeedle && !s.title.toLowerCase().includes(titleNeedle)) return false;\n return true;\n });\n }\n ids = sessions.map((s) => s.id);\n }\n\n const hits: SessionSearchHit[] = [];\n\n // Fast path: when the underlying store supports streaming search,\n // walk each session's JSONL line-by-line and bail out the moment we\n // hit `limit`. This avoids reading + parsing the entire file (which\n // `load()` does) and never reuses `_loadCache`, so concurrent\n // analytics queries don't churn the writer-side cache.\n const streaming = this.store.searchEvents?.bind(this.store);\n if (streaming) {\n for (const id of ids) {\n // Scrub once per event and reuse the scrubbed form for both\n // matching and the returned hit. Keyed by eventIndex so a store\n // that returns a different object reference than the one passed to\n // the predicate still gets the reader-owned scrubbed event.\n const scrubbedByIndex = new Map<number, SessionEvent>();\n const matched = await streaming(\n id,\n (rawEvent, eventIndex) => {\n // Scrub the event for matching against the search query. This\n // is redundant when the store scrubs before invoking the\n // predicate (DefaultSessionStore does), but the reader works\n // with any SessionStore impl \u2014 a custom store that returns raw\n // plaintext must not leak secrets into the matcher's text path.\n const ev = scrubPersistedSessionEvent(rawEvent, this.secretScrubber);\n if (allowedTypes && !allowedTypes.has(ev.type)) return false;\n const text = eventText(ev);\n if (text === null) return false;\n const matched = matcher(text) !== null;\n if (matched) scrubbedByIndex.set(eventIndex, ev);\n return matched;\n },\n { limit: limit - hits.length },\n );\n for (const m of matched) {\n // Reuse the already-scrubbed event captured during matching\n // (falls back to scrubbing m.event for stores that synthesize a\n // different object reference than the predicate received).\n const event =\n scrubbedByIndex.get(m.eventIndex) ??\n scrubPersistedSessionEvent(m.event, this.secretScrubber);\n const text = expectDefined(eventText(event));\n const hit = expectDefined(matcher(text));\n hits.push({\n sessionId: id,\n eventIndex: m.eventIndex,\n ts: m.ts,\n type: event.type,\n snippet: snippetOf(text, hit.start, hit.end),\n });\n if (hits.length >= limit) return hits;\n }\n }\n return hits;\n }\n\n // Fallback: stores that don't implement streaming. Loads the full\n // event stream per session \u2014 necessary for in-memory or non-file\n // stores that don't expose a streaming surface.\n for (const id of ids) {\n let data;\n try {\n data = await this.loadCachedSessionData(id);\n } catch {\n continue;\n }\n for (let i = 0; i < data.events.length; i++) {\n const ev = expectDefined(data.events[i]);\n if (allowedTypes && !allowedTypes.has(ev.type)) continue;\n const text = eventText(ev);\n if (text === null) continue;\n const hit = matcher(text);\n if (!hit) continue;\n hits.push({\n sessionId: id,\n eventIndex: i,\n ts: ev.ts,\n type: ev.type,\n snippet: snippetOf(text, hit.start, hit.end),\n });\n if (hits.length >= limit) return hits;\n }\n }\n return hits;\n }\n\n async export(sessionId: string, opts: SessionExportOptions): Promise<string> {\n const data = await this.loadCachedSessionData(sessionId);\n const includeTools = opts.includeTools ?? true;\n const includeDiagnostics = opts.includeDiagnostics ?? true;\n\n const filtered = data.events.filter((e) => {\n if (\n !includeTools &&\n (e.type === 'tool_use' ||\n e.type === 'tool_result' ||\n e.type === 'tool_call_start' ||\n e.type === 'tool_call_end')\n ) {\n return false;\n }\n if (\n !includeDiagnostics &&\n (e.type === 'error' || e.type === 'compaction' || e.type === 'message_truncated')\n ) {\n return false;\n }\n return true;\n });\n\n if (opts.format === 'json') {\n return JSON.stringify({ metadata: data.metadata, events: filtered }, null, 2);\n }\n if (opts.format === 'text') {\n return renderPlainText(data.metadata, filtered);\n }\n return renderMarkdown(data.metadata, filtered);\n }\n\n async metadata(sessionId: string): Promise<SessionMetadata> {\n const data = await this.loadCachedSessionData(sessionId);\n return data.metadata;\n }\n}\n\nfunction buildMatcher(\n q: SessionSearchQuery,\n): (text: string) => { start: number; end: number } | null {\n const ci = q.caseInsensitive ?? true;\n if (q.regex) {\n const flags = ci ? 'i' : '';\n const compiled = compileUserRegex(q.query, flags);\n if (!compiled.ok) {\n throw new Error(`Invalid search regex \"${q.query}\": ${compiled.reason}`);\n }\n const re = compiled.regex;\n return (text) => {\n const m = re.exec(text);\n return m ? { start: m.index, end: m.index + m[0].length } : null;\n };\n }\n const needle = ci ? q.query.toLowerCase() : q.query;\n return (text) => {\n const hay = ci ? text.toLowerCase() : text;\n const idx = hay.indexOf(needle);\n return idx === -1 ? null : { start: idx, end: idx + needle.length };\n };\n}\n\nfunction eventText(e: SessionEvent): string | null {\n switch (e.type) {\n case 'user_input':\n return contentToString(e.content);\n case 'llm_response':\n return contentToString(e.content);\n case 'tool_use':\n return `${e.name} ${JSON.stringify(e.input)}`;\n case 'tool_result':\n return typeof e.content === 'string' ? e.content : JSON.stringify(e.content);\n case 'error':\n return `${e.phase}: ${e.message}`;\n case 'session_start':\n case 'session_resumed':\n return `${e.model}/${e.provider}`;\n case 'task_created':\n case 'task_completed':\n return e.title;\n case 'task_failed':\n return `${e.title}: ${e.error}`;\n case 'skill_activated':\n case 'skill_deactivated':\n return e.skillName;\n default:\n return null;\n }\n}\n\nfunction contentToString(content: string | ContentBlock[]): string {\n if (typeof content === 'string') return content;\n return content\n .map((b) => {\n switch (b.type) {\n case 'text':\n return b.text;\n case 'tool_use':\n return `[tool_use:${b.name} ${JSON.stringify(b.input)}]`;\n case 'tool_result':\n return typeof b.content === 'string' ? b.content : JSON.stringify(b.content);\n default:\n return '';\n }\n })\n .join('\\n');\n}\n\nconst SNIPPET_RADIUS = 60;\n\nfunction snippetOf(text: string, start: number, end: number): string {\n const from = Math.max(0, start - SNIPPET_RADIUS);\n const to = Math.min(text.length, end + SNIPPET_RADIUS);\n const prefix = from > 0 ? '\u2026' : '';\n const suffix = to < text.length ? '\u2026' : '';\n return prefix + text.slice(from, to).replace(/\\s+/g, ' ').trim() + suffix;\n}\n\nfunction renderMarkdown(meta: SessionMetadata, events: SessionEvent[]): string {\n const lines: string[] = [];\n lines.push(`# Session ${meta.id}`);\n lines.push('');\n if (meta.model || meta.provider) {\n lines.push(`- **Model:** ${meta.provider ?? '?'}/${meta.model ?? '?'}`);\n }\n lines.push(`- **Started:** ${meta.startedAt}`);\n if (meta.endedAt) lines.push(`- **Ended:** ${meta.endedAt}`);\n lines.push('');\n lines.push('---');\n lines.push('');\n for (const e of events) {\n switch (e.type) {\n case 'user_input': {\n lines.push(`## User \u2014 ${e.ts}`);\n lines.push('');\n lines.push(contentToString(e.content));\n lines.push('');\n break;\n }\n case 'llm_response': {\n lines.push(`## Assistant \u2014 ${e.ts}`);\n lines.push('');\n lines.push(contentToString(e.content));\n if (e.stopReason && e.stopReason !== 'end_turn') {\n lines.push('');\n lines.push(`*stop: ${e.stopReason}*`);\n }\n lines.push('');\n break;\n }\n case 'tool_use': {\n lines.push(`### Tool call: \\`${e.name}\\``);\n lines.push('');\n lines.push('```json');\n lines.push(JSON.stringify(e.input, null, 2));\n lines.push('```');\n lines.push('');\n break;\n }\n case 'tool_result': {\n const body = typeof e.content === 'string' ? e.content : JSON.stringify(e.content, null, 2);\n lines.push(`### Tool result${e.isError ? ' (error)' : ''}`);\n lines.push('');\n lines.push('```');\n lines.push(body);\n lines.push('```');\n lines.push('');\n break;\n }\n case 'error': {\n lines.push(`> **Error** (${e.phase}): ${e.message}`);\n lines.push('');\n break;\n }\n case 'compaction': {\n lines.push(`> **Compaction**: ${e.before} \u2192 ${e.after} tokens`);\n lines.push('');\n break;\n }\n default:\n break;\n }\n }\n return lines.join('\\n');\n}\n\nfunction renderPlainText(meta: SessionMetadata, events: SessionEvent[]): string {\n const lines: string[] = [];\n lines.push(\n `Session ${meta.id} \u2014 ${meta.provider ?? '?'}/${meta.model ?? '?'} \u2014 started ${meta.startedAt}`,\n );\n lines.push(''.padEnd(72, '-'));\n for (const e of events) {\n switch (e.type) {\n case 'user_input':\n lines.push(`[${e.ts}] USER`);\n lines.push(contentToString(e.content));\n lines.push('');\n break;\n case 'llm_response':\n lines.push(`[${e.ts}] ASSISTANT`);\n lines.push(contentToString(e.content));\n lines.push('');\n break;\n case 'tool_use':\n lines.push(`[${e.ts}] TOOL_USE ${e.name} ${JSON.stringify(e.input)}`);\n break;\n case 'tool_result':\n lines.push(\n `[${e.ts}] TOOL_RESULT${e.isError ? ' (error)' : ''} ${\n typeof e.content === 'string' ? e.content : JSON.stringify(e.content)\n }`,\n );\n break;\n case 'error':\n lines.push(`[${e.ts}] ERROR (${e.phase}): ${e.message}`);\n break;\n default:\n break;\n }\n }\n return lines.join('\\n');\n}\n", "import { randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport type { EventBus } from '../kernel/events.js';\nimport { ERROR_CODES, WrongStackError } from '../types/errors.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { expectDefined } from '../utils/expect-defined.js';\nimport { sessionScopedPath } from '../utils/session-scoped-path.js';\n/**\n * L2-B: AnnotationsStore \u2014 sidecar storage for collaboration annotations\n * (Phase 2 of idea #13 from IDEAS.md).\n *\n * Why a sidecar file, not the session JSONL?\n *\n * The session log is an event-sourced append-only journal\n * (`packages/core/src/types/session.ts` invariant: events are\n * append-only, with `truncateToCheckpoint` only as an explicit\n * rewind). Mixing in human-typed annotations would break that\n * invariant \u2014 the user's note about \"this rm looks dangerous\"\n * is not part of the agent's event history; it is meta-commentary\n * on the history.\n *\n * So we keep annotations in a sibling file: one JSON document per\n * session, resolved with `sessionScopedPath(sessionDir, sessionId,\n * '.annotations.json')`. The shape is a simple versioned array,\n * written atomically.\n *\n * Concurrency model:\n *\n * The store uses a per-session Promise chain to serialize writes.\n * Multiple annotators adding notes at the same time will queue,\n * not race. The atomic write itself is the second line of\n * defense (in case the chain is bypassed \u2014 e.g. two processes\n * pointing at the same dir).\n */\n\n/** Wire/storage shape for one annotation. */\nexport interface Annotation {\n /** Stable id (UUIDv4-ish). Referenced by resolve/delete. */\n id: string;\n /** Session this annotation belongs to. */\n sessionId: string;\n /** Index into the session event log the annotation refers to. */\n atEventIndex: number;\n /** Participant id of the annotator (matches WSCollabParticipantJoined.participantId). */\n authorId: string;\n /** Human-readable role label snapshot for display (e.g. \"annotator\"). */\n authorRole: 'annotator';\n /** The note itself. Trimmed, capped at `MAX_TEXT_LENGTH` on add. */\n text: string;\n /** ISO timestamp of creation. */\n createdAt: string;\n /** Resolved state. Annotations start unresolved. */\n resolved: boolean;\n /** ISO timestamp when resolved (if resolved). */\n resolvedAt?: string | undefined;\n /** Participant id of the resolver (if resolved). */\n resolvedBy?: string | undefined;\n}\n\ninterface AnnotationsFile {\n /** Bumped when the on-disk shape changes. v1 = initial release. */\n version: 1;\n annotations: Annotation[];\n}\n\n/** Bumped when the on-disk shape changes. Bump + migration on change. */\nconst FILE_VERSION = 1;\n/** Hard cap to keep a runaway annotator from writing megabytes. */\nconst MAX_TEXT_LENGTH = 2000;\n/** Hard cap on total annotations per session (oldest are evicted beyond this). */\nconst MAX_ANNOTATIONS = 1000;\n\nexport interface AnnotationsStoreOptions {\n /** Root sessions directory used with `sessionScopedPath(..., '.annotations.json')`. */\n dir: string;\n events?: EventBus;\n traceId?: string;\n /**\n * Override the max annotations per session (default 1000).\n * Exposed for tests to avoid 1001 sequential disk writes on every eviction test.\n */\n maxAnnotations?: number;\n}\n\nexport class AnnotationsStore {\n private readonly dir: string;\n private readonly events: EventBus | undefined;\n private readonly traceId: string | undefined;\n private readonly maxAnnotations: number;\n /** Per-session write queue. Created lazily on first add. */\n private readonly writeChains = new Map<string, Promise<void>>();\n\n constructor(opts: AnnotationsStoreOptions) {\n this.dir = opts.dir;\n this.events = opts.events;\n this.traceId = opts.traceId;\n this.maxAnnotations = opts.maxAnnotations ?? MAX_ANNOTATIONS;\n }\n\n // \u2500\u2500 Reads \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Return all annotations for `sessionId` in insertion order\n * (oldest first). Returns an empty array when no file exists\n * yet (the normal case for a fresh session) and also degrades\n * gracefully to `[]` on a read error (permissions, corruption) \u2014\n * the failure is still surfaced via a `storage.read` event so it\n * never silently hides I/O problems from observers.\n */\n async list(sessionId: string): Promise<Annotation[]> {\n const t0 = Date.now();\n const fp = this.filePath(sessionId);\n try {\n const file = await this.readFile(sessionId);\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.read', {\n sessionId,\n store: 'annotations',\n filePath: fp,\n operation: 'list',\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return file ? file.annotations : [];\n } catch (err) {\n this.events?.emit('storage.read', {\n sessionId,\n store: 'annotations',\n filePath: fp,\n operation: 'list',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: toErrorMessage(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return [];\n }\n }\n\n /**\n * Convenience: only unresolved annotations, newest first \u2014 the\n * common UI rendering for \"what still needs attention?\".\n */\n async listOpen(sessionId: string): Promise<Annotation[]> {\n const all = await this.list(sessionId);\n return all.filter((a) => !a.resolved).reverse();\n }\n\n // \u2500\u2500 Writes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Add a new annotation. Returns the persisted record (with id\n * and timestamps filled in). Throws when `text` is empty or\n * exceeds `MAX_TEXT_LENGTH`.\n */\n async add(input: {\n sessionId: string;\n atEventIndex: number;\n authorId: string;\n text: string;\n }): Promise<Annotation> {\n const text = input.text.trim();\n if (text.length === 0) {\n throw new WrongStackError({\n message: 'Annotation text must be non-empty',\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n context: { field: 'text', sessionId: input.sessionId },\n });\n }\n if (text.length > MAX_TEXT_LENGTH) {\n throw new WrongStackError({\n message: `Annotation text exceeds ${MAX_TEXT_LENGTH} chars (got ${text.length})`,\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n context: { field: 'text', maxLength: MAX_TEXT_LENGTH, actualLength: text.length },\n });\n }\n if (!Number.isInteger(input.atEventIndex) || input.atEventIndex < 0) {\n throw new WrongStackError({\n message: 'atEventIndex must be a non-negative integer',\n code: ERROR_CODES.VALIDATION_ERROR,\n subsystem: 'general',\n context: { field: 'atEventIndex', value: input.atEventIndex },\n });\n }\n const annotation: Annotation = {\n id: randomUUID(),\n sessionId: input.sessionId,\n atEventIndex: input.atEventIndex,\n authorId: input.authorId,\n authorRole: 'annotator',\n text,\n createdAt: new Date().toISOString(),\n resolved: false,\n };\n const fp = this.filePath(input.sessionId);\n const t0 = Date.now();\n try {\n await this.enqueue(input.sessionId, async () => {\n await withFileLock(fp, async () => {\n const all = await this.list(input.sessionId);\n all.push(annotation);\n // Evict oldest if we crossed the cap. Resolved first, then oldest.\n if (all.length > this.maxAnnotations) {\n const sorted = all\n .map((a, i) => ({ a, i }))\n .sort((x, y) => {\n // resolved=false wins (keep unresolved); among same resolved state, oldest first.\n /* v8 ignore next -- scale+tiebreak: needs >1000 annotations with mixed resolved states */\n if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;\n return x.a.createdAt.localeCompare(y.a.createdAt);\n });\n const evictCount = all.length - this.maxAnnotations;\n const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));\n const kept = all.filter((a) => !toEvict.has(a.id));\n await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.write', {\n sessionId: input.sessionId,\n store: 'annotations',\n filePath: fp,\n operation: 'evict',\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n } else {\n await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.write', {\n sessionId: input.sessionId,\n store: 'annotations',\n filePath: fp,\n operation: 'add',\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n }\n });\n });\n return annotation;\n } catch (err) {\n this.events?.emit('storage.error', {\n sessionId: input.sessionId,\n store: 'annotations',\n filePath: fp,\n operation: 'add',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: false,\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n /**\n * Mark an annotation as resolved. Returns the updated record, or\n * `null` if no annotation with that id exists in this session.\n * Idempotent: resolving an already-resolved annotation refreshes\n * `resolvedAt` / `resolvedBy` to the latest call.\n */\n async resolve(input: {\n sessionId: string;\n annotationId: string;\n resolvedBy: string;\n }): Promise<Annotation | null> {\n let updated: Annotation | null = null;\n const fp = this.filePath(input.sessionId);\n const t0 = Date.now();\n try {\n await this.enqueue(input.sessionId, async () => {\n await withFileLock(fp, async () => {\n const all = await this.list(input.sessionId);\n const idx = all.findIndex((a) => a.id === input.annotationId);\n if (idx === -1) {\n updated = null;\n return;\n }\n const next: Annotation = {\n ...expectDefined(all[idx]),\n resolved: true,\n resolvedAt: new Date().toISOString(),\n resolvedBy: input.resolvedBy,\n };\n all[idx] = next;\n await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });\n updated = next;\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.write', {\n sessionId: input.sessionId,\n store: 'annotations',\n filePath: fp,\n operation: 'resolve',\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n });\n });\n return updated;\n } catch (err) {\n this.events?.emit('storage.error', {\n sessionId: input.sessionId,\n store: 'annotations',\n filePath: fp,\n operation: 'resolve',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: false,\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n // \u2500\u2500 Internals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private filePath(sessionId: string): string {\n // Containment-checked: date-sharded ids (\"2026-06-11/<base>\") are\n // legitimate; traversal is rejected. A plain slash ban here used to\n // throw for every modern session id, breaking annotations entirely.\n return sessionScopedPath(this.dir, sessionId, '.annotations.json');\n }\n\n private async readFile(sessionId: string): Promise<AnnotationsFile | null> {\n const fp = this.filePath(sessionId);\n let raw: string;\n try {\n raw = await fs.readFile(fp, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n // Non-ENOENT I/O errors (EACCES, ENOSPC): re-throw so callers emit\n // storage.error.\n throw err;\n }\n try {\n const parsed = JSON.parse(raw) as AnnotationsFile;\n if (parsed.version !== FILE_VERSION) {\n return { version: FILE_VERSION, annotations: [] };\n }\n return parsed;\n } catch {\n // JSON parse error (SyntaxError): treat as empty store \u2014 not an I/O failure.\n return null;\n }\n }\n\n private async writeFile(sessionId: string, file: AnnotationsFile): Promise<void> {\n const fp = this.filePath(sessionId);\n await atomicWrite(fp, JSON.stringify(file, null, 2));\n }\n\n /**\n * Serialize writes per-sessionId. We chain promises instead of\n * using a Mutex class so the contract is obvious from the\n * call-site: `enqueue(sid, fn)` runs `fn` after every prior\n * enqueue for `sid` has settled.\n */\n private enqueue(sessionId: string, fn: () => Promise<void>): Promise<void> {\n const prev = this.writeChains.get(sessionId) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n // Keep the chain intact even when `fn` throws; failures\n // shouldn't break subsequent writes.\n const settled = next.catch(() => undefined);\n this.writeChains.set(sessionId, settled);\n void settled.finally(() => {\n if (this.writeChains.get(sessionId) === settled) this.writeChains.delete(sessionId);\n });\n return next;\n }\n}\n", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { EventBus } from '../kernel/events.js';\nimport { hashRequest } from '../replay/hash.js';\nimport type { Request, Response } from '../types/provider.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { safeParse } from '../utils/safe-json.js';\nimport { sessionScopedPath } from '../utils/session-scoped-path.js';\n\n/**\n * Surface the OS error code (EACCES, ENOSPC, \u2026) alongside the message in\n * storage.* event payloads. Codes are stable and locale-independent, so\n * they are what dashboards and alerts key on; the message is supplementary.\n */\nfunction storageErrorString(err: unknown): string {\n if (err instanceof Error) {\n const code = (err as NodeJS.ErrnoException).code;\n return code ? `${code}: ${err.message}` : err.message;\n }\n /* v8 ignore next -- defensive: fs/lock failures are always Error instances */\n return String(err);\n}\n\n/**\n * ReplayLogStore \u2014 sidecar store for deterministic-replay support\n * (idea #2 from IDEAS.md). One JSONL file per session, recording\n * every provider request/response pair so the same agent loop can\n * be re-run later with frozen API responses.\n *\n * Why a sidecar (not the session JSONL)?\n *\n * Same reason as `AnnotationsStore` \u2014 the session log is\n * event-sourced and append-only; a provider request payload can be\n * tens of kilobytes (especially with long conversation history),\n * and we want replay to be opt-in (recorded only when the user\n * runs with `--replay` or a future equivalent). Mixing it into\n * the event log would inflate every read for replay-irrelevant\n * paths.\n *\n * File layout: `sessionScopedPath(dir, sessionId, '.replay.jsonl')`,\n * one entry per line.\n * Each entry: `{ hash, ts, request, response }`. The `hash` is\n * computed via `hashRequest` so lookups are O(1) by hash.\n *\n * Concurrency: per-session write queue (same pattern as\n * `AnnotationsStore`). Reads are lock-free; the write chain makes\n * the append + rehash sequence atomic.\n */\nexport interface ReplayEntry {\n hash: string;\n ts: string;\n request: Request;\n response: Response;\n}\n\nconst FILE_VERSION = 1;\n\n/** Default cap on the number of entries per session. */\nconst DEFAULT_MAX_ENTRIES = 1000;\n\nexport interface ReplayLogStoreOptions {\n /** Root sessions directory used with `sessionScopedPath(..., '.replay.jsonl')`. */\n dir: string;\n /**\n * Cap on the number of entries per session. When a `record` would\n * push the file beyond this, the oldest entries are evicted (LRU\n * by insertion order). Set to `Infinity` to disable rotation.\n * Defaults to 1000 \u2014 a single LLM call averages ~5KB serialized\n * (messages + tools + response), so 1000 entries is ~5MB per\n * session which is a reasonable upper bound.\n */\n maxEntries?: number | undefined;\n events?: EventBus;\n traceId?: string;\n}\n\ninterface ReplayEntryLocation {\n entry?: ReplayEntry;\n offset: number;\n length: number;\n}\n\nexport class ReplayLogStore {\n private readonly dir: string;\n private readonly events: EventBus | undefined;\n private readonly traceId: string | undefined;\n private readonly writeChains = new Map<string, Promise<void>>();\n /** Per-session hash \u2192 on-disk location, with lazy entry hydration. */\n private readonly cache = new Map<string, Map<string, ReplayEntryLocation>>();\n /** Per-session entry count on disk, to detect when compaction is needed. */\n private readonly diskCount = new Map<string, number>();\n private readonly maxEntries: number;\n private static readonly MAX_CACHED_SESSIONS = 32;\n\n constructor(opts: ReplayLogStoreOptions) {\n this.dir = opts.dir;\n this.events = opts.events;\n this.traceId = opts.traceId;\n this.maxEntries = opts.maxEntries ?? DEFAULT_MAX_ENTRIES;\n }\n\n // \u2500\u2500 Writes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Record a request/response pair. Idempotent on hash: a second\n * `record` for the same hash is a no-op (the existing entry wins).\n * Returns the hash.\n */\n async record(input: {\n sessionId: string;\n request: Request;\n response: Response;\n }): Promise<string> {\n const hash = hashRequest(input.request);\n const fp = this.filePath(input.sessionId);\n const t0 = Date.now();\n try {\n await this.enqueue(input.sessionId, async () => {\n await withFileLock(fp, async () => {\n // Dedup via the in-memory hash map \u2014 O(1) instead of re-reading\n // and re-parsing the whole JSONL just to run `entries.some(...)`.\n // `ensureCache` populates both the cache and `diskCount` from a\n // single read on first contact; subsequent records reuse it.\n const cache = await this.ensureCache(input.sessionId);\n if (cache.has(hash)) return; // already recorded\n\n const entry: ReplayEntry = {\n hash,\n ts: new Date().toISOString(),\n request: input.request,\n response: input.response,\n };\n\n const currentCount = this.diskCount.get(input.sessionId) ?? 0;\n const willEvict = currentCount + 1 > this.maxEntries;\n\n if (!willEvict) {\n // Common path (the first `maxEntries` writes per session, plus\n // any session that never hits the cap): a single O(1) append.\n // The previous implementation did a full readAll + full rewrite\n // (atomicWrite of the entire file) on every single record, which\n // was quadratic in session length \u2014 a 1000-call session rewrote\n // a multi-MB file 1000 times.\n const line = JSON.stringify(entry) + '\\n';\n let offset = 0;\n try {\n const stat = await fs.stat(fp);\n offset = stat.size;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n await fs.appendFile(fp, line, 'utf8');\n cache.set(hash, { entry, offset, length: Buffer.byteLength(line, 'utf8') });\n this.diskCount.set(input.sessionId, currentCount + 1);\n this.events?.emit('storage.write', {\n sessionId: input.sessionId,\n store: 'replay',\n filePath: fp,\n operation: 'record',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return;\n }\n\n // Eviction path: we're at capacity, drop the oldest entry to make\n // room. This does require a full read + rewrite, but it fires at\n // most once per `maxEntries` writes (default 1000), not per write.\n const all = await this.readAll(input.sessionId);\n all.push(entry);\n const keep = all.slice(-this.maxEntries);\n const refreshed = new Map<string, ReplayEntryLocation>();\n let offset = 0;\n for (const e of keep) {\n const line = JSON.stringify(e) + '\\n';\n refreshed.set(e.hash, { entry: e, offset, length: Buffer.byteLength(line, 'utf8') });\n offset += Buffer.byteLength(line, 'utf8');\n }\n this.cache.set(input.sessionId, refreshed);\n this.diskCount.set(input.sessionId, keep.length);\n await this.writeAll(input.sessionId, keep, 'compact');\n });\n });\n return hash;\n } catch (err) {\n this.events?.emit('storage.error', {\n sessionId: input.sessionId,\n store: 'replay',\n filePath: fp,\n operation: 'record',\n outcome: 'failure',\n error: storageErrorString(err),\n recoverable: false,\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n // \u2500\u2500 Reads \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Look up an entry by hash. Returns `null` when the request has\n * not been recorded for this session. O(1) after the first call\n * per session (in-memory cache).\n */\n async lookup(sessionId: string, hash: string): Promise<ReplayEntry | null> {\n const fp = this.filePath(sessionId);\n const t0 = Date.now();\n try {\n const cache = await this.ensureCache(sessionId);\n const location = cache.get(hash);\n const entry = location ? await this.hydrateEntry(sessionId, hash, location) : null;\n this.events?.emit('storage.read', {\n sessionId,\n store: 'replay',\n filePath: fp,\n operation: 'lookup',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return entry;\n } catch (err) {\n this.events?.emit('storage.read', {\n sessionId,\n store: 'replay',\n filePath: fp,\n operation: 'lookup',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: storageErrorString(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n /** All recorded entries for a session, in insertion order. */\n async load(sessionId: string): Promise<ReplayEntry[]> {\n const fp = this.filePath(sessionId);\n const t0 = Date.now();\n try {\n const cache = await this.ensureCache(sessionId);\n const entries: ReplayEntry[] = [];\n for (const [hash, location] of cache) {\n entries.push(await this.hydrateEntry(sessionId, hash, location));\n }\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.read', {\n sessionId,\n store: 'replay',\n filePath: fp,\n operation: 'load',\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return entries;\n } catch (err) {\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.read', {\n sessionId,\n store: 'replay',\n filePath: fp,\n operation: 'load',\n outcome: 'failure',\n durationMs,\n error: storageErrorString(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n /**\n * List every session id that has a replay log in the store dir.\n * Returns an array of `{ sessionId, entryCount, path }` sorted\n * by sessionId for stable output. Used by `wstack replay --list`.\n */\n async list(): Promise<Array<{ sessionId: string; entryCount: number; path: string }>> {\n const out: Array<{ sessionId: string; entryCount: number; path: string }> = [];\n // Replay logs sit next to their session JSONL \u2014 flat at the root for\n // legacy/`record-<ts>` ids, inside a date-shard dir for modern ids.\n // Scan both levels; a root-only scan misses every sharded session.\n const scan = async (dir: string, prefix: string, depth: number): Promise<void> => {\n let entries: import('node:fs').Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch (err) {\n if (depth === 0 && (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n // EACCES, ENOTDIR, etc. \u2014 log the real error so the operator can\n // diagnose a misconfiguration, but still return empty list so the\n // caller (slash command display) doesn't crash.\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'replay_log_store.list_readdir_failed',\n dir,\n message: toErrorMessage(err),\n timestamp: new Date().toISOString(),\n }),\n );\n }\n return;\n }\n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue;\n if (entry.isDirectory()) {\n if (depth === 0) await scan(path.join(dir, entry.name), entry.name, depth + 1);\n continue;\n }\n if (!entry.isFile() || !entry.name.endsWith('.replay.jsonl')) continue;\n const base = entry.name.slice(0, -'.replay.jsonl'.length);\n const sessionId = prefix ? `${prefix}/${base}` : base;\n const fp = path.join(dir, entry.name);\n out.push({\n sessionId,\n entryCount: await this.countEntries(fp),\n path: fp,\n });\n }\n };\n await scan(this.dir, '', 0);\n return out.sort((a, b) => a.sessionId.localeCompare(b.sessionId));\n }\n\n // \u2500\u2500 Internals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private filePath(sessionId: string): string {\n // Containment-checked: date-sharded ids (\"2026-06-11/<base>\") are\n // legitimate; traversal is rejected. A plain slash ban would throw\n // the moment a real (sharded) session id is used for --replay.\n return sessionScopedPath(this.dir, sessionId, '.replay.jsonl');\n }\n\n private async countEntries(filePath: string): Promise<number> {\n // list() only needs a count, not provider request/response payloads. Count\n // non-empty JSONL rows in fixed-size chunks so large replay logs are not\n // parsed or materialized just to render `wstack replay --list`.\n const handle = await fs.open(filePath, 'r');\n const buffer = Buffer.allocUnsafe(64 * 1024);\n let count = 0;\n let hasNonWhitespace = false;\n try {\n while (true) {\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);\n if (bytesRead === 0) break;\n for (let i = 0; i < bytesRead; i++) {\n const ch = buffer[i]!;\n if (ch === 10) {\n if (hasNonWhitespace) count++;\n hasNonWhitespace = false;\n continue;\n }\n if (ch !== 13 && ch !== 32 && ch !== 9) {\n hasNonWhitespace = true;\n }\n }\n }\n } finally {\n await handle.close();\n }\n if (hasNonWhitespace) count++;\n return count;\n }\n\n private parseReplayLine(line: string): ReplayEntry | null {\n try {\n const parsed = safeParse<\n { version?: number | undefined; entry?: ReplayEntry | undefined } & ReplayEntry\n >(line);\n if (!parsed.ok || !parsed.value) return null;\n if ('entry' in parsed.value && parsed.value.entry) {\n return parsed.value.entry;\n }\n return parsed.value;\n } catch {\n return null;\n }\n }\n\n private async readAll(sessionId: string): Promise<ReplayEntry[]> {\n const fp = this.filePath(sessionId);\n try {\n const raw = await fs.readFile(fp, 'utf8');\n const out: ReplayEntry[] = [];\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n const parsed = this.parseReplayLine(line);\n if (parsed) out.push(parsed);\n }\n return out;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw err;\n }\n }\n\n private async writeAll(\n sessionId: string,\n entries: ReplayEntry[],\n operation: 'record' | 'compact' = 'record',\n ): Promise<void> {\n const fp = this.filePath(sessionId);\n const t0 = Date.now();\n const body = entries.map((e) => JSON.stringify(e)).join('\\n') + (entries.length ? '\\n' : '');\n await atomicWrite(fp, body);\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.write', {\n sessionId,\n store: 'replay',\n filePath: fp,\n operation,\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n // Drop the version-stamp comment at the top \u2014 v1 has no envelope,\n // but we keep a one-line marker for human readers / future tooling.\n // (The atomicWrite just wrote pure JSONL; that's correct for v1.)\n void FILE_VERSION;\n }\n\n private async ensureCache(sessionId: string): Promise<Map<string, ReplayEntryLocation>> {\n let cache = this.cache.get(sessionId);\n if (cache) {\n this.cache.delete(sessionId);\n this.cache.set(sessionId, cache);\n return cache;\n }\n\n const fp = this.filePath(sessionId);\n cache = new Map();\n try {\n const handle = await fs.open(fp, 'r');\n const CHUNK = 64 * 1024;\n const buffer = Buffer.alloc(CHUNK);\n let leftover = '';\n let leftoverOffset = 0;\n let fileOffset = 0;\n try {\n while (true) {\n const { bytesRead } = await handle.read(buffer, 0, CHUNK, fileOffset);\n if (bytesRead === 0) break;\n const chunk = buffer.subarray(0, bytesRead).toString('utf8');\n const text = leftover + chunk;\n let lineStart = leftoverOffset;\n let searchFrom = 0;\n while (true) {\n const newlineIndex = text.indexOf('\\n', searchFrom);\n if (newlineIndex === -1) break;\n const line = text.slice(searchFrom, newlineIndex);\n const lineLength = Buffer.byteLength(text.slice(searchFrom, newlineIndex + 1), 'utf8');\n if (line.trim()) {\n const entry = this.parseReplayLine(line);\n if (entry) {\n cache.set(entry.hash, { offset: lineStart, length: lineLength });\n }\n }\n lineStart += lineLength;\n searchFrom = newlineIndex + 1;\n }\n leftover = text.slice(searchFrom);\n leftoverOffset = lineStart;\n fileOffset += bytesRead;\n }\n if (leftover.trim()) {\n const entry = this.parseReplayLine(leftover);\n if (entry) {\n cache.set(entry.hash, {\n offset: leftoverOffset,\n length: Buffer.byteLength(leftover, 'utf8'),\n });\n }\n }\n } finally {\n await handle.close();\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;\n }\n\n while (this.cache.size >= ReplayLogStore.MAX_CACHED_SESSIONS) {\n const oldest = this.cache.keys().next().value;\n if (oldest === undefined) break;\n this.cache.delete(oldest);\n this.diskCount.delete(oldest);\n }\n this.cache.set(sessionId, cache);\n this.diskCount.set(sessionId, cache.size);\n return cache;\n }\n\n private async hydrateEntry(\n sessionId: string,\n hash: string,\n location: ReplayEntryLocation,\n ): Promise<ReplayEntry> {\n if (location.entry) return location.entry;\n\n const direct = await this.readEntryAt(sessionId, hash, location);\n if (direct) {\n location.entry = direct;\n return direct;\n }\n\n // The cached byte offset is stale: the file was compacted, or a second\n // process recording the same session appended and shifted every offset.\n // Reading there returns a wrong range (or a DIFFERENT valid line, which the\n // identity check below rejects). Rebuild the cache from disk once and read\n // at the fresh location rather than throwing on an intact file.\n this.cache.delete(sessionId);\n const fresh = await this.ensureCache(sessionId);\n const freshLoc = fresh.get(hash);\n if (freshLoc) {\n const entry = freshLoc.entry ?? (await this.readEntryAt(sessionId, hash, freshLoc));\n if (entry) {\n freshLoc.entry = entry;\n return entry;\n }\n }\n throw new Error(`Replay entry ${hash} is unreadable`);\n }\n\n /**\n * Read and parse the entry at `location`, returning it ONLY when its hash\n * matches `hash`. A stale offset can land on a different valid line; without\n * this identity check that line would be returned as if it were the\n * requested entry (silent wrong data). Returns null on any mismatch/failure.\n */\n private async readEntryAt(\n sessionId: string,\n hash: string,\n location: ReplayEntryLocation,\n ): Promise<ReplayEntry | null> {\n const fp = this.filePath(sessionId);\n let handle: fs.FileHandle;\n try {\n handle = await fs.open(fp, 'r');\n } catch {\n return null;\n }\n try {\n const buffer = Buffer.alloc(location.length);\n const { bytesRead } = await handle.read(buffer, 0, location.length, location.offset);\n const line = buffer.subarray(0, bytesRead).toString('utf8').trimEnd();\n const entry = this.parseReplayLine(line);\n return entry && entry.hash === hash ? entry : null;\n } finally {\n await handle.close();\n }\n }\n\n private enqueue(sessionId: string, fn: () => Promise<void>): Promise<void> {\n const prev = this.writeChains.get(sessionId) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n const settled = next.catch(() => undefined);\n this.writeChains.set(sessionId, settled);\n void settled.finally(() => {\n if (this.writeChains.get(sessionId) === settled) this.writeChains.delete(sessionId);\n });\n return next;\n }\n}\n", "import { createHash } from 'node:crypto';\nimport type { Request } from '../types/provider.js';\n\n/**\n * Idea #2 from IDEAS.md \u2014 Deterministic Replay.\n *\n * The hash function is the foundation of replay: given a `Request`,\n * produce a stable identifier so a recorded `Response` can be looked\n * up later when we want to \"re-run\" the same agent loop without\n * burning API credits.\n *\n * Stability rules:\n *\n * - All object keys are sorted recursively before stringification.\n * Without this, two semantically identical requests that differ\n * only in key insertion order would produce different hashes.\n * - We hash ONLY the fields that affect the response: `model`,\n * `system`, `messages`, `tools`, `maxTokens`, and the four\n * sampling knobs (`temperature`, `topP`, `stopSequences`,\n * `toolChoice`). Anything else on the `Request` (metadata,\n * future extensions) is ignored so replay stays forward-compat.\n * - We serialize to JSON. The `ContentBlock` and `Message` shapes\n * are pure data; this works as long as no `undefined` values\n * sneak in (those get dropped by `JSON.stringify`, which is\n * fine \u2014 the structural diff is what matters).\n *\n * The SHA-256 output is hex-encoded and prefixed with the algorithm\n * tag so a future migration to a different hash (e.g. blake3) is\n * trivial to detect.\n */\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(sortKeys(value));\n}\n\nfunction sortKeys(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeys);\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = sortKeys(obj[key]);\n }\n return sorted;\n }\n return value;\n}\n\nexport function hashRequest(request: Request): string {\n // Pick only the fields that affect the response. See stability rules.\n const payload = {\n model: request.model,\n system: request.system,\n messages: request.messages,\n tools: request.tools,\n maxTokens: request.maxTokens,\n temperature: request.temperature,\n topP: request.topP,\n stopSequences: request.stopSequences,\n toolChoice: request.toolChoice,\n };\n const json = stableStringify(payload);\n const digest = createHash('sha256').update(json, 'utf8').digest('hex');\n return `sha256:${digest}`;\n}\n", "import { createReadStream } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { createInterface } from 'node:readline';\nimport type { SessionEvent } from '../types/session.js';\nimport { sessionScopedPath } from '../utils/session-scoped-path.js';\nimport { resolveSessionId, sessionIdResolutionError } from './session-id-resolver.js';\nimport { collectSessionIds } from './session-store/directory-session-files.js';\n/**\n * Idea #1 from IDEAS.md \u2014 Stateful Session Recovery.\n *\n * `SessionRecovery` is the read-side companion to the in-flight\n * marker mechanism. When the agent loop is running, it writes an\n * `in_flight_start` event at the current point in the log. On\n * clean shutdown, a matching `in_flight_end` follows. Provider and\n * tool events may legitimately appear after the start marker, so\n * recovery finds the latest lifecycle boundary rather than assuming\n * the marker is literally the last JSONL record.\n *\n * Detection is diagnostic only. Normal session resume reconstructs the\n * conversation by replaying persisted JSONL events; it must not blindly\n * re-execute tool calls after a crash. `recover()` exposes the tail after the\n * last checkpoint so callers can explain which persisted work was in flight.\n *\n * Concurrency: pure read; no writes. Safe to call from multiple\n * processes simultaneously.\n */\nexport interface StaleSession {\n sessionId: string;\n /** Path to the JSONL log. */\n path: string;\n /** Last event ts (the in_flight_start timestamp). */\n lastEventTs: string;\n /** Context the agent was working on when it died. */\n context: string;\n /** Total events in the log. */\n eventCount: number;\n}\n\nexport interface RecoveryPlan {\n sessionId: string;\n /** True if the session is stale (has a dangling in_flight_start). */\n stale: boolean;\n /** The last `checkpoint` event before the un-replayed work, or null. */\n lastCheckpoint: SessionEvent | null;\n /** All persisted events after the last checkpoint (diagnostic in-flight tail). */\n pendingEvents: SessionEvent[];\n /** The dangling in_flight_start event, if any. */\n inFlightStart: SessionEvent | null;\n /** Free-form context the agent was working on, if any. */\n context: string | null;\n}\n\n/**\n * Result of `SessionRecovery.recover(sessionId)`. Distinct from\n * `StaleSession`: a session is \"stale\" if its latest lifecycle\n * boundary is an open marker, but a \"recovery plan\" can also be generated for\n * clean sessions whose last checkpoint is older than the\n * conversation history (e.g. a user-initiated \"rewind to last\n * good state\" flow). This is a diagnostic plan, not authorization to replay\n * external side effects.\n */\nexport class SessionRecovery {\n private static readonly MAX_PENDING_EVENTS = 10_000;\n private static readonly MAX_PENDING_BYTES = 16 * 1024 * 1024;\n /**\n * Scan a session log and return a `StaleSession` if and only if the newest\n * lifecycle boundary is an `in_flight_start` without a later\n * `in_flight_end`/`session_end`. Ordinary provider/tool events after the\n * marker do not make the session clean. Returns `null` when:\n * - the log does not exist;\n * - the log is empty;\n * - the latest lifecycle boundary is `in_flight_end` or `session_end`;\n * - there is no lifecycle boundary (legacy/pre-marker log).\n *\n * The reverse scanner is chunked and line-aware. It can cross arbitrarily\n * large JSONL records (for example a large tool result) without imposing a\n * fixed tail-size correctness limit. Clean logs normally return after the\n * first chunk; stale logs continue counting lines so `eventCount` remains\n * the documented total rather than a tail-only approximation.\n */\n async resolveId(query: string): Promise<string> {\n const resolution = resolveSessionId(query, await collectSessionIds(this.dir));\n if (resolution.status === 'resolved') return resolution.id;\n if (resolution.status === 'missing') return resolution.query;\n throw sessionIdResolutionError(resolution);\n }\n\n async detectStale(sessionId: string): Promise<StaleSession | null> {\n const canonicalId = await this.resolveId(sessionId);\n return this.detectStaleExact(canonicalId);\n }\n\n private async detectStaleExact(sessionId: string): Promise<StaleSession | null> {\n const fp = this.filePath(sessionId);\n let stat;\n try {\n stat = await fs.stat(fp);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n /* v8 ignore next -- defensive: any other stat failure is also non-recoverable */\n return null;\n }\n if (stat.size === 0) return null;\n\n try {\n const scan = await scanLatestLifecycleBoundary(fp, stat.size);\n if (scan?.boundary.type !== 'in_flight_start') return null;\n return {\n sessionId,\n path: fp,\n lastEventTs: scan.boundary.ts,\n context: scan.boundary.context,\n eventCount: scan.eventCount,\n };\n /* v8 ignore start -- defensive: reverse scan failure after a successful stat is rare */\n } catch {\n return null;\n }\n /* v8 ignore stop */\n }\n\n /**\n * Generate a recovery plan for a session. The plan describes\n * the persisted tail after the last checkpoint, plus the dangling in-flight\n * marker if present. SessionStore.resume() independently reconstructs state\n * from the journal and does not re-run these events as commands.\n *\n * Returns a non-null plan for ANY session that has at least\n * one event after a checkpoint (or, for legacy sessions, at\n * least one event). Pure read; no mutation.\n */\n async recover(sessionId: string): Promise<RecoveryPlan | null> {\n const canonicalId = await this.resolveId(sessionId);\n const fp = this.filePath(canonicalId);\n const pendingEvents: SessionEvent[] = [];\n const pendingSizes: number[] = [];\n let pendingBytes = 0;\n let lastCheckpoint: SessionEvent | null = null;\n let latestBoundary: LifecycleBoundary | null = null;\n let sawEvent = false;\n const stream = createReadStream(fp, { encoding: 'utf8' });\n const lines = createInterface({ input: stream, crlfDelay: Infinity });\n try {\n for await (const line of lines) {\n if (!line.trim()) continue;\n let event: SessionEvent;\n try {\n event = JSON.parse(line) as SessionEvent;\n } catch {\n continue;\n }\n if (!event || typeof event !== 'object' || typeof event.type !== 'string') continue;\n sawEvent = true;\n if (event.type === 'checkpoint') {\n lastCheckpoint = event;\n pendingEvents.length = 0;\n pendingSizes.length = 0;\n pendingBytes = 0;\n } else {\n const bytes = Buffer.byteLength(line, 'utf8');\n if (bytes <= SessionRecovery.MAX_PENDING_BYTES) {\n while (\n pendingEvents.length >= SessionRecovery.MAX_PENDING_EVENTS ||\n pendingBytes + bytes > SessionRecovery.MAX_PENDING_BYTES\n ) {\n pendingEvents.shift();\n pendingBytes = Math.max(0, pendingBytes - (pendingSizes.shift() ?? 0));\n }\n pendingEvents.push(event);\n pendingSizes.push(bytes);\n pendingBytes += bytes;\n }\n }\n if (isLifecycleBoundary(event)) latestBoundary = event;\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n /* v8 ignore next -- defensive: any other read failure is also non-recoverable */\n return null;\n } finally {\n lines.close();\n stream.close();\n }\n if (!sawEvent) return null;\n // The dangling in_flight_start, if it is the newest lifecycle boundary.\n // Provider/tool events may follow the marker and are still pending work.\n const inFlightStart = latestBoundary?.type === 'in_flight_start' ? latestBoundary : null;\n const context =\n inFlightStart && inFlightStart.type === 'in_flight_start' ? inFlightStart.context : null;\n return {\n sessionId: canonicalId,\n stale: inFlightStart !== null,\n lastCheckpoint,\n pendingEvents,\n inFlightStart,\n context,\n };\n }\n\n /**\n * List every stale session in a directory. Returns an array\n * (possibly empty) sorted by `lastEventTs` descending \u2014 most\n * recent crash first.\n */\n async listResumable(): Promise<StaleSession[]> {\n const out: StaleSession[] = [];\n // Modern sessions live inside date-shard subdirectories\n // (\"2026-06-11/<base>.jsonl\"); legacy/flat sessions sit at the root.\n // Scan both \u2014 a root-only scan silently misses every modern crash.\n const collect = async (dir: string, prefix: string, depth: number): Promise<void> => {\n let entries: import('node:fs').Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n /* v8 ignore start -- defensive: the sessions dir (and its shards) are readable during a scan */\n } catch {\n return;\n }\n /* v8 ignore stop */\n for (const entry of entries) {\n if (entry.name.startsWith('.')) continue;\n if (entry.name === 'shared' || entry.name === 'subagents' || entry.name === 'attachments')\n continue;\n if (entry.isDirectory()) {\n if (depth === 0) {\n await collect(path.join(dir, entry.name), entry.name, depth + 1);\n }\n continue;\n }\n if (!entry.isFile() || !entry.name.endsWith('.jsonl')) continue;\n if (entry.name === '_index.jsonl' || entry.name === '_mailbox.jsonl') continue;\n const base = entry.name.slice(0, -'.jsonl'.length);\n if (base.includes('.replay') || base.includes('.annotations') || base.includes('.audit'))\n continue;\n const sessionId = prefix ? `${prefix}/${base}` : base;\n const stale = await this.detectStaleExact(sessionId);\n if (stale) out.push(stale);\n }\n };\n await collect(this.dir, '', 0);\n return out.sort((a, b) => b.lastEventTs.localeCompare(a.lastEventTs));\n }\n\n // \u2500\u2500 Internals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private filePath(sessionId: string): string {\n // Containment-checked: date-sharded ids (\"2026-06-11/<base>\") are\n // legitimate; traversal is rejected. Shared with the other per-session\n // sidecar stores so the contract can't drift.\n return sessionScopedPath(this.dir, sessionId, '.jsonl');\n }\n\n constructor(private readonly dir: string) {}\n}\n\ntype LifecycleBoundary = Extract<\n SessionEvent,\n { type: 'in_flight_start' | 'in_flight_end' | 'session_end' }\n>;\n\nfunction isLifecycleBoundary(event: SessionEvent): event is LifecycleBoundary {\n return (\n event.type === 'in_flight_start' ||\n event.type === 'in_flight_end' ||\n event.type === 'session_end'\n );\n}\n\nfunction parseLifecycleBoundary(line: Buffer): LifecycleBoundary | null {\n try {\n const parsed = JSON.parse(line.toString('utf8')) as SessionEvent;\n return isLifecycleBoundary(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nfunction hasNonWhitespace(line: Buffer): boolean {\n for (const byte of line) {\n if (byte !== 0x09 && byte !== 0x0a && byte !== 0x0d && byte !== 0x20) return true;\n }\n return false;\n}\n\n/**\n * Walk JSONL records newest-first without decoding partial UTF-8 chunks. A\n * record split across chunks is retained as bytes and decoded only after its\n * opening newline is found, so large/non-ASCII tool results cannot hide the\n * lifecycle marker immediately before them.\n */\nasync function scanLatestLifecycleBoundary(\n filePath: string,\n size: number,\n): Promise<{ boundary: LifecycleBoundary; eventCount: number } | null> {\n const CHUNK_SIZE = 64 * 1024;\n const handle = await fs.open(filePath, 'r');\n let position = size;\n let laterLineFragment = Buffer.alloc(0);\n let latestBoundary: LifecycleBoundary | null = null;\n let eventCount = 0;\n\n const observeLine = (line: Buffer): LifecycleBoundary | null => {\n if (!hasNonWhitespace(line)) return null;\n eventCount++;\n return parseLifecycleBoundary(line);\n };\n\n try {\n while (position > 0) {\n const length = Math.min(CHUNK_SIZE, position);\n position -= length;\n const chunk = Buffer.allocUnsafe(length);\n const { bytesRead } = await handle.read(chunk, 0, length, position);\n const data = Buffer.concat([chunk.subarray(0, bytesRead), laterLineFragment]);\n\n let lineEnd = data.length;\n for (let i = data.length - 1; i >= 0; i--) {\n if (data[i] !== 0x0a) continue;\n const boundary = observeLine(data.subarray(i + 1, lineEnd));\n if (!latestBoundary && boundary) latestBoundary = boundary;\n lineEnd = i;\n }\n laterLineFragment = data.subarray(0, lineEnd);\n\n // A clean boundary needs no exact event count because no StaleSession is\n // returned. The common clean path therefore remains a one-chunk read.\n if (latestBoundary && latestBoundary.type !== 'in_flight_start') {\n return { boundary: latestBoundary, eventCount };\n }\n }\n\n const boundary = observeLine(laterLineFragment);\n if (!latestBoundary && boundary) latestBoundary = boundary;\n return latestBoundary ? { boundary: latestBoundary, eventCount } : null;\n } finally {\n await handle.close();\n }\n}\n", "import { createHash, randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport type { EventBus } from '../kernel/events.js';\nimport { withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { expectDefined } from '../utils/expect-defined.js';\nimport { safeParse } from '../utils/safe-json.js';\nimport { sessionScopedPath } from '../utils/session-scoped-path.js';\n/**\n * ToolAuditLog \u2014 idea #9 from IDEAS.md.\n *\n * Tamper-evident audit trail for tool calls. Every tool_use /\n * tool_result pair is appended to a sidecar JSONL with a chained\n * SHA-256 \u2014 each entry's `prevHash` is the prior entry's `hash`,\n * so any post-hoc modification of a single line breaks the chain\n * from that point forward.\n *\n * Why a sidecar (not the session JSONL)?\n * Same reason as `AnnotationsStore` and `ReplayLogStore`: the\n * session log is an event-sourced journal. Mixing in a hash\n * chain would inflate every read and tightly couple the\n * integrity check to the event format. Sidecar keeps both\n * concerns orthogonal.\n *\n * What \"tamper-evident\" means here:\n * - The hash covers the full serialized entry: tool name, id,\n * input, output, timestamp, author. Changing any byte\n * changes the hash.\n * - The chain is sequential \u2014 a verifier walks the file in\n * order, recomputing each hash, and checks `prevHash`\n * matches the previous entry's `hash`.\n * - Any insertion, deletion, or modification of a single\n * entry surfaces as a \"chain broken at entry N\" verdict.\n *\n * What it does NOT defend against:\n * - An attacker who rewrites the whole file consistently.\n * For that you'd need an external anchor (signing key,\n * transparency log, etc.) \u2014 out of scope for Phase 1.\n * - The agent itself misbehaving; this is post-hoc audit, not\n * real-time enforcement. Use `PermissionPolicy` for that.\n *\n * File layout: `sessionScopedPath(dir, sessionId, '.audit.jsonl')`,\n * one entry per line. The chain starts with a `genesis` entry whose\n * `prevHash` is all zeros.\n */\nexport interface AuditEntry {\n /** Monotonic index (0-based). */\n index: number;\n /** UUID for cross-referencing with logs. */\n id: string;\n /** ISO timestamp. */\n ts: string;\n /** Hash of the previous entry (or all-zeros for the genesis entry). */\n prevHash: string;\n /** Hash of this entry's content (sha256 over the canonical JSON). */\n hash: string;\n toolName: string;\n toolUseId: string;\n input: unknown;\n output: unknown;\n isError: boolean;\n}\n\nconst GENESIS_PREV = '0'.repeat(64);\n\nexport type VerifyResult =\n | { ok: true; entries: number }\n | { ok: false; brokenAt: number; reason: string };\n\nexport interface ToolAuditLogOptions {\n /** Root sessions directory used with `sessionScopedPath(..., '.audit.jsonl')`. */\n dir: string;\n /**\n * Flush the file system cache to disk every N writes per session.\n * Default 100. Lower values = better crash durability, more I/O overhead.\n * Set to `Infinity` to disable periodic fsync (fastest, but highest data-loss risk).\n */\n fsyncEvery?: number | undefined;\n events?: EventBus;\n traceId?: string;\n}\n\n/** Default number of writes between fsync calls. */\nconst DEFAULT_FSYNC_EVERY = 100;\n\nexport class ToolAuditLog {\n private readonly dir: string;\n private readonly events: EventBus | undefined;\n private readonly traceId: string | undefined;\n /** In-memory cache of the last entry's hash (per session), to compute chains efficiently. */\n private readonly tailHash = new Map<string, string>();\n /** In-memory counter for entry indices \u2014 avoids re-reading the file on every write. */\n private readonly tailIndex = new Map<string, number>();\n /**\n * File mtime+size recorded after our last write, per session. Used to\n * detect cross-process writes (session handoff, recovery) that would\n * invalidate the in-memory tail cache: if the stat no longer matches\n * we re-read the file to re-establish the chain tip before appending.\n */\n private readonly tailStat = new Map<string, { mtimeMs: number; size: number }>();\n /** Tracks writes since last fsync, per session. */\n private readonly unSyncedWrites = new Map<string, number>();\n private readonly writeChains = new Map<string, Promise<void>>();\n private readonly fsyncEvery: number;\n\n constructor(opts: ToolAuditLogOptions) {\n this.dir = opts.dir;\n this.events = opts.events;\n this.traceId = opts.traceId;\n this.fsyncEvery = opts.fsyncEvery ?? DEFAULT_FSYNC_EVERY;\n }\n\n /**\n * Append a tool call/result pair to the chain. Returns the\n * resulting entry. Idempotency is not guaranteed \u2014 if you\n * record the same tool_use twice you get two entries. That's\n * intentional: the audit log is a record, not a cache.\n */\n async record(input: {\n sessionId: string;\n toolName: string;\n toolUseId: string;\n input: unknown;\n output: unknown;\n isError: boolean;\n }): Promise<AuditEntry> {\n let entry!: AuditEntry; // assigned inside the enqueue callback\n const fp = this.filePath(input.sessionId);\n const t0 = Date.now();\n try {\n await this.enqueue(input.sessionId, async () => {\n await withFileLock(fp, async () => {\n // Resolve the chain tip from the in-memory cache when possible,\n // re-reading the file only on first contact or when an external\n // writer (cross-process session handoff) changed it under us.\n // The previous implementation did `readAll` on every single\n // record just to find `entries.at(-1)` \u2014 a full parse of the\n // audit file per tool call.\n const tip = await this._resolveChainTip(input.sessionId, fp);\n const prevHash = tip.prevHash;\n const index = tip.nextIndex;\n\n const id = randomUUID();\n const ts = new Date().toISOString();\n const content = {\n id,\n ts,\n prevHash,\n toolName: input.toolName,\n toolUseId: input.toolUseId,\n input: input.input,\n output: input.output,\n isError: input.isError,\n index,\n };\n const hash = createHash('sha256').update(stableStringify(content), 'utf8').digest('hex');\n entry = {\n id,\n ts,\n prevHash,\n hash,\n toolName: input.toolName,\n toolUseId: input.toolUseId,\n input: input.input,\n output: input.output,\n isError: input.isError,\n index,\n };\n\n // True O(1) append \u2014 one line, no full-file rewrite. The previous\n // `writeAll(entries)` path re-serialized every entry on every\n // record; a 1000-call session rewrote a multi-MB audit file 1000\n // times (quadratic in session length). `withFileLock` above\n // guarantees no concurrent writer interleaves with our append.\n await fs.appendFile(fp, JSON.stringify(entry) + '\\n', 'utf8');\n\n // Refresh caches + fsync bookkeeping. We stat post-write so the\n // mtime+size tracker reflects the just-appended line \u2014 the next\n // record can trust the cache without re-reading.\n try {\n const st = await fs.stat(fp);\n this.tailStat.set(input.sessionId, { mtimeMs: st.mtimeMs, size: st.size });\n } catch {\n /* best-effort; next record will just re-read */\n }\n this.tailHash.set(input.sessionId, hash);\n this.tailIndex.set(input.sessionId, index + 1);\n await this._trackUnsynced(input.sessionId, fp);\n\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.write', {\n sessionId: input.sessionId,\n store: 'audit',\n filePath: fp,\n operation: 'record',\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n });\n });\n return entry;\n } catch (err) {\n this.events?.emit('storage.error', {\n sessionId: input.sessionId,\n store: 'audit',\n filePath: fp,\n operation: 'record',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: false,\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n /**\n * Resolve the chain tip (previous hash + next index) for an append.\n * Uses the in-memory `tailHash`/`tailIndex` cache when the file's\n * stat matches our last known write; falls back to a full read on\n * cache miss or when an external writer has extended the file.\n */\n private async _resolveChainTip(\n sessionId: string,\n fp: string,\n ): Promise<{ prevHash: string; nextIndex: number }> {\n const cachedHash = this.tailHash.get(sessionId);\n const cachedIndex = this.tailIndex.get(sessionId);\n const cachedStat = this.tailStat.get(sessionId);\n\n if (cachedHash !== undefined && cachedIndex !== undefined && cachedStat) {\n // Verify no other process appended since our last write. Stat is a\n // single inode lookup; the common case (same-process sequential\n // writes) returns immediately and we skip the full read.\n try {\n const st = await fs.stat(fp);\n if (st.mtimeMs === cachedStat.mtimeMs && st.size === cachedStat.size) {\n return { prevHash: cachedHash, nextIndex: cachedIndex };\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n // File was removed out from under us \u2014 reset and start a new chain.\n this.tailHash.delete(sessionId);\n this.tailIndex.delete(sessionId);\n this.tailStat.delete(sessionId);\n return { prevHash: GENESIS_PREV, nextIndex: 0 };\n }\n /* v8 ignore next -- defensive: a non-ENOENT stat failure during cache-hit is rare */\n throw err;\n }\n }\n\n // Cache miss or external write detected \u2014 read the true tail.\n const entries = await this.readAll(sessionId);\n const prev = entries.at(-1);\n const prevHash = prev?.hash ?? GENESIS_PREV;\n const nextIndex = prev ? prev.index + 1 : 0;\n this.tailHash.set(sessionId, prevHash);\n this.tailIndex.set(sessionId, nextIndex);\n try {\n const st = await fs.stat(fp);\n this.tailStat.set(sessionId, { mtimeMs: st.mtimeMs, size: st.size });\n } catch {\n /* leave cache empty; next record re-reads */\n }\n return { prevHash, nextIndex };\n }\n\n /**\n * Walk the chain and verify every entry's hash and prevHash.\n * Returns a structured verdict \u2014 never throws.\n */\n async verify(sessionId: string): Promise<VerifyResult> {\n const fp = this.filePath(sessionId);\n const t0 = Date.now();\n let entries: AuditEntry[];\n try {\n entries = await this.readAll(sessionId);\n } catch (err) {\n // The file exists but can't be read (permissions, corruption). We\n // can't verify it, so emit a read failure for observability and\n // degrade gracefully \u2014 this method's contract is \"never throws\".\n this.events?.emit('storage.read', {\n sessionId,\n store: 'audit',\n filePath: fp,\n operation: 'verify',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: toErrorMessage(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return { ok: true, entries: 0 };\n }\n\n // Walk the chain into a verdict, then emit a single storage.read whose\n // outcome reflects the verification result (a broken chain is a failure).\n const verdict = ((): VerifyResult => {\n if (entries.length === 0) return { ok: true, entries: 0 };\n // The first entry's prevHash must be the all-zeros genesis marker.\n if (entries[0]?.prevHash !== GENESIS_PREV) {\n return {\n ok: false,\n brokenAt: 0,\n reason: 'first entry is not the genesis (prevHash != 0\u20260)',\n };\n }\n let prevHash = GENESIS_PREV;\n for (let i = 0; i < entries.length; i++) {\n const e = expectDefined(entries[i]);\n if (e.prevHash !== prevHash) {\n return {\n ok: false,\n brokenAt: i,\n reason: `prevHash mismatch at entry ${i} (expected ${prevHash.slice(0, 8)}\u2026, got ${e.prevHash.slice(0, 8)}\u2026)`,\n };\n }\n // Recompute the hash from the entry's content (without the\n // `hash` field itself, which is what we are verifying).\n const content = {\n id: e.id,\n ts: e.ts,\n prevHash: e.prevHash,\n toolName: e.toolName,\n toolUseId: e.toolUseId,\n input: e.input,\n output: e.output,\n isError: e.isError,\n index: e.index,\n };\n const expectedHash = createHash('sha256')\n .update(stableStringify(content), 'utf8')\n .digest('hex');\n if (expectedHash !== e.hash) {\n return {\n ok: false,\n brokenAt: i,\n reason: `hash mismatch at entry ${i} (entry content was modified)`,\n };\n }\n prevHash = e.hash;\n }\n return { ok: true, entries: entries.length };\n })();\n\n this.events?.emit('storage.read', {\n sessionId,\n store: 'audit',\n filePath: fp,\n operation: 'verify',\n outcome: verdict.ok ? 'success' : 'failure',\n durationMs: Date.now() - t0,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return verdict;\n }\n\n /** All entries for a session, in insertion order. */\n async load(sessionId: string): Promise<AuditEntry[]> {\n const fp = this.filePath(sessionId);\n const t0 = Date.now();\n try {\n const entries = await this.readAll(sessionId);\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.read', {\n sessionId,\n store: 'audit',\n filePath: fp,\n operation: 'load',\n outcome: 'success',\n durationMs,\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n return entries;\n } catch (err) {\n const durationMs = Date.now() - t0;\n this.events?.emit('storage.read', {\n sessionId,\n store: 'audit',\n filePath: fp,\n operation: 'load',\n outcome: 'failure',\n durationMs,\n error: toErrorMessage(err),\n ...(this.traceId !== undefined ? { traceId: this.traceId } : {}),\n });\n throw err;\n }\n }\n\n // \u2500\u2500 Internals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private filePath(sessionId: string): string {\n // Containment-checked: date-sharded ids (\"2026-06-11/<base>\") are\n // legitimate; traversal is rejected. A plain slash ban would throw\n // for every modern session id the moment record() gets wired in.\n return sessionScopedPath(this.dir, sessionId, '.audit.jsonl');\n }\n\n private async readAll(sessionId: string): Promise<AuditEntry[]> {\n const fp = this.filePath(sessionId);\n try {\n const raw = await fs.readFile(fp, 'utf8');\n const out: AuditEntry[] = [];\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const parsed = safeParse<AuditEntry>(line);\n if (parsed.ok && parsed.value) out.push(parsed.value);\n } catch {\n // Skip corrupt lines \u2014 audit data is meta, not fatal.\n }\n }\n return out;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return [];\n // Non-ENOENT errors (EACCES, ENOSPC, etc.) are real I/O failures \u2014\n // re-throw so callers (verify, load) can emit storage.error.\n throw err;\n }\n }\n\n /**\n * Tracks writes since last fsync and triggers periodic fsync.\n * Called after each O(1) append to maintain the same durability\n * guarantees as the old writeAll approach.\n */\n private async _trackUnsynced(sessionId: string, fp: string): Promise<void> {\n const count = (this.unSyncedWrites.get(sessionId) ?? 0) + 1;\n this.unSyncedWrites.set(sessionId, count);\n if (this.fsyncEvery !== Number.POSITIVE_INFINITY && count % this.fsyncEvery === 0) {\n await this.sync(sessionId, fp);\n }\n }\n\n /**\n * Explicitly sync the file to disk. Called automatically every\n * `fsyncEvery` writes, and available for callers who want to\n * force a sync before closing or during graceful shutdown.\n */\n async flush(sessionId: string): Promise<void> {\n await this.sync(sessionId, this.filePath(sessionId));\n }\n\n private async sync(sessionId: string, fp: string): Promise<void> {\n try {\n const fh = await fs.open(fp, 'r+');\n try {\n await fh.sync();\n } finally {\n await fh.close();\n }\n } catch {\n // fsync is best-effort; a failure here does not corrupt the chain.\n } finally {\n this.unSyncedWrites.set(sessionId, 0);\n }\n }\n\n private enqueue(sessionId: string, fn: () => Promise<void>): Promise<void> {\n const prev = this.writeChains.get(sessionId) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n const settled = next.catch(() => undefined);\n this.writeChains.set(sessionId, settled);\n void settled.finally(() => {\n if (this.writeChains.get(sessionId) === settled) this.writeChains.delete(sessionId);\n });\n return next;\n }\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortKeys(value));\n}\n\nfunction sortKeys(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeys);\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = sortKeys(obj[key]);\n }\n return sorted;\n }\n return value;\n}\n", "import type { SessionEvent } from '../types/session.js';\n\nexport interface QueryFilter {\n eventTypes?: string[] | undefined;\n toolNames?: string[] | undefined;\n timeRange?: { start: string; end: string } | undefined;\n}\n\nexport interface ToolInvocation {\n ts: string;\n name: string;\n input: unknown;\n output?: unknown | undefined;\n error?: string | undefined;\n durationMs: number;\n}\n\nexport interface SessionError {\n ts: string;\n phase: string;\n message: string;\n}\n\nexport interface ModeChange {\n ts: string;\n from: string;\n to: string;\n}\n\nexport interface TaskSummary {\n taskId: string;\n title: string;\n status: string;\n createdAt: string;\n completedAt?: string | undefined;\n}\n\nexport interface SessionAnalysis {\n sessionId: string;\n totalDuration: number;\n toolUsageCount: Record<string, number>;\n errorCount: number;\n modeChanges: ModeChange[];\n tasks: TaskSummary[];\n}\n\nexport class SessionAnalyzer {\n analyze(events: SessionEvent[]): SessionAnalysis {\n const toolUsageCount: Record<string, number> = {};\n const errors: SessionError[] = [];\n const modeChanges: ModeChange[] = [];\n const tasksById = new Map<string, TaskSummary>();\n let sessionId = '';\n\n for (const event of events) {\n // sessionId comes from session_start / session_resumed.\n if (event.type === 'session_start' || event.type === 'session_resumed') {\n if (!sessionId) sessionId = event.id;\n }\n if (event.type === 'tool_use') {\n toolUsageCount[event.name] = (toolUsageCount[event.name] ?? 0) + 1;\n }\n if (event.type === 'error') {\n errors.push({ ts: event.ts, phase: event.phase, message: event.message });\n }\n if (event.type === 'mode_changed') {\n modeChanges.push({ ts: event.ts, from: event.from, to: event.to });\n }\n if (event.type === 'task_created') {\n tasksById.set(event.taskId, {\n taskId: event.taskId,\n title: event.title,\n status: 'created',\n createdAt: event.ts,\n });\n }\n if (event.type === 'task_updated') {\n const t = tasksById.get(event.taskId);\n if (t) t.status = event.status;\n }\n if (event.type === 'task_completed') {\n const t = tasksById.get(event.taskId);\n if (t) {\n t.status = 'completed';\n t.completedAt = event.ts;\n } else {\n tasksById.set(event.taskId, {\n taskId: event.taskId,\n title: event.title,\n status: 'completed',\n createdAt: event.ts,\n completedAt: event.ts,\n });\n }\n }\n if (event.type === 'task_failed') {\n const t = tasksById.get(event.taskId);\n if (t) {\n t.status = 'failed';\n t.completedAt = event.ts;\n } else {\n tasksById.set(event.taskId, {\n taskId: event.taskId,\n title: event.title,\n status: 'failed',\n createdAt: event.ts,\n completedAt: event.ts,\n });\n }\n }\n }\n\n return {\n sessionId,\n totalDuration: this.calcDuration(events),\n toolUsageCount,\n errorCount: errors.length,\n modeChanges,\n tasks: Array.from(tasksById.values()),\n };\n }\n\n query(events: SessionEvent[], filter: QueryFilter): SessionEvent[] {\n return events.filter((e) => {\n if (filter.eventTypes?.length && !filter.eventTypes.includes(e.type)) return false;\n if (filter.toolNames?.length && e.type === 'tool_use') {\n const toolEvent = e as { type: 'tool_use'; name: string };\n if (!filter.toolNames.includes(toolEvent.name)) return false;\n }\n if (filter.timeRange) {\n const ts = new Date(e.ts).getTime();\n const start = new Date(filter.timeRange.start).getTime();\n const end = new Date(filter.timeRange.end).getTime();\n if (ts < start || ts > end) return false;\n }\n return true;\n });\n }\n\n private calcDuration(events: SessionEvent[]): number {\n if (events.length < 2) return 0;\n const firstEvent = events[0];\n const lastEvent = events[events.length - 1];\n /* v8 ignore next -- defensive: length>=2 guard above guarantees both ends exist */\n if (!firstEvent || !lastEvent) return 0;\n const first = new Date(firstEvent.ts).getTime();\n const last = new Date(lastEvent.ts).getTime();\n return last - first;\n }\n}\n", "/**\n * SessionRegistry \u2014 cross-process session and agent tracker.\n *\n * Each WrongStack process registers its session on start and updates its\n * status periodically. The registry is a single JSON file at\n * `~/.wrongstack/session-registry.json`. Entries are keyed by session ID.\n *\n * Because multiple processes may write concurrently, every write is an\n * atomic read-modify-write protected by a per-file advisory lock (flock on\n * Unix, exclusive open on Windows). Stale entries (process no longer alive)\n * are pruned on every read.\n *\n * @module session-registry\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport {\n breakStaleLock,\n pruneStaleTempFiles,\n STALE_TMP_MS,\n writeAtomicFile,\n} from './session-registry-atomic-file.js';\nimport type {\n AgentEntry,\n SessionLiveStatus,\n SessionRegistryEntry,\n} from './session-registry-types.js';\nimport { isPidAlive } from './utils/pid.js';\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Re-exported so `session-registry.js` stays the single import site for these\n// record shapes across the workspace; the declarations live in their own module.\nexport type {\n AgentActivityTotals,\n AgentEntry,\n AgentLiveStatus,\n AgentRecentMail,\n AgentRecentTool,\n AgentTodoItem,\n SessionLiveStatus,\n SessionRegistryEntry,\n} from './session-registry-types.js';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst REGISTRY_FILE = 'session-registry.json';\nconst HEARTBEAT_INTERVAL_MS = 5_000;\nconst STALE_TIMEOUT_MS = 30_000; // entry considered stale after 30s without heartbeat\n// A live session heartbeats every HEARTBEAT_INTERVAL_MS, so two missed beats\n// already mean something is wrong \u2014 cheap enough to confirm with a PID probe\n// from then on, instead of waiting out the full stale window.\nconst PID_CHECK_AFTER_MS = HEARTBEAT_INTERVAL_MS * 2;\n// A session that announced `closing` (heartbeat stopped) is dropped this long\n// after its last heartbeat, so the fleet view doesn't keep a dead client around.\nconst CLOSING_GRACE_MS = 15_000;\n// A live PID with a missed heartbeat is marked lost but retained. Removing it\n// would let another process open the same journal while the original process\n// can still write. Operators must terminate the live owner before takeover.\n/** Subagents without any activity this long are not live, even if the owning session still heartbeats. */\nconst AGENT_STALE_MS = 5 * 60_000;\n// Ownership claims are rare and must survive ordinary Windows fsync/antivirus\n// latency plus a burst of simultaneous heartbeats from several clients.\n// Telemetry writes remain cheap/best-effort and keep the shorter budget.\nconst OWNERSHIP_LOCK_WAIT_MS = 8_000;\nconst OPTIONAL_LOCK_WAIT_MS = 750;\nconst LOCK_RETRY_MAX_MS = 100;\n// Agent snapshots arrive on every tool boundary \u2014 a busy agent can cross\n// several boundaries per second, and each write is a full temp+fsync+rename\n// under the advisory lock. Coalesce to at most one write per window; the\n// first call in a quiet window still writes immediately so status stays live.\nconst AGENTS_WRITE_THROTTLE_MS = 300;\n\n/** Conflict errors are the one registry-write failure callers must observe. */\nclass SessionOwnershipConflictError extends Error {}\nclass SessionRegistryWriteError extends Error {}\n// Directory enumeration and per-file stats are substantially more expensive\n// than the tiny registry write itself on networked/antivirus-scanned homes.\n// Temp files cannot become stale faster than STALE_TMP_MS, so scanning once in\n// that window is sufficient even though heartbeats write every five seconds.\nconst TEMP_PRUNE_INTERVAL_MS = STALE_TMP_MS;\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Liveness probe for registry entries. Delegates to the shared helper so this\n * module can't drift back to the bare-catch variant, which reported a live\n * but non-signalable process (EPERM) as dead and pruned its session.\n */\nconst pidAlive = isPidAlive;\n\nfunction sameOwner(\n entry: Pick<SessionRegistryEntry, 'pid' | 'startedAt'>,\n owner: Pick<SessionRegistryEntry, 'pid' | 'startedAt'>,\n): boolean {\n return entry.pid === owner.pid && entry.startedAt === owner.startedAt;\n}\n\n/**\n * Parse registry file contents, tolerating corruption. A system crash can\n * leave the file zero-filled (NTFS journals the rename metadata but the data\n * blocks were never flushed \u2014 the file is its full size of NUL bytes), or a\n * torn write can leave invalid JSON. Treat anything unparsable \u2014 or parsable\n * but not a plain object \u2014 as an empty registry so the next write heals the\n * file instead of wedging every future write forever.\n */\nfunction parseRegistry(raw: string): Record<string, SessionRegistryEntry> {\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, SessionRegistryEntry>;\n }\n } catch {\n /* corrupt file \u2014 fall through to empty */\n }\n return {};\n}\n\n/** Derive the session-level status from the agent collective. */\nfunction deriveSessionStatus(agents: AgentEntry[]): SessionLiveStatus {\n const hasRunning = agents.some((a) => a.status === 'running' || a.status === 'streaming');\n const hasWaiting = agents.some((a) => a.status === 'waiting_user');\n const hasError = agents.some((a) => a.status === 'error');\n return hasRunning || hasWaiting || hasError ? 'active' : 'idle';\n}\n\n// \u2500\u2500 Registry class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class SessionRegistry {\n private readonly filePath: string;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private currentSessionId: string | null = null;\n private lastTempPruneAt = 0;\n private tempPrunePromise: Promise<void> | null = null;\n /** Latest agent snapshot not yet written; superseded by newer calls. */\n private pendingAgents: AgentEntry[] | null = null;\n /** Shared settle promise for all updateAgents calls coalesced into one trailing write. */\n private pendingAgentsFlush: Promise<void> | null = null;\n private pendingAgentsResolve: (() => void) | null = null;\n private agentsFlushTimer: ReturnType<typeof setTimeout> | null = null;\n private lastAgentsWriteAt = 0;\n /**\n * Last full entry this process registered. Kept so the heartbeat can\n * re-create our entry if it ever goes missing because the file was reset or\n * a reader pruned a stale snapshot.\n */\n private lastEntry: SessionRegistryEntry | null = null;\n private readonly ownershipLockWaitMs: number;\n\n constructor(globalRoot: string, options: { ownershipLockWaitMs?: number } = {}) {\n this.filePath = path.join(globalRoot, REGISTRY_FILE);\n this.ownershipLockWaitMs = Math.max(0, options.ownershipLockWaitMs ?? OWNERSHIP_LOCK_WAIT_MS);\n }\n\n // \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Register the current session. Call once on session start.\n * Starts the heartbeat timer.\n */\n async register(\n entry: Omit<SessionRegistryEntry, 'status' | 'lastHeartbeatAt' | 'agentCount' | 'agents'> & {\n agents?: AgentEntry[] | undefined;\n },\n ): Promise<void> {\n // Safe to call again on a project switch: the WebUI re-roots in place and\n // creates a fresh session id pointing at the new project. Clear the prior\n // heartbeat timer (otherwise each switch leaks a timer that keeps writing).\n // A process owns exactly one entry, so the same-pid dedup below drops our\n // own previous entry \u2014 the registry never carries a phantom session still\n // pointing at the old project's root/workingDir.\n const full: SessionRegistryEntry = {\n ...entry,\n status: 'active',\n lastHeartbeatAt: new Date().toISOString(),\n agentCount: entry.agents?.length ?? 0,\n agents: entry.agents ?? [],\n };\n // Fast pre-check for the common conflict path. The same check is repeated\n // under atomicUpdate's cross-process lock below to close the resume race.\n const existingRegistry = await this.readAndPrune();\n const currentOwner = existingRegistry[entry.sessionId];\n if (currentOwner && currentOwner.pid !== entry.pid && pidAlive(currentOwner.pid)) {\n throw new SessionOwnershipConflictError(\n `Session ${entry.sessionId} is already open in another running wstack (pid ${currentOwner.pid}).`,\n );\n }\n\n await this.atomicUpdate((registry) => {\n // Prune dead entries that haven't heartbeated recently.\n // A just-created entry has no heartbeat yet \u2014 don't prune it.\n const now = Date.now();\n for (const [id, existing] of Object.entries(registry)) {\n if (existing.pid === entry.pid) {\n // Our own process owns exactly one entry. When re-registering under\n // a new session id (project switch re-roots in place), drop the\n // stale same-pid entry so it doesn't linger pointing at the old\n // project's root/workingDir.\n if (id !== entry.sessionId) delete registry[id];\n continue;\n }\n const heartbeatAge = now - new Date(existing.lastHeartbeatAt).getTime();\n if (heartbeatAge > PID_CHECK_AFTER_MS && !pidAlive(existing.pid)) {\n delete registry[id];\n }\n }\n const lockedOwner = registry[entry.sessionId];\n if (lockedOwner && lockedOwner.pid !== entry.pid && pidAlive(lockedOwner.pid)) {\n throw new SessionOwnershipConflictError(\n `Session ${entry.sessionId} is already open in another running wstack (pid ${lockedOwner.pid}).`,\n );\n }\n registry[entry.sessionId] = full;\n }, true);\n // Only publish local ownership after the cross-process claim succeeds.\n // A failed duplicate-session claim must not make heartbeats overwrite the\n // real owner's registry entry.\n this.cancelPendingAgentsFlush();\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n this.lastAgentsWriteAt = 0;\n this.currentSessionId = entry.sessionId;\n this.lastEntry = full;\n\n // Start heartbeat\n /* v8 ignore start -- 5s heartbeat timer fires only in a live process, not under test */\n this.heartbeatTimer = setInterval(() => {\n void this.heartbeat();\n }, HEARTBEAT_INTERVAL_MS);\n /* v8 ignore stop */\n if (this.heartbeatTimer.unref) this.heartbeatTimer.unref();\n }\n\n /**\n * Update agent status for the current session. Call on every\n * significant status change (agent start, tool start, user wait, error).\n *\n * Writes are coalesced: the first call in a quiet window writes\n * immediately; calls arriving within {@link AGENTS_WRITE_THROTTLE_MS} of\n * the last write collapse into one trailing write carrying the newest\n * snapshot. The in-memory cache ({@link lastEntry}) is always updated\n * synchronously, so heartbeat re-inserts never carry stale agents.\n */\n async updateAgents(agents: AgentEntry[]): Promise<void> {\n if (!this.currentSessionId) return;\n this.pendingAgents = agents;\n\n // Keep the cached entry current so a heartbeat re-insert carries live agents.\n if (this.lastEntry) {\n this.lastEntry.agents = agents;\n this.lastEntry.agentCount = agents.length;\n this.lastEntry.status = deriveSessionStatus(agents);\n this.lastEntry.lastHeartbeatAt = new Date().toISOString();\n }\n\n const sinceLastWrite = Date.now() - this.lastAgentsWriteAt;\n if (!this.agentsFlushTimer && sinceLastWrite >= AGENTS_WRITE_THROTTLE_MS) {\n await this.writeAgentsSnapshot();\n return;\n }\n\n if (!this.agentsFlushTimer) {\n const delay = Math.max(0, AGENTS_WRITE_THROTTLE_MS - sinceLastWrite);\n this.pendingAgentsFlush = new Promise<void>((resolve, reject) => {\n this.pendingAgentsResolve = resolve;\n const timer = setTimeout(() => {\n this.agentsFlushTimer = null;\n this.pendingAgentsFlush = null;\n this.pendingAgentsResolve = null;\n this.writeAgentsSnapshot().then(resolve, reject);\n }, delay);\n if (typeof timer.unref === 'function') timer.unref();\n this.agentsFlushTimer = timer;\n });\n }\n await this.pendingAgentsFlush;\n }\n\n /** Write the newest pending agent snapshot to the registry file. */\n private async writeAgentsSnapshot(): Promise<void> {\n const agents = this.pendingAgents;\n const sessionId = this.currentSessionId;\n const owner = this.lastEntry;\n if (!agents || !sessionId || !owner || owner.sessionId !== sessionId) return;\n this.pendingAgents = null;\n this.lastAgentsWriteAt = Date.now();\n const status = deriveSessionStatus(agents);\n const nowIso = new Date().toISOString();\n\n await this.atomicUpdate((registry) => {\n if (this.currentSessionId !== sessionId || this.lastEntry !== owner) return;\n let entry = registry[sessionId];\n if (entry && !sameOwner(entry, owner)) return;\n if (!entry) {\n // Our entry vanished (dropped write / reset / pruned) \u2014 re-create it.\n entry = { ...owner };\n registry[sessionId] = entry;\n }\n entry.agents = agents;\n entry.agentCount = agents.length;\n entry.status = status;\n entry.lastHeartbeatAt = nowIso;\n });\n }\n\n /**\n * Cancel a scheduled trailing agents write (releasing any waiters). Used on\n * shutdown so a late timer can't resurrect an entry that markClosing() /\n * unregister() is about to finalize. Returns the snapshot that was pending.\n */\n private cancelPendingAgentsFlush(): AgentEntry[] | null {\n if (this.agentsFlushTimer) {\n clearTimeout(this.agentsFlushTimer);\n this.agentsFlushTimer = null;\n }\n this.pendingAgentsResolve?.();\n this.pendingAgentsResolve = null;\n this.pendingAgentsFlush = null;\n const pending = this.pendingAgents;\n this.pendingAgents = null;\n return pending;\n }\n\n /**\n * Mark the session as closing. Called during shutdown.\n * Stops the heartbeat timer.\n */\n async markClosing(): Promise<void> {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n if (!this.currentSessionId) return;\n const sessionId = this.currentSessionId;\n const owner = this.lastEntry;\n if (!owner) return;\n // Fold any coalesced-but-unwritten agent snapshot into this final write so\n // the trailing timer can't fire later and flip us back to active.\n const pendingAgents = this.cancelPendingAgentsFlush();\n await this.atomicUpdate((registry) => {\n const entry = registry[sessionId];\n if (!entry || !sameOwner(entry, owner)) return;\n if (pendingAgents) {\n entry.agents = pendingAgents;\n entry.agentCount = pendingAgents.length;\n }\n entry.status = 'closing';\n entry.lastHeartbeatAt = new Date().toISOString();\n });\n }\n\n /**\n * Remove the current session from the registry. Call on clean exit.\n */\n async unregister(): Promise<void> {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n if (!this.currentSessionId) return;\n // Drop any coalesced agent snapshot \u2014 the entry is being deleted, and a\n // late trailing write would resurrect it from lastEntry.\n this.cancelPendingAgentsFlush();\n const sid = this.currentSessionId;\n const owner = this.lastEntry;\n this.currentSessionId = null;\n this.lastEntry = null;\n await this.atomicUpdate((registry) => {\n const entry = registry[sid];\n if (owner && entry && sameOwner(entry, owner)) {\n delete registry[sid];\n }\n });\n }\n\n /**\n * List all non-stale sessions. Prunes stale entries automatically.\n */\n async list(): Promise<SessionRegistryEntry[]> {\n const registry = await this.readAndPrune();\n return Object.values(registry);\n }\n\n /**\n * Get a single session entry by ID. Returns undefined if not found or stale.\n */\n async get(sessionId: string): Promise<SessionRegistryEntry | undefined> {\n const registry = await this.readAndPrune();\n return registry[sessionId];\n }\n\n /**\n * List all sessions for a specific project (by slug).\n */\n async listByProject(projectSlug: string): Promise<SessionRegistryEntry[]> {\n const all = await this.list();\n return all.filter((e) => e.projectSlug === projectSlug);\n }\n\n /**\n * Return the registry file path. Useful for WebUI to watch/read.\n */\n get registryPath(): string {\n return this.filePath;\n }\n\n // \u2500\u2500 Internal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async heartbeat(): Promise<void> {\n if (!this.currentSessionId) return;\n try {\n const sessionId = this.currentSessionId;\n const owner = this.lastEntry;\n if (!owner || owner.sessionId !== sessionId) return;\n const nowIso = new Date().toISOString();\n await this.atomicUpdate((registry) => {\n if (this.currentSessionId !== sessionId || this.lastEntry !== owner) return;\n const entry = registry[sessionId];\n if (entry) {\n if (!sameOwner(entry, owner)) return;\n entry.lastHeartbeatAt = nowIso;\n // Status bound: if closing, don't revert\n if (entry.status !== 'closing') {\n const hasRunning = (entry.agents ?? []).some(\n (a) => a.status === 'running' || a.status === 'streaming',\n );\n entry.status = hasRunning ? 'active' : 'idle';\n }\n return;\n }\n // Our entry was externally removed/reset. Re-create it only while this\n // exact ownership generation is still current; a heartbeat queued\n // before a session switch must never resurrect the previous session.\n registry[sessionId] = { ...owner, lastHeartbeatAt: nowIso };\n });\n } catch {\n // Best-effort heartbeat \u2014 never throw\n }\n }\n\n private async readAndPrune(): Promise<Record<string, SessionRegistryEntry>> {\n try {\n const raw = await fs.readFile(this.filePath, 'utf8');\n const registry = parseRegistry(raw);\n const observed = new Map(\n Object.entries(registry).map(([id, entry]) => [\n id,\n { pid: entry.pid, lastHeartbeatAt: entry.lastHeartbeatAt },\n ]),\n );\n const now = Date.now();\n let pruned = false;\n\n for (const [id, entry] of Object.entries(registry)) {\n const heartbeatAt = Date.parse(entry.lastHeartbeatAt);\n if (!Number.isFinite(heartbeatAt)) {\n delete registry[id];\n pruned = true;\n continue;\n }\n const heartbeatAge = now - heartbeatAt;\n\n // A live session heartbeat does not prove every cached subagent still\n // exists. Keep the leader as the surface itself, but reap workers whose\n // activity stopped several minutes ago (the common shadow-agent ghost).\n const agents = Array.isArray(entry.agents) ? entry.agents : [];\n const liveAgents = agents.filter((agent) => {\n if (agent.id === 'leader') return true;\n const lastActivityAt = Date.parse(agent.lastActivityAt);\n return Number.isFinite(lastActivityAt) && now - lastActivityAt <= AGENT_STALE_MS;\n });\n if (liveAgents.length !== agents.length || entry.agentCount !== liveAgents.length) {\n entry.agents = liveAgents;\n entry.agentCount = liveAgents.length;\n pruned = true;\n }\n\n // A cleanly-closing session is removable after the grace only when its\n // process is actually gone. A hung cleanup can still hold the writer.\n if (entry.status === 'closing' && heartbeatAge > CLOSING_GRACE_MS) {\n if (!pidAlive(entry.pid)) {\n delete registry[id];\n pruned = true;\n }\n continue;\n }\n // A dead PID is definitive, and this probe used to sit behind the full\n // STALE_TIMEOUT_MS window. That meant a session killed without\n // `markClosing` (SIGKILL, taskkill /F, the rapid-Ctrl+C\n // `process.exit(130)` path) kept its last written status \u2014 for an idle\n // TUI, literally `'idle'` \u2014 so every reader showed a live-looking\n // session on a dead pid for up to 30s. Probing after two missed\n // heartbeats cuts that to ~10s while still leaving healthy entries\n // (which heartbeat every 5s) untouched on the hot read path.\n if (heartbeatAge > PID_CHECK_AFTER_MS && !pidAlive(entry.pid)) {\n delete registry[id];\n pruned = true;\n continue;\n }\n if (heartbeatAge <= STALE_TIMEOUT_MS) continue;\n\n // A live PID remains the owner even when its heartbeat is lost. It may\n // be wedged, but it can still hold and write the session journal.\n if (entry.status !== 'lost') {\n entry.status = 'lost';\n pruned = true;\n }\n }\n\n if (pruned) {\n // Merge the pruned snapshot under the same cross-process lock used by\n // registrations. Never rename an old full-file snapshot over a session\n // that another process registered after our read.\n await this.atomicUpdate((current) => {\n for (const [id, version] of observed) {\n const latest = current[id];\n if (\n !latest ||\n latest.pid !== version.pid ||\n latest.lastHeartbeatAt !== version.lastHeartbeatAt\n ) {\n continue;\n }\n const next = registry[id];\n if (next) current[id] = next;\n else delete current[id];\n }\n });\n /* v8 ignore stop */\n }\n\n return registry;\n } catch {\n return {};\n }\n }\n\n private async atomicUpdate(\n fn: (registry: Record<string, SessionRegistryEntry>) => void,\n required = false,\n ): Promise<void> {\n const lockPath = `${this.filePath}.lock`;\n const waitBudgetMs = required ? this.ownershipLockWaitMs : OPTIONAL_LOCK_WAIT_MS;\n const deadline = Date.now() + waitBudgetMs;\n let attempt = 0;\n\n try {\n await fs.mkdir(path.dirname(this.filePath), { recursive: true });\n // Directory scans are independent cleanup work and can be slow on\n // Windows/antivirus-scanned homes. Never hold the registry mutex while\n // pruning old temp files.\n await this.maybePruneStaleTempFiles();\n } catch (err) {\n if (required) {\n throw new SessionRegistryWriteError(\n `Session registry ownership update failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n return;\n }\n\n while (true) {\n try {\n // Acquire exclusive lock via O_CREAT | O_EXCL\n let lockHandle = await fs.open(lockPath, 'wx').catch(() => null);\n if (!lockHandle) {\n // Lock contended. A crashed process can leave its lock file behind\n // forever (the `finally` unlink never ran), which would wedge EVERY\n // future write \u2014 the registry silently stops updating. Break the lock\n // when its owner pid is dead or it has been held implausibly long\n // (legit holds are sub-millisecond), then retry the open once.\n if (await breakStaleLock(lockPath)) {\n /* v8 ignore start -- retry-open after breaking a stale lock; .catch only fires on contention */\n lockHandle = await fs.open(lockPath, 'wx').catch(() => null);\n /* v8 ignore stop */\n }\n if (!lockHandle) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) break;\n const retryDelayMs = Math.min(LOCK_RETRY_MAX_MS, 20 * (attempt + 1));\n await new Promise((resolve) =>\n setTimeout(resolve, Math.min(retryDelayMs, remainingMs)),\n );\n attempt += 1;\n continue;\n }\n }\n\n try {\n // Stamp the owner pid so other processes can detect a stale lock.\n /* v8 ignore start -- best-effort owner-pid stamp; .catch only fires on a write failure */\n await lockHandle.writeFile(String(process.pid)).catch(() => undefined);\n /* v8 ignore stop */\n const raw = await fs.readFile(this.filePath, 'utf8').catch(() => '{}');\n // Corruption-tolerant: a crash-zeroed or torn file must not abort\n // the write \u2014 starting from {} rewrites a healthy registry.\n const registry = parseRegistry(raw);\n fn(registry);\n // Windows writeAtomicFile can fail with EPERM/EBUSY/EACCES when the\n // destination file is momentarily held by an antivirus scan, a\n // coexisting watcher, or a peer that just opened the file for read.\n // The lock we hold here proves WE have exclusive write intent; the\n // brief contention on the data file is best handled by a tiny\n // bounded retry, NOT by escalating into a SessionRegistryWriteError\n // that aborts every ownership claim in the process.\n await this.writeAtomicWithRetry(registry);\n return; // success\n } finally {\n await lockHandle.close();\n /* v8 ignore start -- best-effort lock cleanup in finally; .catch only fires if the lock vanished */\n await fs.unlink(lockPath).catch(() => undefined);\n /* v8 ignore stop */\n }\n } catch (err) {\n if (err instanceof SessionOwnershipConflictError) throw err;\n if (required) {\n throw new SessionRegistryWriteError(\n `Session registry ownership update failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n // Telemetry/cleanup writes remain best-effort.\n return;\n }\n }\n if (required) {\n throw new SessionRegistryWriteError(\n `Session registry ownership update failed: lock remained busy for ${waitBudgetMs}ms at ${lockPath}`,\n );\n }\n // All retries exhausted \u2014 non-ownership telemetry update dropped.\n }\n\n private async writeAtomicLocked(registry: Record<string, SessionRegistryEntry>): Promise<void> {\n await this.writeAtomicFile(registry);\n }\n\n /**\n * Windows-safe wrapper around {@link writeAtomicLocked}. Retries on the small\n * set of transient OS errors that occur when an antivirus scan, an indexing\n * service, or a peer process opens the destination file for read at the\n * same instant rename/copy tries to land. Retries are short and bounded so\n * the heartbeat never wedges; the per-call cap is well below the 5 s\n * heartbeat interval.\n *\n * Errors that look persistent (ENOENT, JSON.parse-style, anything other than\n * the well-known transient codes) are rethrown immediately so the original\n * failure isn't masked.\n */\n private async writeAtomicWithRetry(\n registry: Record<string, SessionRegistryEntry>,\n ): Promise<void> {\n const transientCodes = new Set(['EPERM', 'EBUSY', 'EACCES', 'ETXTBSY', 'ENOTEMPTY']);\n const maxAttempts = 5;\n let lastErr: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n try {\n await this.writeAtomicLocked(registry);\n return;\n } catch (err) {\n lastErr = err;\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (!transientCodes.has(String(code ?? '')) || attempt === maxAttempts) {\n throw err;\n }\n // Tiny backoff with a 5 ms floor and 80 ms ceiling; combined with\n // maxAttempts=5 the worst-case wait is ~250 ms \u2014 acceptable for a\n // 5 s heartbeat and well under the cross-process lock budget.\n const delayMs = Math.min(80, 5 * attempt);\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));\n }\n\n private async maybePruneStaleTempFiles(): Promise<void> {\n if (this.tempPrunePromise) {\n await this.tempPrunePromise;\n return;\n }\n const now = Date.now();\n if (now - this.lastTempPruneAt < TEMP_PRUNE_INTERVAL_MS) return;\n\n this.lastTempPruneAt = now;\n this.tempPrunePromise = this.pruneStaleTempFiles();\n try {\n await this.tempPrunePromise;\n } finally {\n this.tempPrunePromise = null;\n }\n }\n\n private async writeAtomicFile(registry: Record<string, SessionRegistryEntry>): Promise<void> {\n await writeAtomicFile(this.filePath, registry);\n }\n\n private async pruneStaleTempFiles(): Promise<void> {\n await pruneStaleTempFiles(this.filePath);\n }\n}\n\n/** Singleton \u2014 created once per process. */\nlet _instance: SessionRegistry | null = null;\n\nexport function getSessionRegistry(globalRoot?: string): SessionRegistry {\n if (!_instance && globalRoot) {\n _instance = new SessionRegistry(globalRoot);\n }\n if (!_instance) {\n /* v8 ignore next -- the singleton is initialized by the first call in every test/process */\n throw new Error('SessionRegistry not initialized. Call getSessionRegistry(globalRoot) first.');\n }\n return _instance;\n}\n\nexport function hasSessionRegistry(): boolean {\n return _instance !== null;\n}\n", "/**\n * Crash- and Windows-safe file primitives behind {@link SessionRegistry}.\n *\n * The registry is one JSON file written by every WrongStack process, so its\n * write path carries all the platform scar tissue: an advisory lock file that a\n * crashed owner can leave behind, a fsync-before-rename that a system crash\n * would otherwise turn into a zero-filled registry, a copyFile fallback for\n * Windows MoveFile sharing violations, and temp-file litter from any of the\n * above. All of that lives here; `session-registry.ts` keeps the state machine.\n *\n * Every function is keyed on the registry file path rather than the registry\n * instance \u2014 none of them need registry state.\n *\n * @module session-registry-atomic-file\n */\nimport { randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { SessionRegistryEntry } from './session-registry-types.js';\nimport { isPidAlive } from './utils/pid.js';\n\n// A held lock is released within milliseconds; anything older is a crashed\n// owner's leftover and is safe to break so writes never wedge permanently.\nconst STALE_LOCK_MS = 10_000;\n/** Also the scan interval for {@link pruneStaleTempFiles} \u2014 see its caller. */\nexport const STALE_TMP_MS = 60_000;\nconst MAX_STALE_TMP_FILES = 20;\n\n/**\n * Break a contended lock if it is stale: the recorded owner pid is no longer\n * alive, or the lock is older than {@link STALE_LOCK_MS}. Returns true when the\n * lock was removed (caller should retry acquisition). Best-effort and\n * race-tolerant \u2014 a fresh lock (age ~0, live owner) is never broken, so the\n * common concurrent case self-heals on the next heartbeat.\n */\nexport async function breakStaleLock(lockPath: string): Promise<boolean> {\n try {\n const [stat, content] = await Promise.all([\n fs.stat(lockPath),\n /* v8 ignore start -- best-effort lock-content read; .catch only fires if the lock vanished */\n fs.readFile(lockPath, 'utf8').catch(() => ''),\n /* v8 ignore stop */\n ]);\n const ageMs = Date.now() - stat.mtimeMs;\n const ownerPid = Number.parseInt(content.trim(), 10);\n const ownerDead =\n Number.isInteger(ownerPid) &&\n ownerPid > 0 &&\n ownerPid !== process.pid &&\n !isPidAlive(ownerPid);\n if (ownerDead || ageMs > STALE_LOCK_MS) {\n /* v8 ignore start -- best-effort stale-lock removal; .catch only fires if the lock vanished */\n await fs.unlink(lockPath).catch(() => undefined);\n /* v8 ignore stop */\n return true;\n }\n return false;\n } catch {\n // stat failed \u2192 the lock vanished underneath us; let the caller retry.\n /* v8 ignore next -- defensive: a vanished lock between stat and read is fine */\n return true;\n }\n}\n\nexport async function writeAtomicFile(\n filePath: string,\n registry: Record<string, SessionRegistryEntry>,\n): Promise<void> {\n const tmp = path.join(\n path.dirname(filePath),\n `.${path.basename(filePath)}.${randomUUID().slice(0, 8)}.tmp`,\n );\n let tmpPersisted = false;\n try {\n // Write + fsync BEFORE the rename: without the fsync, a system crash\n // can journal the rename metadata while the data blocks were never\n // flushed, leaving a zero-filled (all-NUL) registry file on reboot.\n const handle = await fs.open(tmp, 'w');\n try {\n await handle.writeFile(JSON.stringify(registry, null, 2), 'utf8');\n await handle.sync().catch(() => undefined);\n } finally {\n await handle.close();\n }\n tmpPersisted = true;\n // Cross-platform atomic publish:\n // 1. POSIX: rename(2) is atomic; if the destination exists it is\n // replaced in one step.\n // 2. Windows (Node 22): fs.rename uses MoveFile, which fails with\n // EPERM/EBUSY when the destination is briefly held by an antivirus\n // scan, a peer process reading the file, or an indexer.\n // fs.copyFile uses CopyFileEx/CopyFile2 with REPLACE_EXISTING,\n // which DOES overwrite an existing destination and tolerates short\n // sharing-violation windows better than MoveFile. Doing copyFile\n // first, then fs.unlink(tmp), gives us \"best of both worlds\":\n // the published bytes are the freshly written + fsynced ones,\n // and the destination is replaced even when MoveFile refuses.\n // The publish is no longer strictly atomic across the OS boundary,\n // but the only readers (cross-process observers) re-read after a\n // brief settle, and we still hold the cross-process advisory lock\n // so no concurrent writer can interleave.\n try {\n await fs.rename(tmp, filePath);\n tmpPersisted = false;\n } catch (renameErr) {\n const code = (renameErr as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'EPERM' || code === 'EBUSY' || code === 'EACCES') {\n // Copy bytes to destination (REPLACE_EXISTING is the default for\n // fs.copyFile when COPYFILE_FAIL_IF_EXISTS is not set), fsync the\n // destination so readers don't see zero-filled data on a crash,\n // then drop the temp file.\n await fs.copyFile(tmp, filePath);\n try {\n const destHandle = await fs.open(filePath, 'r+');\n try {\n await destHandle.sync().catch(() => undefined);\n } finally {\n await destHandle.close();\n }\n } catch {\n // Best-effort fsync of the destination; failure is non-fatal\n // because we already wrote the bytes and the OS will flush.\n }\n await fs.unlink(tmp).catch(() => undefined);\n tmpPersisted = false;\n } else {\n throw renameErr;\n }\n }\n } catch (err) {\n /* v8 ignore start -- rename-failure cleanup: best-effort tmp unlink + rethrow (atomicUpdate swallows it) */\n if (tmpPersisted) await fs.unlink(tmp).catch(() => undefined);\n throw err;\n /* v8 ignore stop */\n }\n}\n\nexport async function pruneStaleTempFiles(filePath: string): Promise<void> {\n try {\n const dir = path.dirname(filePath);\n const base = path.basename(filePath);\n const now = Date.now();\n const stale: Array<{ name: string; mtimeMs: number }> = [];\n\n for (const name of await fs.readdir(dir)) {\n const isTemp =\n (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith('.tmp');\n if (!isTemp) continue;\n /* v8 ignore start -- best-effort temp stat; .catch(null)+continue only fire when the temp vanished */\n const stat = await fs.stat(path.join(dir, name)).catch(() => null);\n if (!stat) continue;\n /* v8 ignore stop */\n if (now - stat.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat.mtimeMs });\n }\n\n stale.sort((a, b) => b.mtimeMs - a.mtimeMs);\n await Promise.all(\n stale.slice(MAX_STALE_TMP_FILES).map(async ({ name }) => {\n /* v8 ignore start -- best-effort temp removal; .catch only fires if the temp vanished */\n await fs.unlink(path.join(dir, name)).catch(() => undefined);\n /* v8 ignore stop */\n }),\n );\n } catch {\n // best-effort cleanup must not block registry heartbeats\n }\n}\n", "/**\n * Pure projection helpers for {@link AgentStatusTracker}.\n *\n * Everything here turns raw event-payload shapes into the bounded, snapshot-safe\n * values the SessionRegistry stores: compacted tool inputs, completed-tool\n * receipts, and rolling activity totals. No I/O, no tracker state \u2014 split out of\n * `agent-status-tracker.ts` so the tracker file is just the event wiring.\n *\n * @module agent-status-helpers\n */\nimport type {\n AgentActivityTotals,\n AgentRecentMail,\n AgentRecentTool,\n AgentTodoItem,\n} from './session-registry.js';\n\n/** Registry snapshots must stay small even when a tool receives a large patch. */\nconst TOOL_TEXT_CAP = 360;\nconst TOUCHED_FILE_LIMIT = 200;\nconst TODO_TEXT_CAP = 360;\nconst TODO_LIMIT = 32;\n\nexport interface PendingTool {\n name: string;\n input?: unknown | undefined;\n startedAt: number;\n}\n\nexport interface CompletedToolPayload {\n id?: string | undefined;\n name: string;\n durationMs?: number | undefined;\n ok?: boolean | undefined;\n input?: unknown | undefined;\n output?: string | undefined;\n outputLines?: number | undefined;\n outputBytes?: number | undefined;\n outputTokens?: number | undefined;\n}\n\nfunction lineCount(value: string): number {\n return value.length === 0 ? 0 : value.split(/\\r?\\n/).length;\n}\n\nfunction patchDelta(value: string): { addedLines: number; removedLines: number } {\n let addedLines = 0;\n let removedLines = 0;\n const hunkStart = value.indexOf('@@');\n if (hunkStart === -1) return { addedLines, removedLines };\n for (const line of value.slice(hunkStart).split(/\\r?\\n/)) {\n if (line.startsWith('+++') || line.startsWith('---')) continue;\n if (line.startsWith('+')) addedLines += 1;\n else if (line.startsWith('-')) removedLines += 1;\n }\n return { addedLines, removedLines };\n}\n\nfunction compactText(value: string): string {\n return value.length <= TOOL_TEXT_CAP ? value : `${value.slice(0, TOOL_TEXT_CAP - 1)}\u2026`;\n}\n\nexport function boundedText(value: string, cap: number): string {\n const trimmed = value.trim();\n return trimmed.length <= cap ? trimmed : `${trimmed.slice(0, cap - 1)}\u2026`;\n}\n\nexport function compactTodos(value: unknown): AgentTodoItem[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const todos: AgentTodoItem[] = [];\n for (const candidate of value) {\n if (typeof candidate !== 'object' || candidate === null) continue;\n const todo = candidate as Record<string, unknown>;\n const id = typeof todo['id'] === 'string' ? todo['id'].trim() : '';\n const content =\n typeof todo['content'] === 'string' ? boundedText(todo['content'], TODO_TEXT_CAP) : '';\n const status = todo['status'];\n if (\n !id ||\n !content ||\n (status !== 'pending' && status !== 'in_progress' && status !== 'completed')\n ) {\n continue;\n }\n const activeForm =\n typeof todo['activeForm'] === 'string'\n ? boundedText(todo['activeForm'], TODO_TEXT_CAP)\n : undefined;\n todos.push({ id, content, status, ...(activeForm ? { activeForm } : {}) });\n if (todos.length >= TODO_LIMIT) break;\n }\n return todos;\n}\n\nfunction compactToolInput(input: unknown): {\n input?: unknown | undefined;\n inputLines?: number | undefined;\n oldLines?: number | undefined;\n newLines?: number | undefined;\n addedLines?: number | undefined;\n removedLines?: number | undefined;\n} {\n if (!input || typeof input !== 'object' || Array.isArray(input)) {\n return typeof input === 'string' ? { input: compactText(input) } : {};\n }\n const source = input as Record<string, unknown>;\n const safe: Record<string, unknown> = {};\n const safeKeys = [\n 'file_path',\n 'filePath',\n 'path',\n 'filename',\n 'target_file',\n 'targetFile',\n 'line',\n 'start_line',\n 'startLine',\n 'end_line',\n 'endLine',\n 'offset',\n 'limit',\n 'command',\n 'cmd',\n 'url',\n 'href',\n 'uri',\n 'query',\n 'pattern',\n ];\n for (const key of safeKeys) {\n const value = source[key];\n if (typeof value === 'string') safe[key] = compactText(value);\n else if (typeof value === 'number' || typeof value === 'boolean') safe[key] = value;\n }\n\n const content = [source['content'], source['text'], source['data']].find(\n (value): value is string => typeof value === 'string',\n );\n const oldText = [source['old_string'], source['oldString'], source['search']].find(\n (value): value is string => typeof value === 'string',\n );\n const newText = [source['new_string'], source['newString'], source['replacement']].find(\n (value): value is string => typeof value === 'string',\n );\n const patch = [source['patch'], source['diff']].find(\n (value): value is string => typeof value === 'string',\n );\n const delta = patch ? patchDelta(patch) : undefined;\n\n return {\n ...(Object.keys(safe).length > 0 ? { input: safe } : {}),\n ...(content !== undefined ? { inputLines: lineCount(content) } : {}),\n ...(oldText !== undefined ? { oldLines: lineCount(oldText) } : {}),\n ...(newText !== undefined ? { newLines: lineCount(newText) } : {}),\n ...(delta && delta.addedLines > 0 ? { addedLines: delta.addedLines } : {}),\n ...(delta && delta.removedLines > 0 ? { removedLines: delta.removedLines } : {}),\n };\n}\n\nexport function completedToolReceipt(\n payload: CompletedToolPayload,\n pending?: PendingTool,\n): AgentRecentTool {\n const completedAt = Date.now();\n const durationMs = Math.max(\n 0,\n payload.durationMs ?? completedAt - (pending?.startedAt ?? completedAt),\n );\n const compact = compactToolInput(payload.input ?? pending?.input);\n return {\n id: payload.id ?? `${payload.name}:${completedAt}`,\n name: payload.name,\n startedAt: pending?.startedAt ?? Math.max(0, completedAt - durationMs),\n completedAt,\n durationMs,\n ok: payload.ok !== false,\n ...compact,\n ...(payload.output !== undefined ? { output: compactText(payload.output) } : {}),\n ...(payload.outputLines !== undefined ? { outputLines: payload.outputLines } : {}),\n ...(payload.outputBytes !== undefined ? { outputBytes: payload.outputBytes } : {}),\n ...(payload.outputTokens !== undefined ? { outputTokens: payload.outputTokens } : {}),\n };\n}\n\nexport function emptyActivityTotals(): AgentActivityTotals {\n return {\n filesTouched: [],\n reads: 0,\n writes: 0,\n edits: 0,\n terminalCalls: 0,\n webCalls: 0,\n searches: 0,\n otherCalls: 0,\n mailReceived: 0,\n mailSent: 0,\n linesRead: 0,\n linesWritten: 0,\n linesAdded: 0,\n linesRemoved: 0,\n };\n}\n\nexport function addMailTotal(\n current: AgentActivityTotals | undefined,\n direction: AgentRecentMail['direction'],\n): AgentActivityTotals {\n const next = { ...(current ?? emptyActivityTotals()) };\n if (direction === 'incoming') next.mailReceived += 1;\n else next.mailSent += 1;\n return next;\n}\n\nfunction toolActivityKind(\n name: string,\n): 'read' | 'write' | 'edit' | 'terminal' | 'web' | 'search' | 'other' {\n const normalized = name.toLowerCase().replace(/[.:/-]+/g, '_');\n if (/^(read|view|open_file|read_file)|file_read/.test(normalized)) return 'read';\n if (/^(write|create|save)|file_write/.test(normalized)) return 'write';\n if (/edit|update|patch|replace|apply_patch/.test(normalized)) return 'edit';\n if (/bash|shell|terminal|exec|command|powershell|cmd/.test(normalized)) return 'terminal';\n if (/fetch|browser|browse|http|url|web/.test(normalized)) return 'web';\n if (/search|grep|find|glob|query/.test(normalized)) return 'search';\n return 'other';\n}\n\nfunction receiptFilePath(receipt: AgentRecentTool): string | undefined {\n if (!receipt.input || typeof receipt.input !== 'object' || Array.isArray(receipt.input)) {\n return undefined;\n }\n const input = receipt.input as Record<string, unknown>;\n for (const key of ['file_path', 'filePath', 'path', 'filename', 'target_file', 'targetFile']) {\n const value = input[key];\n if (typeof value === 'string' && value.trim()) return value.trim();\n }\n return undefined;\n}\n\nexport function addToolActivity(\n current: AgentActivityTotals | undefined,\n receipt: AgentRecentTool,\n): AgentActivityTotals {\n const next: AgentActivityTotals = {\n ...(current ?? emptyActivityTotals()),\n filesTouched: [...(current?.filesTouched ?? [])],\n };\n const kind = toolActivityKind(receipt.name);\n if (kind === 'read') {\n next.reads += 1;\n next.linesRead += receipt.outputLines ?? 0;\n } else if (kind === 'write') {\n next.writes += 1;\n next.linesWritten += receipt.inputLines ?? 0;\n } else if (kind === 'edit') {\n next.edits += 1;\n next.linesAdded += receipt.addedLines ?? receipt.newLines ?? 0;\n next.linesRemoved += receipt.removedLines ?? receipt.oldLines ?? 0;\n } else if (kind === 'terminal') next.terminalCalls += 1;\n else if (kind === 'web') next.webCalls += 1;\n else if (kind === 'search') next.searches += 1;\n else next.otherCalls += 1;\n\n const filePath = receiptFilePath(receipt);\n if (\n filePath &&\n !next.filesTouched.includes(filePath) &&\n next.filesTouched.length < TOUCHED_FILE_LIMIT\n ) {\n next.filesTouched.push(filePath);\n }\n return next;\n}\n\nexport function clampPct(pct: number): number {\n if (!Number.isFinite(pct)) return 0;\n return Math.max(0, Math.min(100, pct));\n}\n", "/**\n * AgentStatusTracker \u2014 subscribes to EventBus events and keeps the\n * SessionRegistry updated with live agent status.\n *\n * Created once per process during boot. Listens for:\n * - agent.run.started / agent.run.completed \u2192 agent status changes\n * - tool.started / tool.executed \u2192 current tool tracking\n * - brain.ask_human \u2192 waiting_user status\n * - fleet events (subagent spawn/start/done) \u2192 agent entries\n *\n * @module agent-status-tracker\n */\nimport {\n addMailTotal,\n addToolActivity,\n boundedText,\n clampPct,\n compactTodos,\n type CompletedToolPayload,\n completedToolReceipt,\n emptyActivityTotals,\n type PendingTool,\n} from './agent-status-helpers.js';\nimport type { EventBus } from './kernel/events.js';\nimport type {\n AgentEntry,\n AgentLiveStatus,\n AgentRecentMail,\n AgentRecentTool,\n AgentTodoItem,\n SessionRegistry,\n} from './session-registry.js';\n\n/** A finished (idle/error) subagent older than this is reaped from the fleet view. */\nconst AGENT_REAP_MS = 30_000;\n/** How often the reaper sweeps for finished subagents. */\nconst AGENT_SWEEP_INTERVAL_MS = 10_000;\n/**\n * How long a PendingTool entry survives without its matching `tool.executed` /\n * `subagent.tool_executed` event before being auto-evicted. Prevents orphaned\n * entries (tool started event \u2192 executed event never arrives due to abort,\n * provider crash, or process kill) from accumulating in the pending-tools Maps\n * and retaining their full tool-input objects for the process lifetime.\n */\nconst PENDING_TOOL_TTL_MS = 300_000;\n/** Max chars of streamed assistant text kept in the registry (the live tail). */\nconst PARTIAL_TEXT_CAP = 1200;\n/** Min gap between registry flushes triggered purely by streamed text. */\nconst PARTIAL_FLUSH_THROTTLE_MS = 300;\n/** Completed receipts retained per agent for cross-process Office history. */\nconst RECENT_TOOL_LIMIT = 12;\nconst RECENT_MAIL_LIMIT = 12;\n/** Registry snapshots must stay small even when a task or prompt is huge. */\nconst TASK_TEXT_CAP = 1200;\nconst PROMPT_TEXT_CAP = 6000;\n\nexport interface AgentStatusTrackerOptions {\n events: EventBus;\n registry: SessionRegistry;\n /** Session id whose live agent state is mirrored by this tracker. */\n sessionId?: string | (() => string | undefined) | undefined;\n /** Leader agent name shown in the registry. Default: \"leader\". */\n leaderName?: string | undefined;\n /**\n * Best-effort callback fired after each registry write settles. Used to nudge\n * local WebUI servers (FleetNotifier) so cross-process status reaches the map\n * without waiting on their file-watch/poll. Never block or throw.\n */\n onUpdate?: (() => void) | undefined;\n}\n\nexport class AgentStatusTracker {\n private readonly events: EventBus;\n private readonly registry: SessionRegistry;\n private readonly sessionId: string | (() => string | undefined) | undefined;\n private readonly leaderName: string;\n\n // Live agent map: agentId \u2192 AgentEntry\n private agents = new Map<string, AgentEntry>();\n // Last full agent list flushed (leader + subagents). Lets external consumers\n // read the current state synchronously without re-deriving it.\n private lastAgents: AgentEntry[] = [];\n\n // Leader tracking\n private leaderStatus: AgentLiveStatus = 'idle';\n private leaderCurrentTool: string | undefined;\n private leaderCurrentTask: string | undefined;\n private leaderIterations = 0;\n private leaderToolCalls = 0;\n private leaderCostUsd = 0;\n private leaderTokensIn = 0;\n private leaderTokensOut = 0;\n private leaderCtxPct: number | undefined;\n private leaderModel: string | undefined;\n private leaderPartialText = '';\n private leaderStartedAt: string | undefined;\n private leaderRecentTools: AgentRecentTool[] = [];\n private leaderRecentMail: AgentRecentMail[] = [];\n private leaderTodos: AgentTodoItem[] = [];\n private leaderLatestPrompt: string | undefined;\n private leaderLatestPromptAt: number | undefined;\n private leaderActivity = emptyActivityTotals();\n private leaderPendingTools = new Map<string, PendingTool>();\n private subagentPendingTools = new Map<string, PendingTool>();\n private seenIncomingMail = new Set<string>();\n private seenOutgoingMail = new Set<string>();\n private static readonly SEEN_MAIL_LIMIT = 10_000;\n\n private rememberMailId(seen: Set<string>, messageId: string): boolean {\n if (seen.has(messageId)) return false;\n seen.add(messageId);\n if (seen.size > AgentStatusTracker.SEEN_MAIL_LIMIT) {\n const oldest = seen.values().next().value;\n if (oldest !== undefined) seen.delete(oldest);\n }\n return true;\n }\n\n private unsubscribers: Array<() => void> = [];\n private readonly onUpdate: (() => void) | undefined;\n private sweepTimer: ReturnType<typeof setInterval> | null = null;\n private partialTimer: ReturnType<typeof setTimeout> | null = null;\n /** Serialize registry writes so concurrent flush() calls don't race. */\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: AgentStatusTrackerOptions) {\n this.events = opts.events;\n this.registry = opts.registry;\n this.sessionId = opts.sessionId;\n this.leaderName = opts.leaderName ?? 'leader';\n this.onUpdate = opts.onUpdate;\n }\n\n /** Current full agent list (leader + subagents) as of the last flush. */\n getAgents(): AgentEntry[] {\n return this.lastAgents.length > 0 ? [...this.lastAgents] : [];\n }\n\n start(): void {\n const on = (pattern: string, fn: (event: string, payload: unknown) => void): (() => void) =>\n this.events.onPattern(pattern, (event, payload) => {\n if (!this.acceptsSession(payload)) return;\n fn(event, payload);\n });\n\n // Leader events\n this.unsubscribers.push(\n on('agent.run.started', (_event, payload) => {\n const p = payload as\n | { at?: string; model?: string; ctx?: unknown; inputText?: string }\n | undefined;\n this.markLeaderStarted(p?.at);\n this.captureLeaderContext(p?.ctx);\n if (p?.model) this.leaderModel = p.model;\n if (p?.inputText?.trim()) {\n const prompt = boundedText(p.inputText, PROMPT_TEXT_CAP);\n this.leaderLatestPrompt = prompt;\n this.leaderLatestPromptAt = p.at ? Date.parse(p.at) : Date.now();\n if (!Number.isFinite(this.leaderLatestPromptAt)) this.leaderLatestPromptAt = Date.now();\n this.leaderCurrentTask = boundedText(p.inputText, TASK_TEXT_CAP);\n }\n this.leaderStatus = 'running';\n this.leaderIterations++;\n this.flush();\n }),\n );\n\n // Capture the leader's model + context fill from each iteration's context.\n this.unsubscribers.push(\n on('iteration.started', (_e, payload) => {\n const p = payload as { ctx?: unknown; index?: number } | undefined;\n const ctx = p?.ctx;\n this.markLeaderStarted();\n this.leaderStatus = 'running';\n if (typeof p?.index === 'number') {\n this.leaderIterations = Math.max(this.leaderIterations, p.index + 1);\n }\n if (!ctx) {\n this.flush();\n return;\n }\n this.captureLeaderContext(ctx);\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('agent.run.completed', (_event, payload) => {\n const p = payload as { status?: string; ctx?: unknown } | undefined;\n this.captureLeaderContext(p?.ctx);\n this.leaderStatus = p?.status === 'failed' ? 'error' : 'idle';\n this.leaderCurrentTool = undefined;\n this.leaderCurrentTask = undefined;\n this.leaderPartialText = '';\n if (this.leaderStatus === 'idle') this.leaderStartedAt = undefined;\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('agent.run.error', (_event, payload) => {\n const p = payload as { ctx?: unknown } | undefined;\n this.captureLeaderContext(p?.ctx);\n this.leaderStatus = 'error';\n this.leaderCurrentTool = undefined;\n this.leaderCurrentTask = undefined;\n this.leaderPartialText = '';\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('iteration.completed', (_event, payload) => {\n const p = payload as { ctx?: unknown } | undefined;\n this.captureLeaderContext(p?.ctx);\n this.flush();\n }),\n );\n\n // Tool events \u2014 track current tool\n this.unsubscribers.push(\n on('tool.started', (_event, payload) => {\n const p = payload as { id?: string; name?: string; input?: unknown } | undefined;\n if (p?.name) {\n this.markLeaderStarted();\n this.leaderCurrentTool = p.name;\n this.leaderToolCalls++;\n this.leaderPendingTools.set(p.id ?? p.name, {\n name: p.name,\n input: p.input,\n startedAt: Date.now(),\n });\n }\n this.leaderStatus = 'running';\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('tool.executed', (_event, payload) => {\n const p = payload as CompletedToolPayload;\n if (p?.name) {\n const key = p.id ?? p.name;\n const receipt = completedToolReceipt(p, this.leaderPendingTools.get(key));\n this.leaderRecentTools = [receipt, ...this.leaderRecentTools].slice(0, RECENT_TOOL_LIMIT);\n this.leaderActivity = addToolActivity(this.leaderActivity, receipt);\n this.leaderPendingTools.delete(key);\n }\n this.leaderCurrentTool = undefined;\n this.flush();\n }),\n );\n\n // Brain ask_human \u2192 waiting for user input\n this.unsubscribers.push(\n on('brain.ask_human', () => {\n this.markLeaderStarted();\n this.leaderStatus = 'waiting_user';\n this.flush();\n }),\n );\n\n const recordMail = (direction: AgentRecentMail['direction'], payload: unknown): void => {\n const p = payload as\n | {\n messageId?: string;\n from?: string;\n to?: string;\n type?: string;\n subject?: string;\n }\n | undefined;\n if (!p?.messageId) return;\n const seen = direction === 'incoming' ? this.seenIncomingMail : this.seenOutgoingMail;\n if (!this.rememberMailId(seen, p.messageId)) return;\n const receipt: AgentRecentMail = {\n id: p.messageId,\n direction,\n from: p.from ?? '?',\n to: p.to ?? (direction === 'incoming' ? 'leader' : '?'),\n type: p.type ?? 'note',\n subject: p.subject ?? 'Message',\n at: Date.now(),\n };\n const directAgentId = direction === 'outgoing' ? p.from : p.to;\n const entry = directAgentId ? this.agents.get(directAgentId) : undefined;\n if (entry) {\n entry.recentMail = [receipt, ...(entry.recentMail ?? [])].slice(0, RECENT_MAIL_LIMIT);\n entry.activity = addMailTotal(entry.activity, direction);\n } else {\n this.leaderRecentMail = [receipt, ...this.leaderRecentMail].slice(0, RECENT_MAIL_LIMIT);\n this.leaderActivity = addMailTotal(this.leaderActivity, direction);\n }\n this.flush();\n };\n\n this.unsubscribers.push(\n on('mailbox.message_sent', (_event, payload) => recordMail('outgoing', payload)),\n on('mailbox.received', (_event, payload) => recordMail('incoming', payload)),\n );\n\n // Streaming events\n this.unsubscribers.push(\n on('llm.stream_started', () => {\n this.markLeaderStarted();\n this.leaderStatus = 'streaming';\n // A new response is starting \u2014 drop the previous turn's live tail.\n this.leaderPartialText = '';\n this.flush();\n }),\n );\n\n // Live assistant text \u2014 accumulate the streamed tail so a cross-process\n // watcher sees the response form. Flushed on a throttle, NOT per token\n // (text_delta fires once per chunk \u2192 would thrash the shared registry).\n this.unsubscribers.push(\n on('provider.text_delta', (_e, payload) => {\n const p = payload as { text?: string; ctx?: unknown } | undefined;\n const text = p?.text;\n if (!text) return;\n this.markLeaderStarted();\n this.captureLeaderContext(p?.ctx);\n this.leaderStatus = 'streaming';\n const next = this.leaderPartialText + text;\n this.leaderPartialText =\n next.length > PARTIAL_TEXT_CAP ? next.slice(next.length - PARTIAL_TEXT_CAP) : next;\n this.schedulePartialFlush();\n }),\n );\n\n this.unsubscribers.push(\n on('provider.response', (_e, payload) => {\n const p = payload as { ctx?: unknown } | undefined;\n this.captureLeaderContext(p?.ctx);\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('provider.fallback', (_e, payload) => {\n const p = payload as { to?: { providerId?: string; model?: string } } | undefined;\n if (p?.to?.model) {\n this.leaderModel = p.to.providerId ? `${p.to.providerId}/${p.to.model}` : p.to.model;\n this.flush();\n }\n }),\n );\n\n this.unsubscribers.push(\n on('ctx.pct', (_e, payload) => {\n const p = payload as { load?: number } | undefined;\n if (typeof p?.load === 'number' && Number.isFinite(p.load)) {\n this.leaderCtxPct = clampPct(Math.round(p.load * 100));\n this.flush();\n }\n }),\n );\n\n // Leader token + cost accounting (per provider call \u2014 accumulate).\n this.unsubscribers.push(\n on('token.accounted', (_e, payload) => {\n const p = payload as\n | { usage?: { input?: number; output?: number }; cost?: { total?: number } }\n | undefined;\n if (!p) return;\n this.leaderTokensIn += p.usage?.input ?? 0;\n this.leaderTokensOut += p.usage?.output ?? 0;\n this.leaderCostUsd += p.cost?.total ?? 0;\n this.flush();\n }),\n );\n\n // \u2500\u2500 Subagent tracking \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // These are the real fleet lifecycle events (emitted by MultiAgentHost /\n // the subagent runner / director). The previous code listened to a\n // `fleet.subagent.*` namespace that nothing emits, so subagents never\n // reached the registry \u2014 only the leader showed up.\n const touch = (id: string): AgentEntry => {\n let entry = this.agents.get(id);\n if (!entry) {\n const now = new Date().toISOString();\n entry = {\n id,\n name: id,\n status: 'idle',\n iterations: 0,\n toolCalls: 0,\n startedAt: now,\n lastActivityAt: now,\n };\n this.agents.set(id, entry);\n }\n entry.lastActivityAt = new Date().toISOString();\n return entry;\n };\n\n this.unsubscribers.push(\n on('subagent.spawned', (_e, payload) => {\n const p = payload as\n | {\n subagentId?: string;\n name?: string;\n model?: string;\n taskId?: string;\n description?: string;\n }\n | undefined;\n if (!p?.subagentId) return;\n const entry = touch(p.subagentId);\n entry.name = p.name?.trim() || entry.name;\n if (p.model) entry.model = p.model;\n if (p.taskId) entry.taskId = p.taskId;\n if (p.description?.trim()) entry.currentTask = boundedText(p.description, TASK_TEXT_CAP);\n /* v8 ignore next -- touch() always sets startedAt on creation */\n if (!entry.startedAt) entry.startedAt = new Date().toISOString();\n entry.status = 'running';\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('subagent.ctx_pct', (_e, payload) => {\n const p = payload as { subagentId?: string; load?: number } | undefined;\n if (!p?.subagentId) return;\n const entry = touch(p.subagentId);\n if (typeof p.load === 'number') entry.ctxPct = clampPct(Math.round(p.load * 100));\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('subagent.task_started', (_e, payload) => {\n const p = payload as\n | { subagentId?: string; taskId?: string; description?: string }\n | undefined;\n if (!p?.subagentId) return;\n const entry = touch(p.subagentId);\n entry.status = 'running';\n if (p.taskId) entry.taskId = p.taskId;\n if (p.description?.trim()) entry.currentTask = boundedText(p.description, TASK_TEXT_CAP);\n /* v8 ignore next -- touch() always sets startedAt on creation */\n if (!entry.startedAt) entry.startedAt = new Date().toISOString();\n entry.iterations++;\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('subagent.tool_started', (_e, payload) => {\n const p = payload as\n | { subagentId?: string; id?: string; name?: string; input?: unknown }\n | undefined;\n if (!p?.subagentId || !p.name) return;\n const entry = touch(p.subagentId);\n entry.status = 'running';\n entry.currentTool = p.name;\n this.subagentPendingTools.set(`${p.subagentId}:${p.id ?? p.name}`, {\n name: p.name,\n input: p.input,\n startedAt: Date.now(),\n });\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('subagent.tool_executed', (_e, payload) => {\n const p = payload as (CompletedToolPayload & { subagentId?: string }) | undefined;\n if (!p?.subagentId || !p.name) return;\n const entry = touch(p.subagentId);\n entry.status = 'running';\n /* v8 ignore next -- touch() always sets startedAt on creation */\n if (!entry.startedAt) entry.startedAt = new Date().toISOString();\n const key = `${p.subagentId}:${p.id ?? p.name}`;\n const receipt = completedToolReceipt(p, this.subagentPendingTools.get(key));\n entry.recentTools = [receipt, ...(entry.recentTools ?? [])].slice(0, RECENT_TOOL_LIMIT);\n entry.activity = addToolActivity(entry.activity, receipt);\n this.subagentPendingTools.delete(key);\n entry.currentTool = undefined;\n entry.toolCalls++;\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('subagent.iteration_summary', (_e, payload) => {\n const p = payload as\n | {\n subagentId?: string;\n iteration?: number;\n toolCalls?: number;\n currentTool?: string;\n costUsd?: number;\n partialText?: string;\n }\n | undefined;\n if (!p?.subagentId) return;\n const entry = touch(p.subagentId);\n entry.status = 'running';\n /* v8 ignore next -- touch() always sets startedAt on creation */\n if (!entry.startedAt) entry.startedAt = new Date().toISOString();\n if (typeof p.iteration === 'number') entry.iterations = p.iteration;\n if (typeof p.toolCalls === 'number') entry.toolCalls = p.toolCalls;\n if (typeof p.costUsd === 'number') entry.costUsd = p.costUsd;\n if (p.currentTool) entry.currentTool = p.currentTool;\n // Live streamed tail of THIS subagent's current response (the runner\n // already accumulates it) \u2014 capped to the same budget as the leader.\n if (typeof p.partialText === 'string') {\n entry.partialText =\n p.partialText.length > PARTIAL_TEXT_CAP\n ? p.partialText.slice(p.partialText.length - PARTIAL_TEXT_CAP)\n : p.partialText;\n }\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('subagent.task_completed', (_e, payload) => {\n const p = payload as\n | { subagentId?: string; status?: string; iterations?: number; toolCalls?: number }\n | undefined;\n if (!p?.subagentId) return;\n // Only update an agent we already know \u2014 a completion for an unseen\n // agent isn't worth materialising.\n const entry = this.agents.get(p.subagentId);\n if (!entry) return;\n entry.status = p.status === 'failed' || p.status === 'timeout' ? 'error' : 'idle';\n entry.currentTool = undefined;\n entry.currentTask = undefined;\n entry.taskId = undefined;\n entry.partialText = undefined;\n if (typeof p.iterations === 'number') entry.iterations = p.iterations;\n if (typeof p.toolCalls === 'number') entry.toolCalls = p.toolCalls;\n entry.lastActivityAt = new Date().toISOString();\n this.flush();\n }),\n );\n\n this.unsubscribers.push(\n on('subagent.stopped', (_e, payload) => {\n const p = payload as { subagentId?: string } | undefined;\n if (!p?.subagentId) return;\n if (this.agents.delete(p.subagentId)) this.flush();\n }),\n );\n this.unsubscribers.push(\n on('subagent.removed', (_e, payload) => {\n const p = payload as { subagentId?: string } | undefined;\n if (!p?.subagentId) return;\n if (this.agents.delete(p.subagentId)) this.flush();\n }),\n );\n\n // Reap finished subagents so the fleet view doesn't fill with dead/idle\n // desks. The leader is synthesised in flush() (never in `this.agents`), so\n // it is never reaped \u2014 it represents the live session.\n this.sweepTimer = setInterval(() => this.sweep(), AGENT_SWEEP_INTERVAL_MS);\n if (typeof this.sweepTimer.unref === 'function') this.sweepTimer.unref();\n }\n\n stop(): void {\n for (const unsub of this.unsubscribers) {\n try {\n unsub();\n } catch {\n /* ignore */\n }\n }\n this.unsubscribers = [];\n if (this.sweepTimer) {\n clearInterval(this.sweepTimer);\n this.sweepTimer = null;\n }\n if (this.partialTimer) {\n clearTimeout(this.partialTimer);\n this.partialTimer = null;\n }\n this.leaderPendingTools.clear();\n this.subagentPendingTools.clear();\n }\n\n /**\n * Coalesce streamed-text flushes: at most one registry write per\n * {@link PARTIAL_FLUSH_THROTTLE_MS} while text streams in, so per-token\n * deltas never thrash the cross-process registry file.\n */\n private schedulePartialFlush(): void {\n if (this.partialTimer) return;\n this.partialTimer = setTimeout(() => {\n this.partialTimer = null;\n this.flush();\n }, PARTIAL_FLUSH_THROTTLE_MS);\n if (typeof this.partialTimer.unref === 'function') this.partialTimer.unref();\n }\n\n /**\n * Remove subagents that have been finished (idle/error) for longer than\n * {@link AGENT_REAP_MS}. Running / streaming / waiting_user agents are kept\n * regardless of age \u2014 only *not-working* agents are reaped.\n *\n * Also trims orphaned PendingTool entries older than {@link PENDING_TOOL_TTL_MS}\n * from both leader and subagent pending-tools Maps. Piggybacks on the same\n * 10-second interval to avoid a second timer.\n */\n private sweep(): void {\n const now = Date.now();\n let removed = false;\n for (const [id, a] of this.agents) {\n const finished =\n a.status !== 'running' && a.status !== 'streaming' && a.status !== 'waiting_user';\n const age = now - Date.parse(a.lastActivityAt);\n if (finished && Number.isFinite(age) && age > AGENT_REAP_MS) {\n this.agents.delete(id);\n removed = true;\n }\n }\n this.trimPendingTools(this.leaderPendingTools, now);\n this.trimPendingTools(this.subagentPendingTools, now);\n if (removed) this.flush();\n }\n\n /**\n * Evict pending-tool entries whose `startedAt` is older than\n * {@link PENDING_TOOL_TTL_MS}. These are tool.started / subagent.tool_started\n * events whose matching tool.executed / subagent.tool_executed never arrived\n * (provider crash, abort, process kill). Without periodic cleanup the\n * Maps retain the full tool-input objects for the process lifetime.\n */\n private trimPendingTools(map: Map<string, PendingTool>, now: number): void {\n const cutoff = now - PENDING_TOOL_TTL_MS;\n for (const [key, entry] of map) {\n if (entry.startedAt < cutoff) map.delete(key);\n }\n }\n\n private flush(): void {\n const leaderEntry: AgentEntry = {\n id: 'leader',\n name: this.leaderName,\n startedAt: this.leaderStartedAt,\n status: this.leaderStatus,\n currentTool: this.leaderCurrentTool,\n currentTask: this.leaderCurrentTask,\n iterations: this.leaderIterations,\n toolCalls: this.leaderToolCalls,\n costUsd: this.leaderCostUsd,\n tokensIn: this.leaderTokensIn,\n tokensOut: this.leaderTokensOut,\n ctxPct: this.leaderCtxPct,\n model: this.leaderModel,\n partialText: this.leaderPartialText || undefined,\n recentTools: this.leaderRecentTools,\n recentMail: this.leaderRecentMail,\n todos: this.leaderTodos,\n latestPrompt: this.leaderLatestPrompt,\n latestPromptAt: this.leaderLatestPromptAt,\n activity: this.leaderActivity,\n lastActivityAt: new Date().toISOString(),\n };\n\n const allAgents = [leaderEntry, ...this.agents.values()];\n this.lastAgents = allAgents;\n // Broadcast the fresh agent list on the local bus so in-process consumers\n // (e.g. the HQ session-telemetry bridge) can build snapshots without\n // re-reading the shared registry file. Best-effort, never throws here.\n try {\n this.events.emit('session.agents_updated', {\n sessionId: this.currentSessionId(),\n agents: allAgents,\n });\n } catch {\n /* best-effort */\n }\n // Nudge local WebUIs only AFTER the write settles. Serialize through a\n // promise chain so concurrent flush() calls never overlap: call\n // updateAgents immediately (so the registry receives the freshest snapshot\n // without a microtask delay), then chain onUpdate so concurrent flushes are\n // still serialized. Best-effort \u2014 never let a notifier failure surface.\n const write = this.registry.updateAgents(allAgents);\n const chain = write.then(() => {\n try {\n this.onUpdate?.();\n } catch {\n /* best-effort */\n }\n });\n\n if (this.flushPromise) {\n // A previous flush is still settling \u2014 chain after it so onUpdate\n // ordering is preserved. Use the rejection callback too so a failed\n // previous flush never strands future updates.\n this.flushPromise = this.flushPromise\n .then(\n () => chain,\n () => chain,\n )\n .catch(() => undefined);\n } else {\n this.flushPromise = chain.catch(() => undefined);\n }\n }\n\n private currentSessionId(): string | undefined {\n return typeof this.sessionId === 'function' ? this.sessionId() : this.sessionId;\n }\n\n private acceptsSession(payload: unknown): boolean {\n const expected = this.currentSessionId();\n if (!expected) return true;\n if (typeof payload !== 'object' || payload === null) return true;\n const actual = (payload as { sessionId?: unknown }).sessionId;\n return typeof actual !== 'string' || actual.length === 0 || actual === expected;\n }\n\n private markLeaderStarted(startedAt?: string): void {\n if (\n this.leaderStartedAt &&\n (this.leaderStatus === 'running' ||\n this.leaderStatus === 'streaming' ||\n this.leaderStatus === 'waiting_user')\n ) {\n return;\n }\n this.leaderStartedAt = startedAt ?? new Date().toISOString();\n }\n\n private captureLeaderContext(ctx: unknown): void {\n if (typeof ctx !== 'object' || ctx === null) return;\n const c = ctx as {\n model?: unknown;\n lastRequestTokens?: unknown;\n todos?: unknown;\n meta?: Record<string, unknown> | undefined;\n provider?: { capabilities?: { maxContext?: unknown } | undefined } | undefined;\n };\n if (typeof c.model === 'string' && c.model.length > 0) this.leaderModel = c.model;\n const todos = compactTodos(c.todos);\n if (todos !== undefined) this.leaderTodos = todos;\n\n const metaLimit = c.meta?.['effectiveMaxContext'];\n const providerMax = c.provider?.capabilities?.maxContext;\n const maxContext =\n typeof metaLimit === 'number' && metaLimit > 0\n ? metaLimit\n : typeof providerMax === 'number' && providerMax > 0\n ? providerMax\n : undefined;\n if (\n typeof c.lastRequestTokens === 'number' &&\n c.lastRequestTokens > 0 &&\n maxContext !== undefined\n ) {\n this.leaderCtxPct = clampPct(Math.round((c.lastRequestTokens / maxContext) * 100));\n }\n }\n}\n", "/**\n * FleetNotifier \u2014 push-on-write nudge to local WebUI servers.\n *\n * The cross-process source of truth is the file-based SessionRegistry, which\n * every WebUI server already watches (`fs.watch`, ~150ms) and polls (5s). This\n * notifier is a best-effort *latency* optimisation on top: after a process\n * writes its session/agent status to the registry, it sends a tiny\n * fire-and-forget `POST /api/fleet/ping` to every WebUI running against the\n * same project (discovered from `~/.wrongstack/webui-instances.json`). The\n * WebUI responds by re-broadcasting the (already-fresh) registry immediately \u2014\n * so a TUI/REPL agent's activity reaches the Fleet HQ map in ~milliseconds\n * instead of waiting on the watch/poll.\n *\n * Design notes:\n * - **Never the source of truth.** If no WebUI is running, the file system +\n * watch/poll still carry everything. Every failure here is swallowed.\n * - **Coalesced.** Bursts of events (tool calls, deltas) collapse into one POST\n * per ~50ms so we never hammer the WebUI per token.\n * - **Discovery cached** for a couple seconds to avoid a disk read per event.\n * - **Loopback only / self-excluded.** Targets the local instances file; a\n * `0.0.0.0`/`::` bind is dialled on `127.0.0.1`, and the caller's own pid is\n * skipped so a WebUI never pings itself.\n *\n * @module fleet-notifier\n */\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { isPidAlive } from './utils/pid.js';\n\nconst INSTANCES_FILE = 'webui-instances.json';\nconst DISCOVERY_TTL_MS = 2_500;\nconst COALESCE_MS = 50;\nconst POST_TIMEOUT_MS = 500;\n\ninterface InstanceRecordLike {\n pid: number;\n httpPort: number;\n host: string;\n projectRoot: string;\n}\n\n/** Shared probe \u2014 see utils/pid.ts. */\nconst pidAlive = isPidAlive;\n\n/** Normalise a project root for cross-process comparison (case-insensitive on Windows). */\nfunction normRoot(root: string): string {\n const resolved = path.resolve(root);\n return process.platform === 'win32' ? resolved.toLowerCase() : resolved;\n}\n\nexport interface FleetNotifierOptions {\n /** WrongStack global dir (`~/.wrongstack`) holding `webui-instances.json`. */\n baseDir: string;\n /** This process's project root \u2014 only WebUIs on the same project are pinged. */\n projectRoot: string;\n /** This process's pid, so a WebUI never pings itself. */\n selfPid?: number | undefined;\n /** Injectable POST for tests. Defaults to a timed `fetch`. */\n post?: ((url: string) => Promise<void>) | undefined;\n}\n\nexport class FleetNotifier {\n private readonly baseDir: string;\n private projectRoot: string;\n private readonly selfPid: number;\n private readonly doPost: (url: string) => Promise<void>;\n\n private cache: { at: number; urls: string[] } | null = null;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private disposed = false;\n\n constructor(opts: FleetNotifierOptions) {\n this.baseDir = opts.baseDir;\n this.projectRoot = normRoot(opts.projectRoot);\n this.selfPid = opts.selfPid ?? process.pid;\n this.doPost = opts.post ?? defaultPost;\n }\n\n /** Coalesced, best-effort nudge. Safe to call on every status change. */\n notify(): void {\n if (this.disposed || this.timer) return;\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, COALESCE_MS);\n /* v8 ignore next -- timer.unref is always a function on Node Timeout objects */\n if (typeof this.timer.unref === 'function') this.timer.unref();\n }\n\n /** Resolve same-project WebUI ping URLs (cached briefly). Exposed for tests. */\n async endpoints(): Promise<string[]> {\n const now = Date.now();\n if (this.cache && now - this.cache.at < DISCOVERY_TTL_MS) return this.cache.urls;\n const urls = await this.discover();\n this.cache = { at: now, urls };\n return urls;\n }\n\n dispose(): void {\n this.disposed = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n /** Re-scope notifications after an in-process project switch. */\n setProjectRoot(projectRoot: string): void {\n this.projectRoot = normRoot(projectRoot);\n this.cache = null;\n }\n\n private async flush(): Promise<void> {\n const urls = await this.endpoints();\n await Promise.all(urls.map((u) => this.doPost(u).catch(() => undefined)));\n }\n\n private async discover(): Promise<string[]> {\n try {\n const raw = await fs.readFile(path.join(this.baseDir, INSTANCES_FILE), 'utf8');\n const data = JSON.parse(raw) as { instances?: InstanceRecordLike[] };\n const list = Array.isArray(data?.instances) ? data.instances : [];\n return list\n .filter((i) => i && typeof i.httpPort === 'number')\n .filter((i) => i.pid !== this.selfPid)\n .filter((i) => normRoot(i.projectRoot) === this.projectRoot)\n .filter((i) => pidAlive(i.pid))\n .map((i) => {\n const host = i.host === '0.0.0.0' || i.host === '::' || !i.host ? '127.0.0.1' : i.host;\n return `http://${host}:${i.httpPort}/api/fleet/ping`;\n });\n } catch {\n // Missing/corrupt instances file \u2192 no WebUIs to notify.\n return [];\n }\n }\n}\n\nasync function defaultPost(url: string): Promise<void> {\n await fetch(url, {\n method: 'POST',\n signal: AbortSignal.timeout(POST_TIMEOUT_MS),\n });\n}\n", "import { createReadStream } from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { createInterface } from 'node:readline';\nimport { ERROR_CODES, SessionError } from '../types/errors.js';\nimport type { FileSnapshot, SessionEvent } from '../types/session.js';\nimport type {\n CheckpointInfo,\n RewindResult,\n RewindResultExtended,\n SessionRewinder,\n} from '../types/session-rewinder.js';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { sessionScopedPath } from '../utils/session-scoped-path.js';\n\nexport interface SessionRewinderOptions {\n sessionsDir: string;\n /** The project root directory; used to validate rewind targets stay inside it. */\n projectRoot: string;\n}\n\n/**\n * Rewind engine that reads session JSONL files and reverts file system\n * changes to any previous checkpoint.\n */\nexport class DefaultSessionRewinder implements SessionRewinder {\n constructor(\n private readonly sessionsDir: string,\n private readonly projectRoot: string,\n ) {}\n\n private sessionFile(sessionId: string): string {\n return sessionScopedPath(this.sessionsDir, sessionId, '.jsonl');\n }\n\n private async *readEvents(file: string): AsyncGenerator<SessionEvent> {\n const stream = createReadStream(file, { encoding: 'utf8' });\n const lines = createInterface({ input: stream, crlfDelay: Infinity });\n try {\n for await (const line of lines) {\n if (!line.trim()) continue;\n try {\n const parsed: unknown = JSON.parse(line);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n typeof (parsed as { type?: unknown }).type === 'string' &&\n typeof (parsed as { ts?: unknown }).ts === 'string'\n ) {\n yield parsed as SessionEvent;\n }\n } catch {\n // Skip malformed lines without materializing the complete file.\n }\n }\n } finally {\n lines.close();\n stream.close();\n }\n }\n\n async listCheckpoints(sessionId: string): Promise<CheckpointInfo[]> {\n const file = this.sessionFile(sessionId);\n\n // Build a map of promptIndex -> file snapshot count\n const fileCountMap = new Map<number, number>();\n const checkpoints: Array<Omit<CheckpointInfo, 'fileCount'>> = [];\n for await (const event of this.readEvents(file)) {\n if (event.type === 'file_snapshot') {\n const e = event as { promptIndex: number; files: FileSnapshot[] };\n fileCountMap.set(e.promptIndex, (fileCountMap.get(e.promptIndex) ?? 0) + e.files.length);\n }\n if (event.type === 'checkpoint') {\n const e = event as { promptIndex: number; promptPreview: string; ts: string };\n checkpoints.push({\n promptIndex: e.promptIndex,\n promptPreview: e.promptPreview,\n ts: e.ts,\n });\n }\n }\n\n return checkpoints.map((checkpoint) => ({\n ...checkpoint,\n fileCount: fileCountMap.get(checkpoint.promptIndex) ?? 0,\n }));\n }\n\n async rewindToCheckpoint(\n sessionId: string,\n checkpointIndex: number,\n ): Promise<RewindResultExtended> {\n const file = this.sessionFile(sessionId);\n let foundTarget = false;\n let removedEvents = 0;\n const snapshotsToRevert: Array<{ promptIndex: number; files: FileSnapshot[] }> = [];\n for await (const event of this.readEvents(file)) {\n if (event.type === 'checkpoint') {\n const checkpointEvent = event as { promptIndex: number };\n if (checkpointEvent.promptIndex === checkpointIndex) {\n foundTarget = true;\n continue;\n }\n }\n if (!foundTarget) continue;\n removedEvents++;\n if (event.type === 'file_snapshot' && event.promptIndex >= checkpointIndex) {\n snapshotsToRevert.push({ promptIndex: event.promptIndex, files: event.files });\n }\n }\n\n if (!foundTarget) {\n throw new SessionError({\n message: `Checkpoint ${checkpointIndex} not found`,\n code: ERROR_CODES.SESSION_NOT_FOUND,\n context: { checkpointIndex },\n });\n }\n\n // Collect EVERY snapshot from the target checkpoint onwards. The writer\n // emits checkpoint(N) first and then labels that prompt's snapshots with\n // promptIndex N (file-session-writer.ts:652-659, :194-199), so later\n // prompts contribute further checkpoint/snapshot pairs. Stopping at the\n // next checkpoint would revert only the target prompt's files while the\n // caller truncates the journal all the way back, leaving the working tree\n // and the conversation in different eras.\n const result = await revertSnapshots(snapshotsToRevert, this.projectRoot);\n return { ...result, toPromptIndex: checkpointIndex, removedEvents };\n }\n\n async rewindLastN(sessionId: string, n: number): Promise<RewindResultExtended> {\n const file = this.sessionFile(sessionId);\n\n const checkpoints: Array<{ promptIndex: number; ts: string }> = [];\n for await (const event of this.readEvents(file)) {\n if (event.type === 'checkpoint') {\n checkpoints.push({ promptIndex: event.promptIndex, ts: event.ts });\n }\n }\n\n if (checkpoints.length === 0 || n <= 0) {\n return { revertedFiles: [], errors: [], toPromptIndex: 0, removedEvents: 0 };\n }\n\n // Undoing the last N prompts means rewinding TO the Nth-newest checkpoint,\n // because rewinding to checkpoint K reverts prompt K and everything after\n // it. So n=1 targets the newest checkpoint \u2014 index n-1, not n. Falling back\n // to 0 when n exceeds the checkpoint count rewinds the whole session.\n checkpoints.sort((a, b) => b.promptIndex - a.promptIndex);\n const targetIndex = checkpoints[n - 1]?.promptIndex ?? 0;\n\n const snapshotsToRevert: Array<{ promptIndex: number; files: FileSnapshot[] }> = [];\n let shouldRevert = false;\n\n for await (const event of this.readEvents(file)) {\n if (event.type === 'checkpoint' && event.promptIndex === targetIndex) {\n shouldRevert = true;\n continue;\n }\n if (shouldRevert && event.type === 'file_snapshot') {\n snapshotsToRevert.push({ promptIndex: event.promptIndex, files: event.files });\n }\n }\n\n const result = await revertSnapshots(snapshotsToRevert, this.projectRoot);\n return { ...result, toPromptIndex: targetIndex, removedEvents: snapshotsToRevert.length };\n }\n\n async rewindToStart(sessionId: string): Promise<RewindResultExtended> {\n const file = this.sessionFile(sessionId);\n\n const allSnapshots: Array<{ promptIndex: number; files: FileSnapshot[] }> = [];\n for await (const event of this.readEvents(file)) {\n if (event.type === 'file_snapshot') {\n allSnapshots.push({ promptIndex: event.promptIndex, files: event.files });\n }\n }\n\n if (allSnapshots.length === 0) {\n return { revertedFiles: [], errors: [], toPromptIndex: 0, removedEvents: 0 };\n }\n\n const result = await revertSnapshots(allSnapshots, this.projectRoot);\n return { ...result, toPromptIndex: 0, removedEvents: allSnapshots.length };\n }\n}\n\nasync function revertSnapshots(\n snapshots: Array<{ promptIndex: number; files: FileSnapshot[] }>,\n projectRoot: string,\n): Promise<RewindResult> {\n const revertedFiles: string[] = [];\n const errors: string[] = [];\n\n // Undo is the inverse of execution order. Reverse both event order and the\n // per-event file list so repeated edits of the same path restore the oldest\n // `before` content rather than stopping at an intermediate version.\n for (const snapshot of [...snapshots].reverse()) {\n for (const file of [...snapshot.files].reverse()) {\n try {\n // Guard: ensure the target path resolves inside the project root.\n // Without this, a maliciously recorded path (e.g., via path traversal\n // in a tool call that wasn't caught) could cause rewind to write\n // to arbitrary locations.\n const absPath = path.resolve(file.path);\n const root = path.resolve(projectRoot);\n const rel = path.relative(root, absPath);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n errors.push(`${file.path}: path resolves outside project root \u2014 skipping`);\n continue;\n }\n\n if (file.action === 'deleted') {\n // File was deleted \u2014 restore it from before\n if (file.before !== null) {\n // atomicWrite: torn restore would leave the user with a frankenstein file.\n await atomicWrite(file.path, file.before, { mode: 0o644 });\n revertedFiles.push(file.path);\n }\n } else if (file.action === 'created') {\n // File was created \u2014 delete it\n await fsp.unlink(file.path);\n revertedFiles.push(file.path);\n } else if (file.action === 'modified') {\n // File was modified \u2014 restore before content\n if (file.before !== null) {\n // atomicWrite: torn restore would leave the user with a frankenstein file.\n await atomicWrite(file.path, file.before, { mode: 0o644 });\n revertedFiles.push(file.path);\n }\n }\n } catch (err) {\n errors.push(`${file.path}: ${toErrorMessage(err)}`);\n }\n }\n }\n\n return { revertedFiles, errors };\n}\n", "import type { Message } from '../types/messages.js';\nimport type { SessionWriter } from '../types/session.js';\nimport { DefaultSessionStore } from './session-store.js';\n\n/**\n * The slice of ConversationState this module needs. Kept structural so callers\n * can pass `ctx.state` without this module depending on the core layer.\n */\nexport interface RewindableConversation {\n replaceMessages(messages: Message[]): void;\n}\n\nexport interface ApplyRewindOptions {\n /** The live writer for the session being rewound. */\n session: SessionWriter;\n /** The live conversation to cut back. */\n state: RewindableConversation;\n /** Directory holding the session JSONL files. */\n sessionsDir: string;\n /** Checkpoint promptIndex to rewind to. */\n promptIndex: number;\n /**\n * Files the SessionRewinder just reverted, recorded on the `rewound` event.\n * Only this caller knows them: the file_snapshot events that prove what\n * changed are truncated away by this very call.\n */\n revertedFiles?: readonly string[] | undefined;\n}\n\nexport interface ApplyRewindResult {\n /** Events dropped from the JSONL by the truncation. */\n removedEvents: number;\n /** Message count the live conversation was cut back to. */\n messageCount: number;\n}\n\n/**\n * Truncate the session JSONL to a checkpoint AND cut the live conversation\n * back to match.\n *\n * Truncating the log alone is not a rewind: the model keeps the rewound turns\n * in its working set, so the next prompt is answered against a history the log\n * no longer contains. Worse, the conversation journal records `message_updated`\n * by absolute index, so once the log is shorter than the live array those\n * updates fail the `index < messages.length` guard on reload and are dropped as\n * damage \u2014 the file diverges from the session permanently.\n *\n * The truncated log is the source of truth for what survives, so this reloads\n * it and replays the result into the live state rather than trying to compute\n * the cut point twice. `replaceMessages` emits `messages_replaced`, which\n * re-anchors the journal at the new length and makes the cut explicit to any\n * later reader.\n *\n * The caller is responsible for reverting files (see `SessionRewinder`); this\n * only reconciles the log with the live conversation.\n */\nexport async function applyRewindToConversation(\n opts: ApplyRewindOptions,\n): Promise<ApplyRewindResult> {\n const { session, state, sessionsDir, promptIndex, revertedFiles } = opts;\n const removedEvents = await session.truncateToCheckpoint(promptIndex, revertedFiles ?? []);\n\n // A fresh store avoids serving a pre-truncation cache entry; load() keys its\n // cache on mtime+size, both of which the rewrite changes, but a caller's\n // long-lived store may also hold a reference to the old data object.\n const store = new DefaultSessionStore({ dir: sessionsDir });\n const data = await store.load(session.id);\n state.replaceMessages(data.messages);\n\n return { removedEvents, messageCount: data.messages.length };\n}\n", "import * as fsp from 'node:fs/promises';\nimport type { TodoItem } from '../core/context.js';\nimport type { ConversationState } from '../core/conversation-state.js';\nimport type { EventBus } from '../kernel/events.js';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\n\n/**\n * On-disk checkpoint for `ctx.todos`. Written atomically every time the\n * todo list changes, read once on session resume. This is the missing\n * piece that lets `wstack resume <id>` rehydrate where the previous run\n * stopped instead of starting with an empty board.\n *\n * Schema is intentionally small \u2014 a single JSON object so a future\n * format bump is easy. The `version` field is the only contract; the\n * shape under `todos` mirrors `TodoItem` so reading is a straight assign.\n */\nexport interface TodosCheckpointFile {\n version: 1;\n sessionId: string;\n updatedAt: string;\n todos: TodoItem[];\n}\n\nexport type TodosCheckpointDetach = () => Promise<void>;\n\n/** Read a checkpoint from disk. Returns null when the file doesn't\n * exist or is corrupt \u2014 callers treat both cases as \"no prior state\".\n */\nexport async function loadTodosCheckpoint(\n filePath: string,\n events?: EventBus,\n traceId?: string,\n sessionId?: string,\n): Promise<TodoItem[] | null> {\n const t0 = Date.now();\n let raw: string;\n try {\n raw = await fsp.readFile(filePath, 'utf8');\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: sessionId ?? '~boot~',\n store: 'todos',\n filePath,\n operation: 'load',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: true,\n ...(traceId !== undefined && { traceId }),\n });\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as TodosCheckpointFile;\n if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) {\n events?.emit('storage.read', {\n sessionId: sessionId ?? '~boot~',\n store: 'todos',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'invalid_schema',\n ...(traceId !== undefined && { traceId }),\n });\n return null;\n }\n events?.emit('storage.read', {\n sessionId: sessionId ?? parsed.sessionId ?? '~boot~',\n store: 'todos',\n filePath,\n operation: 'load',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(traceId !== undefined && { traceId }),\n });\n return parsed.todos.filter(\n (t): t is TodoItem =>\n !!t &&\n typeof t.id === 'string' &&\n typeof t.content === 'string' &&\n typeof t.status === 'string' &&\n (t.activeForm === undefined || typeof t.activeForm === 'string'),\n );\n } catch {\n events?.emit('storage.read', {\n sessionId: sessionId ?? '~boot~',\n store: 'todos',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse_failed',\n ...(traceId !== undefined && { traceId }),\n });\n return null;\n }\n}\n\n/** Write the checkpoint atomically. Best-effort: a write failure is\n * logged but does not throw \u2014 losing one checkpoint shouldn't bring\n * down the agent run.\n */\nexport async function saveTodosCheckpoint(\n filePath: string,\n sessionId: string,\n todos: readonly TodoItem[],\n events?: EventBus,\n traceId?: string,\n warn?: (msg: string) => void,\n): Promise<void> {\n const t0 = Date.now();\n const payload: TodosCheckpointFile = {\n version: 1,\n sessionId,\n updatedAt: new Date().toISOString(),\n todos: [...todos],\n };\n try {\n await atomicWrite(filePath, JSON.stringify(payload, null, 2), { mode: 0o600 });\n events?.emit('storage.write', {\n sessionId,\n store: 'todos',\n filePath,\n operation: 'save',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(traceId !== undefined && { traceId }),\n });\n } catch (err) {\n events?.emit('storage.error', {\n sessionId,\n store: 'todos',\n filePath,\n operation: 'save',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: false,\n ...(traceId !== undefined && { traceId }),\n });\n (\n warn ??\n ((m) =>\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'todos_checkpoint.save_failed',\n message: m,\n timestamp: new Date().toISOString(),\n }),\n ))\n )(toErrorMessage(err));\n }\n}\n\n/**\n * Subscribe a `ConversationState` so every `todos_replaced` mutation\n * triggers an atomic write to disk. Returns the unsubscribe function.\n *\n * Writes are debounced by 150ms so a flurry of edits (e.g. the LLM\n * marking three items done in the same tool call) coalesces into one\n * disk hit.\n */\nexport function attachTodosCheckpoint(\n state: ConversationState,\n filePath: string,\n sessionId: string,\n events?: EventBus,\n traceId?: string,\n warn?: (msg: string) => void,\n): TodosCheckpointDetach {\n let timer: NodeJS.Timeout | null = null;\n let pending: readonly TodoItem[] | null = null;\n let queuedWrite: readonly TodoItem[] | null = null;\n let writeInFlight: Promise<void> | null = null;\n\n const enqueueWrite = (todos: readonly TodoItem[]) => {\n queuedWrite = todos;\n if (writeInFlight) return writeInFlight;\n const drain = (async () => {\n while (queuedWrite) {\n const latest = queuedWrite;\n queuedWrite = null;\n await saveTodosCheckpoint(filePath, sessionId, latest, events, traceId).catch((err) => {\n const msg = toErrorMessage(err);\n (\n warn ??\n ((m) =>\n console.error(\n JSON.stringify({\n level: 'error',\n event: 'todos_checkpoint.write_failed',\n sessionId,\n message: m,\n timestamp: new Date().toISOString(),\n }),\n ))\n )(msg);\n });\n }\n })().finally(() => {\n if (writeInFlight === drain) writeInFlight = null;\n if (queuedWrite) enqueueWrite(queuedWrite);\n });\n writeInFlight = drain;\n return drain;\n };\n\n const flush = () => {\n timer = null;\n if (pending) {\n const todos = pending;\n pending = null;\n return enqueueWrite(todos);\n }\n /* v8 ignore next -- defensive: flush is only invoked when a change is pending */\n return writeInFlight ?? Promise.resolve();\n };\n\n const unsubscribe = state.onChange((change) => {\n if (change.kind !== 'todos_replaced') return;\n pending = change.todos;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n void flush();\n }, 150);\n });\n let detached = false;\n let detachPromise: Promise<void> | null = null;\n return () => {\n if (detachPromise) return detachPromise;\n if (detached) return Promise.resolve();\n detached = true;\n unsubscribe();\n detachPromise = (async () => {\n if (timer) {\n clearTimeout(timer);\n // Flush any pending write before detach so callers can safely\n // unsubscribe at shutdown without losing the last update.\n await flush();\n } else {\n await writeInFlight;\n }\n })();\n return detachPromise;\n };\n}\n", "import * as fsp from 'node:fs/promises';\nimport type { EventBus } from '../kernel/events.js';\nimport type { CompletedWorkEvidence } from '../types/context-evidence.js';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\n\nexport interface CompletedWorkCheckpointFile {\n version: 1;\n sessionId: string;\n updatedAt: string;\n completedWork: CompletedWorkEvidence[];\n}\n\nexport async function loadCompletedWorkCheckpoint(\n filePath: string,\n events?: EventBus,\n traceId?: string,\n): Promise<CompletedWorkEvidence[] | null> {\n const t0 = Date.now();\n let raw: string;\n try {\n raw = await fsp.readFile(filePath, 'utf8');\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: traceId ?? '~boot~',\n store: 'completed-work',\n filePath,\n operation: 'load',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: true,\n });\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as CompletedWorkCheckpointFile;\n if (parsed?.version !== 1 || !Array.isArray(parsed.completedWork)) {\n events?.emit('storage.read', {\n sessionId: traceId ?? '~boot~',\n store: 'completed-work',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'invalid_schema',\n ...(traceId !== undefined && { traceId }),\n });\n return null;\n }\n events?.emit('storage.read', {\n sessionId: traceId ?? '~boot~',\n store: 'completed-work',\n filePath,\n operation: 'load',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(traceId !== undefined && { traceId }),\n });\n return parsed.completedWork.filter(isCompletedWorkEvidence);\n } catch {\n events?.emit('storage.read', {\n sessionId: traceId ?? '~boot~',\n store: 'completed-work',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse_failed',\n ...(traceId !== undefined && { traceId }),\n });\n return null;\n }\n}\n\nexport async function saveCompletedWorkCheckpoint(\n filePath: string,\n sessionId: string,\n completedWork: readonly CompletedWorkEvidence[],\n events?: EventBus,\n traceId?: string,\n): Promise<void> {\n const t0 = Date.now();\n const payload: CompletedWorkCheckpointFile = {\n version: 1,\n sessionId,\n updatedAt: new Date().toISOString(),\n completedWork: [...completedWork],\n };\n try {\n await atomicWrite(filePath, JSON.stringify(payload, null, 2), { mode: 0o600 });\n events?.emit('storage.write', {\n sessionId: traceId ?? sessionId,\n store: 'completed-work',\n filePath,\n operation: 'save',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(traceId !== undefined && { traceId }),\n });\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: traceId ?? sessionId,\n store: 'completed-work',\n filePath,\n operation: 'save',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: false,\n });\n console.warn(JSON.stringify({\n level: 'warn',\n event: 'completed_work_checkpoint.save_failed',\n message: toErrorMessage(err),\n timestamp: new Date().toISOString(),\n }));\n }\n}\n\nfunction isCompletedWorkEvidence(value: unknown): value is CompletedWorkEvidence {\n if (!value || typeof value !== 'object') return false;\n const item = value as Partial<CompletedWorkEvidence>;\n return (\n typeof item.key === 'string' &&\n typeof item.source === 'string' &&\n ['todo', 'plan', 'task', 'verification', 'manual'].includes(item.source) &&\n typeof item.summary === 'string' &&\n typeof item.completedAt === 'number' &&\n (item.evidence === undefined || typeof item.evidence === 'string')\n );\n}\n", "import * as fsp from 'node:fs/promises';\nimport { randomUUID } from 'node:crypto';\nimport type { EventBus } from '../kernel/events.js';\nimport type { ConversationState } from '../core/conversation-state.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { SessionError } from '../types/errors.js';\n\n/**\n * Plan items are the strategic counterpart to todos. Where `ctx.todos`\n * is the moment-to-moment task board the LLM mutates per-turn, a plan\n * captures the higher-level approach \u2014 the steps the user (or LLM)\n * laid out before any work began.\n *\n * Plans persist by default (per session) so a resumed session can show\n * \"you were on step 3 of 5\". Todos are derived/transient. Both can\n * coexist: think roadmap (plan) vs. sprint board (todos).\n */\nexport interface PlanItem {\n id: string;\n title: string;\n /** Optional longer-form context or rationale. */\n details?: string | undefined;\n status: 'open' | 'in_progress' | 'done';\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface PlanFile {\n version: 1;\n sessionId: string;\n title?: string | undefined;\n updatedAt: string;\n items: PlanItem[];\n}\n\nexport async function loadPlan(filePath: string, events?: EventBus): Promise<PlanFile | null> {\n const t0 = Date.now();\n let raw: string;\n try {\n raw = await fsp.readFile(filePath, 'utf8');\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: '~boot~',\n store: 'plan',\n filePath,\n operation: 'load',\n error: toErrorMessage(err),\n recoverable: true,\n });\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as PlanFile;\n if (parsed?.version !== 1 || !Array.isArray(parsed.items)) {\n events?.emit('storage.read', {\n sessionId: '~boot~',\n store: 'plan',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'invalid_schema',\n });\n return null;\n }\n events?.emit('storage.read', {\n sessionId: '~boot~',\n store: 'plan',\n filePath,\n operation: 'load',\n outcome: 'success',\n durationMs: Date.now() - t0,\n });\n return parsed;\n } catch {\n events?.emit('storage.read', {\n sessionId: '~boot~',\n store: 'plan',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse_failed',\n });\n return null;\n }\n}\n\n/**\n * Persist a plan. Returns `true` on success, `false` if the write failed\n * (still emits `storage.error` + warns \u2014 it does NOT throw, so callers that\n * treat a lost plan-save as non-fatal keep working). `mutatePlan` inspects the\n * result and throws so the plan TOOL can report `ok:false` instead of falsely\n * claiming the plan was persisted.\n */\nexport async function savePlan(\n filePath: string,\n plan: PlanFile,\n events?: EventBus,\n warn?: (msg: string) => void,\n): Promise<boolean> {\n const t0 = Date.now();\n try {\n await atomicWrite(filePath, JSON.stringify(plan, null, 2), { mode: 0o600 });\n events?.emit('storage.write', {\n sessionId: '~boot~',\n store: 'plan',\n filePath,\n operation: 'save',\n outcome: 'success',\n durationMs: Date.now() - t0,\n });\n return true;\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: '~boot~',\n store: 'plan',\n filePath,\n operation: 'save',\n error: toErrorMessage(err),\n recoverable: false,\n });\n (\n warn ??\n ((m) =>\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'plan_store.save_failed',\n message: m,\n timestamp: new Date().toISOString(),\n }),\n ))\n )(toErrorMessage(err));\n return false;\n }\n}\n\n/** Create a new PlanFile when none exists on disk. */\nexport function emptyPlan(sessionId: string, title?: string): PlanFile {\n return {\n version: 1,\n sessionId,\n title,\n updatedAt: new Date().toISOString(),\n items: [],\n };\n}\n\nexport function addPlanItem(\n plan: PlanFile,\n title: string,\n details?: string | undefined,\n): { plan: PlanFile; item: PlanItem } {\n const now = new Date().toISOString();\n const item: PlanItem = {\n id: `plan_${Date.now()}_${randomUUID().slice(0, 6)}`,\n title,\n details,\n status: 'open',\n createdAt: now,\n updatedAt: now,\n };\n return {\n plan: { ...plan, items: [...plan.items, item], updatedAt: now },\n item,\n };\n}\n\nexport function removePlanItem(plan: PlanFile, idOrIndex: string): PlanFile {\n const idx = matchIndex(plan, idOrIndex);\n if (idx === -1) return plan;\n return {\n ...plan,\n items: plan.items.filter((_, i) => i !== idx),\n updatedAt: new Date().toISOString(),\n };\n}\n\nexport function setPlanItemStatus(\n plan: PlanFile,\n idOrIndex: string,\n status: PlanItem['status'],\n): PlanFile {\n const idx = matchIndex(plan, idOrIndex);\n if (idx === -1) return plan;\n const now = new Date().toISOString();\n const items = plan.items.map((it, i) => (i === idx ? { ...it, status, updatedAt: now } : it));\n return { ...plan, items, updatedAt: now };\n}\n\nexport function clearPlan(plan: PlanFile): PlanFile {\n return { ...plan, items: [], updatedAt: new Date().toISOString() };\n}\n\n/** Render the plan as a short markdown-ish string suitable for slash output. */\nexport function formatPlan(plan: PlanFile): string {\n if (plan.items.length === 0) return 'Plan is empty.';\n const lines: string[] = [];\n if (plan.title) lines.push(`# ${plan.title}`);\n plan.items.forEach((it, i) => {\n const mark = it.status === 'done' ? '[x]' : it.status === 'in_progress' ? '[~]' : '[ ]';\n lines.push(`${i + 1}. ${mark} ${it.title}`);\n if (it.details) {\n for (const line of it.details.split('\\n')) lines.push(` ${line}`);\n }\n });\n return lines.join('\\n');\n}\n\nfunction matchIndex(plan: PlanFile, idOrIndex: string): number {\n const asNum = Number.parseInt(idOrIndex, 10);\n if (!Number.isNaN(asNum) && asNum >= 1 && asNum <= plan.items.length) return asNum - 1;\n const byId = plan.items.findIndex((it) => it.id === idOrIndex);\n if (byId !== -1) return byId;\n const lower = idOrIndex.toLowerCase();\n return plan.items.findIndex((it) => it.title.toLowerCase().includes(lower));\n}\n\n/**\n * Promote a plan item to a set of todo items.\n * The plan item is marked 'in_progress' (if not already done) and its\n * title + details become the first todo; additional subtasks are appended.\n * Returns the derived todo list so the caller can pass it to `todoTool`\n * or `ctx.state.replaceTodos()`.\n */\nexport function deriveTodosFromPlanItem(\n plan: PlanFile,\n idOrIndex: string,\n subtasks?: string[] | undefined,\n): {\n plan: PlanFile;\n todos: Array<{\n id: string;\n content: string;\n status: 'pending' | 'in_progress' | 'completed';\n activeForm?: string | undefined;\n promotedFromPlan?: string | undefined;\n }>;\n} | null {\n const idx = matchIndex(plan, idOrIndex);\n if (idx === -1) return null;\n\n const item = plan.items[idx];\n /* v8 ignore next -- defensive: matchIndex returns a valid in-range index or -1 (handled above) */\n if (!item) return null;\n\n // Mark the plan item in_progress if it wasn't already done\n let updatedPlan = plan;\n if (item.status !== 'done') {\n updatedPlan = setPlanItemStatus(plan, idOrIndex, 'in_progress');\n }\n\n const todos: Array<{\n id: string;\n content: string;\n status: 'pending' | 'in_progress' | 'completed';\n activeForm?: string | undefined;\n promotedFromPlan?: string | undefined;\n }> = [];\n\n // First todo from the plan item itself\n todos.push({\n id: `todo_${Date.now()}_plan`,\n content: item.title,\n status: 'in_progress',\n activeForm: item.title,\n promotedFromPlan: item.id,\n });\n\n // Optional subtasks\n if (subtasks && subtasks.length > 0) {\n for (const st of subtasks) {\n todos.push({\n id: `todo_${Date.now()}_${randomUUID().slice(0, 6)}`,\n content: st,\n status: 'pending',\n promotedFromPlan: item.id,\n });\n }\n }\n\n return { plan: updatedPlan, todos };\n}\n\n/**\n * Load, modify, and save the plan file under a file-level lock.\n * Prevents races from parallel tool invocations (e.g. batch_tool_use).\n */\nexport async function mutatePlan(\n filePath: string,\n sessionId: string,\n fn: (plan: PlanFile) => PlanFile | Promise<PlanFile>,\n): Promise<PlanFile> {\n return withFileLock(filePath, async () => {\n const plan = (await loadPlan(filePath)) ?? emptyPlan(sessionId);\n const updated = await fn(plan);\n const persisted = await savePlan(filePath, updated);\n if (!persisted) {\n throw new SessionError({\n message: `Failed to persist plan to ${filePath} \u2014 the change was NOT saved.`,\n code: 'SESSION_WRITE_FAILED',\n sessionId,\n context: { filePath, operation: 'mutatePlan' },\n });\n }\n return updated;\n });\n}\n\n/**\n * Optional: attach a state-listener so meta operations (storing a plan\n * id on ctx.meta) trigger a save. Currently a stub \u2014 plans don't live\n * on Context, but this keeps the API surface symmetric with the todos\n * checkpoint so future refactors can flip plans into Context if needed.\n */\nexport function attachPlanCheckpoint(\n _state: ConversationState,\n _filePath: string,\n _sessionId: string,\n): () => void {\n return () => undefined;\n}\n", "/**\n * Plan templates \u2014 pre-defined plan skeletons for common workflows.\n *\n * Templates are stored in-memory (no disk I/O). Users instantiate them\n * via `/plan template use <name>` or `planTool(action: 'template_use')`.\n * Each template is a function that returns an array of item titles, so\n * dynamic content (dates, project names) can be injected later.\n */\n\nexport interface PlanTemplate {\n name: string;\n description: string;\n category: 'development' | 'release' | 'maintenance' | 'infrastructure';\n items: Array<{\n title: string;\n details?: string | undefined;\n }>;\n}\n\nconst templates: Record<string, PlanTemplate> = {\n 'new-feature': {\n name: 'new-feature',\n description: 'Standard workflow for adding a new feature',\n category: 'development',\n items: [\n { title: 'Write specification / design doc', details: 'Define scope, acceptance criteria, edge cases' },\n { title: 'Set up feature branch', details: 'git checkout -b feature/...' },\n { title: 'Implement core logic', details: 'TDD preferred \u2014 write tests first' },\n { title: 'Add unit tests', details: '>= 80% coverage for new code' },\n { title: 'Add integration tests', details: 'End-to-end happy path + error paths' },\n { title: 'Update documentation', details: 'README, API docs, changelog' },\n { title: 'Code review', details: 'Self-review before requesting review' },\n { title: 'Merge and deploy', details: 'CI green, tag release' },\n ],\n },\n 'bug-fix': {\n name: 'bug-fix',\n description: 'Systematic approach to fixing bugs',\n category: 'maintenance',\n items: [\n { title: 'Reproduce the bug', details: 'Minimal reproduction case' },\n { title: 'Root cause analysis', details: 'Trace through logs, debugger' },\n { title: 'Write failing test', details: 'Test must fail before fix' },\n { title: 'Implement fix', details: 'Smallest possible change' },\n { title: 'Verify fix', details: 'Test passes, reproduction no longer fails' },\n { title: 'Regression test', details: 'Ensure no related tests broken' },\n { title: 'Document in changelog', details: 'Brief description + issue link' },\n ],\n },\n 'refactor': {\n name: 'refactor',\n description: 'Safe refactoring workflow',\n category: 'maintenance',\n items: [\n { title: 'Identify refactoring target', details: 'Code smell, performance bottleneck, or tech debt' },\n { title: 'Ensure test coverage', details: 'Existing tests must pass before and after' },\n { title: 'Write characterization tests', details: 'Capture current behavior if tests weak' },\n { title: 'Apply refactoring', details: 'Small steps, frequent commits' },\n { title: 'Run full test suite', details: 'All tests must pass' },\n { title: 'Performance check', details: 'Ensure no regression' },\n { title: 'Code review', details: 'Explain the why, not just the what' },\n ],\n },\n 'release': {\n name: 'release',\n description: 'Preparing a new release',\n category: 'release',\n items: [\n { title: 'Version bump', details: 'package.json, lockfiles, tags' },\n { title: 'Update changelog', details: 'All changes since last release' },\n { title: 'Run full test suite', details: 'Unit + integration + e2e' },\n { title: 'Build artifacts', details: 'Docker images, bundles, binaries' },\n { title: 'Staging smoke tests', details: 'Deploy to staging, verify' },\n { title: 'Production deploy', details: 'Blue-green or canary' },\n { title: 'Post-deploy verification', details: 'Health checks, error rates' },\n { title: 'Announce release', details: 'Slack, email, GitHub release notes' },\n ],\n },\n 'security-audit': {\n name: 'security-audit',\n description: 'Security review and hardening',\n category: 'infrastructure',\n items: [\n { title: 'Dependency audit', details: 'npm audit, Snyk, Dependabot alerts' },\n { title: 'Secret scan', details: 'git-secrets, truffleHog, manual review' },\n { title: 'Access control review', details: 'IAM, roles, least privilege' },\n { title: 'Input validation audit', details: 'SQL injection, XSS, path traversal' },\n { title: 'Authentication review', details: 'Session management, MFA, password policy' },\n { title: 'Logging and monitoring', details: 'PII in logs, audit trails' },\n { title: 'Incident response plan', details: 'Runbooks, contacts, escalation' },\n ],\n },\n 'onboarding': {\n name: 'onboarding',\n description: 'New developer onboarding checklist',\n category: 'infrastructure',\n items: [\n { title: 'Repository access', details: 'GitHub/GitLab permissions' },\n { title: 'Local environment setup', details: 'Docker, dependencies, env files' },\n { title: 'Run tests locally', details: 'Verify green suite' },\n { title: 'Read architecture docs', details: 'ADR, README, onboarding guide' },\n { title: 'First commit', details: 'Docs fix or small improvement' },\n { title: 'Pair programming session', details: 'Walk through codebase with buddy' },\n { title: 'Deploy to staging', details: 'Verify CI/CD access' },\n ],\n },\n};\n\nexport function listPlanTemplates(): PlanTemplate[] {\n return Object.values(templates);\n}\n\nexport function getPlanTemplate(name: string): PlanTemplate | undefined {\n return templates[name];\n}\n\nexport function formatPlanTemplates(): string {\n const cats = new Map<PlanTemplate['category'], PlanTemplate[]>();\n for (const t of Object.values(templates)) {\n const arr = cats.get(t.category) ?? [];\n arr.push(t);\n cats.set(t.category, arr);\n }\n\n const lines: string[] = ['Available plan templates:'];\n for (const [cat, items] of cats) {\n lines.push(`\\n${cat}:`);\n for (const t of items) {\n lines.push(` ${t.name.padEnd(18)} \u2014 ${t.description}`);\n }\n }\n return lines.join('\\n');\n}\n", "import * as fsp from 'node:fs/promises';\nimport type { EventBus } from '../kernel/events.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport type { TaskItem } from '../utils/task-format.js';\nimport { SessionError } from '../types/errors.js';\n\n// ---------------------------------------------------------------------------\n// Task file persistence \u2014 one JSON file per session, resolved via\n// `sessionScopedPath(projectSessions, sessionId, '.tasks.json')`.\n//\n// Low-level load/save are exported for read-only consumers. Mutating callers\n// should use `mutateTasks` which wraps the entire read-modify-write cycle\n// under a file-level lock, preventing races from parallel tool invocations.\n// ---------------------------------------------------------------------------\n\nexport interface TaskFile {\n version: 1;\n sessionId: string;\n updatedAt: string;\n tasks: TaskItem[];\n}\n\nexport function emptyTaskFile(sessionId: string): TaskFile {\n return {\n version: 1,\n sessionId,\n updatedAt: new Date().toISOString(),\n tasks: [],\n };\n}\n\n/** Read the task file. Returns null when the file doesn't exist. */\nexport async function loadTasks(\n filePath: string,\n events?: EventBus,\n traceId?: string,\n): Promise<TaskFile | null> {\n const t0 = Date.now();\n let raw: string;\n try {\n raw = await fsp.readFile(filePath, 'utf8');\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: traceId ?? '~boot~',\n store: 'tasks',\n filePath,\n operation: 'load',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: true,\n });\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as TaskFile;\n if (parsed?.version !== 1 || !Array.isArray(parsed.tasks)) {\n events?.emit('storage.read', {\n sessionId: traceId ?? '~boot~',\n store: 'tasks',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'invalid_schema',\n ...(traceId !== undefined && { traceId }),\n });\n return null;\n }\n events?.emit('storage.read', {\n sessionId: traceId ?? '~boot~',\n store: 'tasks',\n filePath,\n operation: 'load',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(traceId !== undefined && { traceId }),\n });\n return parsed;\n } catch {\n events?.emit('storage.read', {\n sessionId: traceId ?? '~boot~',\n store: 'tasks',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse_failed',\n ...(traceId !== undefined && { traceId }),\n });\n return null;\n }\n}\n\n/**\n * Write the task file atomically. Prefer `mutateTasks` for read-modify-write\n * cycles \u2014 this low-level function does NOT acquire a lock.\n */\n/**\n * Persist the task file. Returns `true` on success, `false` if the write\n * failed (still emits `storage.error` + warns \u2014 does NOT throw). `mutateTasks`\n * inspects the result and throws so the task TOOL can report `ok:false`\n * instead of falsely claiming the tasks were saved.\n */\nexport async function saveTasks(\n filePath: string,\n tasks: TaskFile,\n events?: EventBus,\n traceId?: string,\n warn?: (msg: string) => void,\n): Promise<boolean> {\n const t0 = Date.now();\n try {\n tasks.updatedAt = new Date().toISOString();\n await atomicWrite(filePath, JSON.stringify(tasks, null, 2), { mode: 0o600 });\n events?.emit('storage.write', {\n sessionId: traceId ?? '~boot~',\n store: 'tasks',\n filePath,\n operation: 'save',\n outcome: 'success',\n durationMs: Date.now() - t0,\n ...(traceId !== undefined && { traceId }),\n });\n return true;\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: traceId ?? '~boot~',\n store: 'tasks',\n filePath,\n operation: 'save',\n outcome: 'failure',\n error: toErrorMessage(err),\n recoverable: false,\n ...(traceId !== undefined && { traceId }),\n });\n (\n warn ??\n ((m) =>\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'task_store.save_failed',\n message: m,\n timestamp: new Date().toISOString(),\n }),\n ))\n )(toErrorMessage(err));\n return false;\n }\n}\n\n/**\n * Load, modify, and save the task file under a file-level lock.\n * `fn` receives the current TaskFile (or a fresh empty one) and must\n * return the mutated TaskFile (mutating in-place is fine \u2014 the returned\n * reference is what gets saved).\n *\n * This is the primary API for any code path that reads *and then writes*\n * the task file \u2014 it prevents races from parallel `batch_tool_use` calls.\n */\nexport async function mutateTasks(\n filePath: string,\n sessionId: string,\n fn: (file: TaskFile) => TaskFile | Promise<TaskFile>,\n events?: EventBus,\n traceId?: string,\n): Promise<TaskFile> {\n return withFileLock(filePath, async () => {\n const file = (await loadTasks(filePath, events, traceId)) ?? emptyTaskFile(sessionId);\n const updated = await fn(file);\n const persisted = await saveTasks(filePath, updated, events, traceId);\n if (!persisted) {\n throw new SessionError({\n message: `Failed to persist tasks to ${filePath} \u2014 the change was NOT saved.`,\n code: 'SESSION_WRITE_FAILED',\n sessionId,\n context: { filePath, operation: 'mutateTasks' },\n });\n }\n return updated;\n });\n}\n", "import * as fsp from 'node:fs/promises';\nimport { hostname } from 'node:os';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { isPidAlive } from '../utils/pid.js';\n\n/**\n * Director state checkpoint \u2014 written incrementally throughout a fleet\n * run so a crashed director can be inspected (and eventually resumed)\n * instead of leaving only a final `fleet.json` manifest after `shutdown()`.\n *\n * Schema is JSON-friendly and deliberately denormalized. Each mutation\n * triggers an atomic-write of the whole file \u2014 small payloads (typically\n * < 10 KB even with dozens of subagents) make this cheap.\n */\nexport interface DirectorSubagentState {\n id: string;\n name?: string | undefined;\n role?: string | undefined;\n provider?: string | undefined;\n model?: string | undefined;\n spawnedAt: string;\n}\n\nexport interface DirectorTaskState {\n taskId: string;\n subagentId?: string | undefined;\n description?: string | undefined;\n status: 'pending' | 'running' | 'completed' | 'failed' | 'stopped' | 'timeout';\n assignedAt?: string | undefined;\n completedAt?: string | undefined;\n iterations?: number | undefined;\n toolCalls?: number | undefined;\n durationMs?: number | undefined;\n error?: string | undefined;\n worktree?: DirectorTaskWorktreeState | undefined;\n}\n\nexport interface DirectorTaskWorktreeState {\n taskId: string;\n subagentId: string;\n handleId: string;\n dir: string;\n branch: string;\n baseBranch: string;\n status:\n | 'allocated'\n | 'fallback'\n | 'committed'\n | 'merged'\n | 'kept'\n | 'released'\n | 'conflict'\n | 'failed';\n commitSha?: string | undefined;\n conflictFiles?: string[] | undefined;\n error?: string | undefined;\n}\n\nexport interface DirectorStateSnapshot {\n version: 1;\n directorRunId: string;\n updatedAt: string;\n spawnCount: number;\n maxSpawns?: number | undefined;\n spawnDepth: number;\n maxSpawnDepth: number;\n directorBudget?:\n | {\n maxCostUsd?: number | undefined;\n maxTokens?: number | undefined;\n }\n | undefined;\n subagents: DirectorSubagentState[];\n tasks: DirectorTaskState[];\n /** Aggregated usage snapshot. Optional \u2014 populated by the Director on save when available. */\n usage?: unknown | undefined;\n}\n\nexport async function loadDirectorState(filePath: string): Promise<DirectorStateSnapshot | null> {\n let raw: string;\n try {\n raw = await fsp.readFile(filePath, 'utf8');\n } catch {\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as DirectorStateSnapshot;\n if (parsed?.version !== 1) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Lock file entry written when a director starts. Prevents two directors\n * from resuming the same run \u2014 the second one sees the lock and refuses\n * rather than corrupting the checkpoint by writing concurrently.\n */\nexport interface DirectorStateLock {\n pid: number;\n hostname: string;\n startedAt: string;\n}\n\n/**\n * Write a lock file to claim this checkpoint. Returns false if the lock\n * is already held by a live process; returns true if the lock was acquired\n * (either the file didn't exist, or the previous holder is dead).\n */\nexport async function acquireDirectorStateLock(\n lockPath: string,\n processId = process.pid,\n): Promise<boolean> {\n let existing: string | undefined;\n try {\n existing = await fsp.readFile(lockPath, 'utf8');\n } catch {\n // No lock file \u2014 we're safe to claim\n }\n\n if (existing) {\n try {\n const lock = JSON.parse(existing) as DirectorStateLock;\n // A live owner still holds this checkpoint \u2014 refuse. Only a genuinely\n // dead pid makes the lock stale and overwritable.\n //\n // This used to inline `process.kill(lock.pid, 0)` with a bare catch,\n // which treated EPERM (\"alive, but you may not signal it\" \u2014 another\n // user, or an elevated process) as dead and stole the lock from a\n // running director. isPidAlive reports EPERM as alive.\n if (isPidAlive(lock.pid)) return false;\n } catch {\n // Malformed lock \u2014 treat as stale.\n }\n }\n\n const lock: DirectorStateLock = {\n pid: processId,\n hostname: hostname(),\n startedAt: new Date().toISOString(),\n };\n await atomicWrite(lockPath, JSON.stringify(lock), { mode: 0o600 });\n return true;\n}\n\n/**\n * Remove the lock file. Call this on graceful Director.shutdown() so the\n * next director run can claim the checkpoint without stale-lock checks.\n */\nexport async function releaseDirectorStateLock(lockPath: string): Promise<void> {\n try {\n await fsp.unlink(lockPath);\n } catch {\n // ignore\n }\n}\n\n/**\n * In-memory accumulator with atomic-write checkpoint. The Director keeps\n * an instance, mutates it on every spawn/assign/complete/fail event, and\n * the instance debounces writes so a burst of activity collapses into a\n * single disk hit.\n *\n * Supports crash recovery: use `loadDirectorState()` to read an existing\n * checkpoint, then call `DirectorStateCheckpoint.resume(snapshot)` to\n * re-attach to a fleet mid-flight. The lock mechanism ensures no two\n * directors can claim the same checkpoint.\n */\nexport class DirectorStateCheckpoint {\n private snapshot: DirectorStateSnapshot;\n private readonly filePath: string;\n private readonly lockPath: string;\n private timer: NodeJS.Timeout | null = null;\n private readonly debounceMs: number;\n private writing = false;\n private rewriteRequested = false;\n\n constructor(\n filePath: string,\n init: {\n directorRunId: string;\n maxSpawns?: number | undefined;\n spawnDepth: number;\n maxSpawnDepth: number;\n directorBudget?:\n | {\n maxCostUsd?: number | undefined;\n maxTokens?: number | undefined;\n }\n | undefined;\n },\n debounceMs = 250,\n ) {\n this.filePath = filePath;\n // Lock file lives alongside the checkpoint \u2014 `<path>.lock`\n this.lockPath = `${filePath}.lock`;\n this.debounceMs = debounceMs;\n this.snapshot = {\n version: 1,\n directorRunId: init.directorRunId,\n updatedAt: new Date().toISOString(),\n spawnCount: 0,\n maxSpawns: init.maxSpawns,\n spawnDepth: init.spawnDepth,\n maxSpawnDepth: init.maxSpawnDepth,\n directorBudget: init.directorBudget,\n subagents: [],\n tasks: [],\n };\n }\n\n /**\n * Attempt to acquire the lock for this checkpoint. Call this before\n * resuming a crashed director run. If it returns false, another\n * director process is still running this fleet \u2014 do not resume.\n */\n async acquireLock(): Promise<boolean> {\n return acquireDirectorStateLock(this.lockPath);\n }\n\n /**\n * Release the lock on graceful shutdown. Call `flush()` first to ensure\n * the final checkpoint state is on disk before removing the lock.\n * Without this, the next resume will see a stale-lock and refuse.\n */\n async releaseLock(): Promise<void> {\n return releaseDirectorStateLock(this.lockPath);\n }\n\n /**\n * Resume from a snapshot previously loaded via `loadDirectorState()`.\n * Use this when `--resume <runId>` is triggered \u2014 the snapshot has\n * the full fleet state (subagents, tasks) from before the crash; the\n * checkpoint continues from there.\n */\n resume(snapshot: DirectorStateSnapshot): void {\n this.snapshot = snapshot;\n }\n\n current(): DirectorStateSnapshot {\n return this.snapshot;\n }\n\n recordSpawn(sub: DirectorSubagentState, spawnCount: number): void {\n this.snapshot = {\n ...this.snapshot,\n spawnCount,\n subagents: [...this.snapshot.subagents.filter((s) => s.id !== sub.id), sub],\n };\n this.bumpUpdatedAt();\n this.schedule();\n }\n\n recordTaskAssigned(task: DirectorTaskState): void {\n const exists = this.snapshot.tasks.some((t) => t.taskId === task.taskId);\n this.snapshot = {\n ...this.snapshot,\n tasks: exists\n ? this.snapshot.tasks.map((t) => (t.taskId === task.taskId ? { ...t, ...task } : t))\n : [...this.snapshot.tasks, task],\n };\n this.bumpUpdatedAt();\n this.schedule();\n }\n\n recordTaskStatus(\n taskId: string,\n patch: Partial<DirectorTaskState> & { status: DirectorTaskState['status'] },\n ): void {\n this.snapshot = {\n ...this.snapshot,\n tasks: this.snapshot.tasks.map((t) => (t.taskId === taskId ? { ...t, ...patch } : t)),\n };\n this.bumpUpdatedAt();\n this.schedule();\n }\n\n recordTaskWorktree(taskId: string, worktree: DirectorTaskWorktreeState): void {\n const exists = this.snapshot.tasks.some((t) => t.taskId === taskId);\n this.snapshot = {\n ...this.snapshot,\n tasks: exists\n ? this.snapshot.tasks.map((t) => (t.taskId === taskId ? { ...t, worktree } : t))\n : [\n ...this.snapshot.tasks,\n {\n taskId,\n subagentId: worktree.subagentId,\n status: 'running',\n worktree,\n },\n ],\n };\n this.bumpUpdatedAt();\n this.schedule();\n }\n\n setUsage(usage: unknown): void {\n this.snapshot = { ...this.snapshot, usage };\n this.bumpUpdatedAt();\n this.schedule();\n }\n\n /** Force a synchronous flush \u2014 used by Director.shutdown(). */\n async flush(): Promise<void> {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n await this.persist();\n // If a rewrite was requested while we waited, persist() scheduled\n // a follow-up write. Loop until no more rewrites are requested so\n // shutdown doesn't return before the most recent state lands on disk.\n /* v8 ignore start -- concurrency-defensive: persist()'s finally clears the flag in single-threaded flow */\n while (this.rewriteRequested) {\n this.rewriteRequested = false;\n await this.persist();\n }\n /* v8 ignore stop */\n }\n\n private bumpUpdatedAt(): void {\n this.snapshot = { ...this.snapshot, updatedAt: new Date().toISOString() };\n }\n\n private schedule(): void {\n if (this.timer) return;\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.persist();\n }, this.debounceMs);\n }\n\n private async persist(): Promise<void> {\n if (this.writing) {\n // A write is already in flight \u2014 defer to a follow-up flush so the\n // most recent state still lands. Without this guard, simultaneous\n // burst mutations can drop the latest snapshot if rename races.\n this.rewriteRequested = true;\n return;\n }\n this.writing = true;\n try {\n await atomicWrite(this.filePath, JSON.stringify(this.snapshot, null, 2), {\n mode: 0o600,\n });\n } catch (err) {\n console.warn(\n JSON.stringify({\n level: 'warn',\n event: 'director_state.checkpoint_write_failed',\n message: toErrorMessage(err),\n timestamp: new Date().toISOString(),\n }),\n );\n } finally {\n this.writing = false;\n /* v8 ignore start -- concurrency-defensive: rewriteRequested is only set by an overlapping persist() */\n if (this.rewriteRequested) {\n this.rewriteRequested = false;\n this.schedule();\n }\n /* v8 ignore stop */\n }\n }\n}\n", "import * as fsp from 'node:fs/promises';\nimport type { EventBus } from '../kernel/events.js';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\nimport { toErrorMessage } from '../utils/error.js';\nimport { color } from '../utils/color.js';\nimport { resolveWstackPaths } from '../utils/wstack-paths.js';\nimport { FsError, ERROR_CODES } from '../types/errors.js';\n\n/**\n * Long-running autonomous mission. A goal survives across sessions and\n * drives the EternalAutonomyEngine \u2014 every iteration of the engine\n * consults the goal to choose what to do next.\n *\n * Storage: `~/.wrongstack/projects/<hash>/goal.json`. Persistent and\n * project-scoped on purpose: the goal belongs to the codebase, not the\n * REPL session.\n */\n\nexport interface JournalEntry {\n /** ISO timestamp of the iteration. */\n at: string;\n /** Sequential iteration counter (1-based, monotonically increasing). */\n iteration: number;\n /** Source that produced the action ('todo' | 'git' | 'brainstorm' | 'resume' | 'manual' | 'parallel' | 'deliverable'). */\n source: 'todo' | 'git' | 'brainstorm' | 'resume' | 'manual' | 'parallel' | 'deliverable';\n /** Short one-line description of what the iteration set out to do. */\n task: string;\n /** Outcome status. */\n status: 'success' | 'failure' | 'aborted' | 'skipped';\n /** Optional free-form note (error message, summary, etc.). */\n note?: string | undefined;\n /** Optional token usage delta for this iteration. */\n tokens?: { input: number; output: number } | undefined;\n /** Optional USD cost delta for this iteration (provider-estimated). */\n costUsd?: number | undefined;\n}\n\nexport interface GoalFile {\n version: 1;\n /** The raw mission statement as entered by the user. */\n goal: string;\n /**\n * LLM-refined version of the goal \u2014 unambiguous, with concrete\n * deliverables and acceptance criteria.\n */\n refinedGoal?: string | undefined;\n /**\n * Concrete, verifiable deliverables extracted from the refined goal.\n */\n deliverables?: string[] | undefined;\n /**\n * Estimated completion 0-100. Updated by the engine after each\n * iteration. Null means \"not yet assessed\".\n */\n progress?: number | undefined;\n /** Human-readable note explaining the current progress estimate. */\n progressNote?: string | undefined;\n /**\n * Time-series of progress measurements for trend analysis.\n * Last 200 entries retained. Use `recordProgress()` to append.\n */\n progressHistory?: ProgressSnapshot[] | undefined;\n /**\n * Computed trend from recent progress measurements.\n * 'accelerating' | 'steady' | 'stalling' | undefined.\n */\n progressTrend?: 'accelerating' | 'steady' | 'stalling' | undefined;\n /** When the goal was first set or last replaced. */\n setAt: string;\n /** Updated on every iteration completion. */\n lastActivityAt: string;\n /** Total iterations the engine has run against this goal (cumulative). */\n iterations: number;\n /** Engine lifecycle state \u2014 'running' means another process owns this goal. */\n engineState: 'idle' | 'running' | 'stopped';\n /** Mission-level lifecycle. */\n goalState?: 'active' | 'paused' | 'completed' | 'abandoned' | undefined;\n /** Goal-linked Kanban board id. Optional for pre-Kanban goal files. */\n kanbanBoardId?: string | undefined;\n /** ISO timestamp recorded after Brain verifies that every deliverable is reached. */\n reachedAt?: string | undefined;\n /** Durable human-readable reach verdict; currently the literal `goal reached`. */\n reachedNote?: string | undefined;\n /**\n * Per-todo attempt counter.\n */\n todoAttempts?: Record<string, number>;\n /** Bounded ring buffer of recent iterations (newest last). */\n journal: JournalEntry[];\n}\n\n/** Cap on persisted journal entries \u2014 older entries are evicted FIFO. */\nexport const MAX_JOURNAL_ENTRIES = 500;\n\n/**\n * Resolve the goal file path for a given project root.\n *\n * SINGLE canonical location: the per-project directory that\n * `resolveWstackPaths()` uses for everything else (sessions, memory, specs) \u2014\n * `~/.wrongstack/projects/<slug>/goal.json`. This is the same path the `/goal`\n * slash command writes via `opts.paths.projectGoal`, so every reader/writer\n * (the eternal/parallel autonomy engines, the CLI autonomy commands, the TUI\n * F9 panel, and `/goal` itself) now agree on one file.\n *\n * Previously this returned a SEPARATE hash-based dir (`projects/<hash>/`), which\n * disagreed with `/goal` and littered the home dir with thousands of stray\n * `<hash>/goal.json` directories that held nothing else.\n */\nexport function goalFilePath(projectRoot: string): string {\n return resolveWstackPaths({ projectRoot }).projectGoal;\n}\n\nexport async function loadGoal(\n filePath: string,\n events?: EventBus,\n warn?: (msg: string) => void,\n): Promise<GoalFile | null> {\n const t0 = Date.now();\n let raw: string;\n try {\n raw = await fsp.readFile(filePath, 'utf8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n events?.emit('storage.read', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'load',\n outcome: 'success',\n durationMs: Date.now() - t0,\n });\n return null; // file doesn't exist \u2014 not an error\n }\n events?.emit('storage.error', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'load',\n error: toErrorMessage(err),\n recoverable: false,\n });\n throw err; // permission errors etc. should surface\n }\n try {\n const parsed = JSON.parse(raw) as GoalFile;\n if (parsed?.version !== 1 || typeof parsed.goal !== 'string' || !Array.isArray(parsed.journal)) {\n (warn ?? ((msg) => console.warn(JSON.stringify({ level: 'warn', event: 'goal_store.invalid_schema', path: filePath, message: msg, timestamp: new Date().toISOString() }))))(\n 'invalid schema \u2014 consider deleting and re-creating',\n );\n events?.emit('storage.read', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'invalid_schema',\n });\n return null;\n }\n events?.emit('storage.read', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'load',\n outcome: 'success',\n durationMs: Date.now() - t0,\n });\n return parsed;\n } catch {\n (warn ?? ((msg) => console.warn(JSON.stringify({ level: 'warn', event: 'goal_store.parse_failed', path: filePath, message: msg, timestamp: new Date().toISOString() }))))(\n 'JSON parse failed \u2014 consider deleting and re-creating',\n );\n events?.emit('storage.read', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'load',\n outcome: 'failure',\n durationMs: Date.now() - t0,\n error: 'parse_failed',\n });\n return null;\n }\n}\n\nexport async function saveGoal(filePath: string, goal: GoalFile, events?: EventBus): Promise<void> {\n const t0 = Date.now();\n try {\n await atomicWrite(filePath, JSON.stringify(goal, null, 2), { mode: 0o600 });\n events?.emit('storage.write', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'save',\n outcome: 'success',\n durationMs: Date.now() - t0,\n });\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'save',\n error: toErrorMessage(err),\n recoverable: false,\n });\n throw new FsError({\n message: toErrorMessage(err),\n code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,\n path: filePath,\n cause: err,\n });\n }\n}\n\n/**\n * Atomically load, modify, and save a goal file under a file lock.\n * Prevents lost-update races when the autonomy engine and CLI /goal commands\n * write concurrently (both eternal and parallel engines may run simultaneously).\n *\n * `fn` receives the current GoalFile (or `null` if no goal exists yet)\n * and must return the updated GoalFile (or `null` to delete).\n */\nexport async function updateGoal(\n filePath: string,\n fn: (current: GoalFile | null) => GoalFile | null,\n events?: EventBus,\n): Promise<void> {\n const t0 = Date.now();\n await withFileLock(filePath, async () => {\n const current = await loadGoal(filePath, events);\n const next = fn(current);\n if (next) {\n await saveGoal(filePath, next, events);\n } else {\n try {\n await fsp.unlink(filePath);\n events?.emit('storage.write', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'delete',\n outcome: 'success',\n durationMs: Date.now() - t0,\n });\n } catch (err) {\n events?.emit('storage.error', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'delete',\n error: toErrorMessage(err),\n recoverable: true,\n });\n // best-effort \u2014 file may not exist\n }\n }\n events?.emit('storage.write', {\n sessionId: '~boot~',\n store: 'goal',\n filePath,\n operation: 'update',\n outcome: 'success',\n durationMs: Date.now() - t0,\n });\n });\n}\n\nexport function emptyGoal(goal: string): GoalFile {\n const now = new Date().toISOString();\n return {\n version: 1,\n goal,\n setAt: now,\n lastActivityAt: now,\n iterations: 0,\n engineState: 'idle',\n goalState: 'active',\n todoAttempts: {},\n journal: [],\n };\n}\n\n/**\n * Set progress estimate on a goal. Returns a new GoalFile.\n * Clamps progress to 0-100.\n */\nexport function setProgress(\n goal: GoalFile,\n progress: number,\n note?: string,\n): GoalFile {\n const clamped = Math.min(100, Math.max(0, progress));\n return {\n ...goal,\n progress: clamped,\n progressNote: note ?? clamped + '% complete',\n };\n}\n\n/**\n * Append a journal entry, bumping iteration counters and trimming the\n * ring buffer. Returns a new GoalFile \u2014 does not mutate the argument.\n */\nexport function appendJournal(goal: GoalFile, entry: Omit<JournalEntry, 'iteration' | 'at'>): GoalFile {\n const iteration = goal.iterations + 1;\n const at = new Date().toISOString();\n const full: JournalEntry = { ...entry, iteration, at };\n const journal = [...goal.journal, full];\n const trimmed = journal.length > MAX_JOURNAL_ENTRIES\n ? journal.slice(journal.length - MAX_JOURNAL_ENTRIES)\n : journal;\n return {\n ...goal,\n iterations: iteration,\n lastActivityAt: at,\n journal: trimmed,\n };\n}\n\n/**\n * Aggregate cumulative cost + tokens across all journal entries.\n */\nexport function summarizeUsage(goal: GoalFile): {\n totalCostUsd: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n iterationsWithUsage: number;\n} {\n let totalCostUsd = 0;\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let iterationsWithUsage = 0;\n for (const e of goal.journal) {\n if (typeof e.costUsd === 'number') totalCostUsd += e.costUsd;\n if (e.tokens) {\n totalInputTokens += e.tokens.input;\n totalOutputTokens += e.tokens.output;\n }\n if (typeof e.costUsd === 'number' || e.tokens) iterationsWithUsage++;\n }\n return { totalCostUsd, totalInputTokens, totalOutputTokens, iterationsWithUsage };\n}\n\nconst DOLLAR = '\\u0024';\n\n/** Format the goal + recent journal as a human-readable status block. */\nexport function formatGoal(goal: GoalFile, journalLimit = 10): string {\n const lines: string[] = [];\n\n // Header \u2014 show refined goal, with original as annotation if different\n const displayGoal = goal.refinedGoal || goal.goal;\n lines.push(color.bold('Goal') + ': ' + displayGoal);\n if (goal.refinedGoal && goal.refinedGoal !== goal.goal) {\n const snippet = goal.goal.length > 60 ? goal.goal.slice(0, 60) + '\u2026' : goal.goal;\n lines.push(color.dim(' (original: \"' + snippet + '\")'));\n }\n\n // Progress bar (20-segment)\n if (typeof goal.progress === 'number') {\n const pct = Math.min(100, Math.max(0, Math.round(goal.progress)));\n const filled = Math.round(pct / 5);\n const empty = 20 - filled;\n const bar = color.green('\u2588'.repeat(filled)) + color.dim('\u2591'.repeat(empty));\n lines.push('Progress: ' + bar + ' ' + color.bold(pct + '%'));\n if (goal.progressNote) {\n lines.push(' ' + color.dim(goal.progressNote));\n }\n // Trend indicator\n if (goal.progressTrend) {\n const trendIcon = goal.progressTrend === 'accelerating' ? '\uD83D\uDE80'\n : goal.progressTrend === 'stalling' ? '\u26A0\uFE0F'\n : '\u27A1\uFE0F';\n lines.push(' Trend: ' + trendIcon + ' ' + goal.progressTrend);\n }\n }\n\n // Deliverables checklist\n if (goal.deliverables && goal.deliverables.length > 0) {\n lines.push('');\n lines.push(color.bold('Deliverables:'));\n for (const d of goal.deliverables) {\n const done = /^\\[[x\u2713]\\]|\u2705|\\(done\\)/i.test(d);\n const marker = done ? color.green('\u2713') : color.dim('\u25CB');\n lines.push(' ' + marker + ' ' + d);\n }\n }\n\n lines.push('');\n lines.push('Set: ' + goal.setAt);\n lines.push('Last activity: ' + goal.lastActivityAt);\n lines.push('Iterations: ' + goal.iterations);\n const stateLabel = goal.goalState ?? 'active';\n lines.push('State: ' + stateLabel + (goal.iterations > 0 ? ' (iteration #' + goal.iterations + ')' : ''));\n lines.push('Engine: ' + goal.engineState);\n const usage = summarizeUsage(goal);\n if (usage.iterationsWithUsage > 0) {\n const spent = 'Spent: ' + DOLLAR + usage.totalCostUsd.toFixed(4)\n + ' (in ' + usage.totalInputTokens + ' / out ' + usage.totalOutputTokens\n + ' tokens across ' + usage.iterationsWithUsage + ' iterations)';\n lines.push(spent);\n }\n if (goal.journal.length > 0) {\n lines.push('');\n lines.push('Recent journal (last ' + Math.min(journalLimit, goal.journal.length) + '):');\n const tail = goal.journal.slice(-journalLimit);\n for (const e of tail) {\n const mark = e.status === 'success' ? '\u2713' : e.status === 'failure' ? '\u2717' : e.status === 'aborted' ? '\u2298' : '\u00B7';\n const note = e.note ? ' \u2014 ' + e.note : '';\n const cost = typeof e.costUsd === 'number' ? ' (' + DOLLAR + e.costUsd.toFixed(4) + ')' : '';\n lines.push(' #' + e.iteration + ' ' + mark + ' [' + e.source + '] ' + e.task + cost + note);\n }\n }\n return lines.join('\\n');\n}\n\n/** A single progress measurement at a point in time. */\nexport interface ProgressSnapshot {\n at: string;\n progress: number;\n note?: string | undefined;\n}\n\n/**\n * Parse [PROGRESS: N%] from agent final text.\n * Supports formats:\n * [PROGRESS: 45%]\n * [PROGRESS: 45%] \u2014 3/5 deliverables done\n * [progress: 100%]\n * Returns null if no match.\n */\nexport function parseProgressFromText(text: string): { progress: number; note?: string } | null {\n const re = /\\[progress:\\s*(\\d{1,3})%\\]\\s*(?:[\u2014-]\\s*(.+))?/i;\n const m = text.match(re);\n if (!m) return null;\n // Regex match guarantees capture group 1 exists, but use ?? fallback to\n // satisfy noUncheckedIndexedAccess without a non-null assertion.\n const progress = Math.min(100, Math.max(0, Number.parseInt(m[1] ?? '0', 10)));\n const note = m[2]?.trim() || undefined;\n return note === undefined ? { progress } : { progress, note };\n}\n\n/**\n * Record a progress measurement. Returns a new GoalFile with:\n * - progress + progressNote updated\n * - progressHistory appended (last 200 entries kept)\n * - progress trend computed (accelerating/steady/stalling)\n */\nexport function recordProgress(\n goal: GoalFile,\n progress: number,\n note?: string,\n): GoalFile {\n const clamped = Math.min(100, Math.max(0, progress));\n const history = [...(goal.progressHistory ?? []), { at: new Date().toISOString(), progress: clamped, note }];\n // Keep last 200 snapshots\n const trimmed = history.length > 200 ? history.slice(-200) : history;\n\n return {\n ...goal,\n progress: clamped,\n progressNote: note ?? `${clamped}% complete`,\n progressHistory: trimmed,\n progressTrend: computeTrend(trimmed),\n };\n}\n\n/** Max progress history entries to retain. */\nexport const MAX_PROGRESS_HISTORY = 200;\n\nfunction computeTrend(history: ProgressSnapshot[]): 'accelerating' | 'steady' | 'stalling' | undefined {\n if (history.length < 3) return undefined;\n const recent = history.slice(-5);\n const deltas: number[] = [];\n for (let i = 1; i < recent.length; i++) {\n deltas.push((recent[i]?.progress ?? 0) - (recent[i - 1]?.progress ?? 0));\n }\n /* v8 ignore next -- unreachable: history.length>=3 guard above guarantees >=2 deltas */\n if (deltas.length < 2) return undefined;\n const avgDelta = deltas.reduce((a, b) => a + b, 0) / deltas.length;\n if (avgDelta > 2) return 'accelerating';\n if (avgDelta < -1) return 'stalling';\n return 'steady';\n}\n", "/**\n * Goal-to-Kanban bridge module.\n *\n * Automatically creates and manages a kanban board for each `/goal set` mission.\n * The board mirrors the goal's deliverables as tasks flowing through\n * Backlog \u2192 In Progress \u2192 Done columns, and the autonomy engines update\n * task status as work progresses.\n *\n * API:\n * createGoalKanbanBoard(projectRoot, goalFile, goalId) \u2192 boardId\n * findGoalKanbanBoard(projectRoot, boardId) \u2192 KanbanBoard | null\n * deleteGoalKanbanBoard(projectRoot, boardId) \u2192 void\n * formatGoalKanbanPreview(goalFile) \u2192 string (rich display)\n * formatGoalKanbanChoicePrompt() \u2192 string (selection screen)\n */\n\nimport { addTask, createBoard, getBoard, listBoards, removeBoard } from '@wrongstack/kanban';\nimport { color } from '../utils/color.js';\nimport type { GoalFile } from './goal-store.js';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst GOAL_BOARD_TAG_PREFIX = 'goal:';\n\n// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Create (or update) a kanban board for the given goal.\n *\n * - If the goal file already carries a `kanbanBoardId` and the board still\n * exists, this is a no-op (returns the existing id).\n * - Otherwise a new board is created with three columns and one task per\n * deliverable. The board id is stored on `goalFile.kanbanBoardId`.\n *\n * Returns the board id. Returns null when the project root is unavailable.\n */\nexport async function createGoalKanbanBoard(\n projectRoot: string,\n goalFile: GoalFile,\n): Promise<string | null> {\n if (!projectRoot) return null;\n\n // Reuse an existing link if the board is still alive\n const existingId = (goalFile as GoalFileWithKanban).kanbanBoardId;\n if (existingId) {\n const existing = await getBoard(projectRoot, existingId).catch(() => null);\n if (existing) return existingId;\n }\n\n // Build a short goal suffix for the board title (max ~60 chars)\n const displayGoal = (goalFile.refinedGoal || goalFile.goal).replace(/\\s+/g, ' ').trim();\n const titleSuffix = displayGoal.length > 80 ? displayGoal.slice(0, 77) + '\u2026' : displayGoal;\n\n const board = await createBoard(projectRoot, {\n title: `\uD83C\uDFAF ${titleSuffix}`,\n description: `Kanban board auto-created for goal: ${displayGoal}`,\n tags: [goalTag(goalFile)],\n // Goal boards mirror an engine-driven run; the goal coordinator owns\n // completion semantics, so the kanban completion gate stays out of the way.\n completionGate: { enforcement: 'off' },\n });\n\n // The board is created with the kanban package's default columns (Backlog,\n // To Do, In Progress, Review, Done) which already cover the goal workflow.\n\n // Add one task per deliverable, each tagged with origin\n // so refreshGoalKanban() can match by stable ID instead of title text.\n const targetColumnId = board.columns[0]?.id ?? 'backlog';\n if (goalFile.deliverables && goalFile.deliverables.length > 0) {\n for (let index = 0; index < goalFile.deliverables.length; index++) {\n const d = goalFile.deliverables[index]!;\n const cleaned = d.replace(/^\\[[x\u2713]\\]|\u2705|\\(done\\)\\s*/i, '').trim();\n if (cleaned) {\n await addTask(projectRoot, board.id, {\n title: cleaned,\n columnId: targetColumnId,\n description: `Deliverable for goal: ${displayGoal}`,\n priority: 'medium',\n origin: {\n system: 'goal',\n taskId: `deliverable:${index}`,\n },\n }).catch(() => {\n // Best-effort: a task-add failure for one deliverable won't crash the goal\n });\n }\n }\n }\n\n // Store the board id on the goal file by mutating the passed reference\n (goalFile as GoalFileWithKanban).kanbanBoardId = board.id;\n\n return board.id;\n}\n\n/**\n * Find a kanban board by id. Returns null when the board doesn't exist\n * (e.g. manually deleted). The caller should handle this gracefully.\n */\nexport async function findGoalKanbanBoard(projectRoot: string, boardId: string) {\n if (!projectRoot || !boardId) return null;\n try {\n return await getBoard(projectRoot, boardId);\n } catch {\n return null;\n }\n}\n\n/**\n * Delete the kanban board linked to a goal. Best-effort.\n */\nexport async function deleteGoalKanbanBoard(projectRoot: string, boardId: string): Promise<void> {\n if (!projectRoot || !boardId) return;\n await removeBoard(projectRoot, boardId).catch(() => {});\n}\n\n/**\n * Find a goal-linked kanban board by its goal tag.\n * Used when the goal file's kanbanBoardId is missing but the board exists.\n */\nexport async function findGoalBoardByTag(projectRoot: string, goalFile: GoalFile) {\n if (!projectRoot) return null;\n const tag = goalTag(goalFile);\n const boards = await listBoards(projectRoot);\n const matched = boards.find((b) => b.tags?.includes(tag));\n if (!matched) return null;\n return getBoard(projectRoot, matched.id).catch(() => null);\n}\n\n/**\n * Build a formatted \"Goal Kanban\" preview block showing deliverables as\n * a kanban-column layout. This is the \"Goal event\" displayed after the\n * deliverables list.\n */\nexport function formatGoalKanbanPreview(\n goalFile: GoalFile,\n boardId?: string | null,\n taskCount?: number,\n): string {\n const lines: string[] = [];\n const displayGoal = (goalFile.refinedGoal || goalFile.goal).replace(/\\s+/g, ' ').trim();\n\n // \u2500\u2500 Header \u2500\u2500\n lines.push('');\n lines.push(color.bold(color.cyan('\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550')));\n lines.push(color.bold(color.cyan(' \uD83C\uDFAF GOAL KANBAN \u2014 Mission Board')));\n lines.push(color.bold(color.cyan('\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550')));\n lines.push('');\n lines.push(` ${color.bold('Mission:')} ${color.bold(displayGoal)}`);\n if (boardId) {\n lines.push(` ${color.dim(`Board: ${boardId.slice(0, 12)}\u2026`)}`);\n }\n if (typeof taskCount === 'number') {\n lines.push(` ${color.dim(`${taskCount} task(s)`)}`);\n }\n lines.push('');\n\n // \u2500\u2500 Deliverables as kanban columns \u2500\u2500\n const deliverables = goalFile.deliverables ?? [];\n const doneDeliverables = deliverables.filter((d) => /^\\[[x\u2713]\\]|\u2705|\\(done\\)/i.test(d)).length;\n const totalDeliverables = deliverables.length;\n\n if (totalDeliverables > 0) {\n // Backlog section (todo items)\n const backlog = deliverables.filter((d) => !/^\\[[x\u2713]\\]|\u2705|\\(done\\)|\uD83D\uDD04|in.progress/i.test(d));\n const inProgress = deliverables.filter((d) => /\uD83D\uDD04|in.progress/i.test(d));\n const done = deliverables.filter((d) => /^\\[[x\u2713]\\]|\u2705|\\(done\\)/i.test(d));\n\n const renderColumn = (\n title: string,\n items: string[],\n icon: string,\n accent: (s: string) => string,\n ) => {\n if (items.length === 0) {\n lines.push(` ${accent(color.bold(`${icon} ${title}`))} ${color.dim('(empty)')}`);\n return;\n }\n lines.push(` ${accent(color.bold(`${icon} ${title} (${items.length})`))}`);\n for (const item of items) {\n const cleaned = item.replace(/^\\[[x\u2713]\\]|\u2705|\\(done\\)|\uD83D\uDD04|in.progress\\s*/gi, '').trim();\n lines.push(` \u25CB ${cleaned}`);\n }\n };\n\n renderColumn('Backlog', backlog, '\uD83D\uDCCB', (s) => color.dim(s));\n renderColumn('In Progress', inProgress, '\uD83D\uDD04', (s) => color.cyan(s));\n renderColumn('Done', done, '\u2705', (s) => color.green(s));\n\n lines.push('');\n lines.push(\n ` ${color.dim(`Progress: ${doneDeliverables}/${totalDeliverables} deliverables complete`)}`,\n );\n }\n\n // \u2500\u2500 Progress bar (when available) \u2500\u2500\n if (typeof goalFile.progress === 'number') {\n const pct = Math.min(100, Math.max(0, Math.round(goalFile.progress)));\n const filled = Math.round(pct / 5);\n const empty = 20 - filled;\n const bar = color.green('\u2588'.repeat(filled)) + color.dim('\u2591'.repeat(empty));\n lines.push(` ${color.dim('Overall:')} ${bar} ${color.bold(`${pct}%`)}`);\n if (goalFile.progressNote) {\n lines.push(` ${color.dim(goalFile.progressNote)}`);\n }\n }\n if (typeof goalFile.progress === 'undefined' && totalDeliverables > 0) {\n const pct = Math.round((doneDeliverables / totalDeliverables) * 100);\n const filled = Math.round(pct / 5);\n const empty = 20 - filled;\n const bar = (pct > 0 ? color.green('\u2588'.repeat(filled)) : '') + color.dim('\u2591'.repeat(empty));\n lines.push(` ${color.dim('Overall:')} ${bar} ${color.bold(`${pct}%`)}`);\n }\n\n lines.push('');\n lines.push(color.bold(color.cyan('\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550')));\n\n return lines.join('\\n');\n}\n\n/**\n * Build the \"Goal event\" banner \u2014 shown after deliverables in `/goal set`.\n * A compact, visually rich summary that doubles as the \"goal event\" trigger.\n */\nexport function formatGoalEvent(goalFile: GoalFile, boardId?: string | null): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(color.bold(color.green('\u250C\u2500\u2500\u2500 Goal Event \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510')));\n lines.push(color.bold(color.green('\u2502')) + ' \uD83C\uDFAF Mission locked and ready');\n if (boardId) {\n lines.push(\n color.bold(color.green('\u2502')) +\n ` \uD83D\uDCCB Kanban board created: ${color.cyan(boardId.slice(0, 12) + '\u2026')}`,\n );\n }\n const deliverables = goalFile.deliverables ?? [];\n lines.push(\n color.bold(color.green('\u2502')) +\n ` \uD83D\uDCE6 ${deliverables.length} deliverable${deliverables.length !== 1 ? 's' : ''} extracted`,\n );\n if (typeof goalFile.progress === 'number') {\n lines.push(color.bold(color.green('\u2502')) + ` \uD83D\uDCCA Progress: ${goalFile.progress}%`);\n }\n lines.push(color.bold(color.green('\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518')));\n\n return lines.join('\\n');\n}\n\n/**\n * Build the autonomy choice prompt \u2014 shown after the goal event.\n * Presents the user with two modes and asks for a selection.\n */\nexport function formatGoalAutonomyChoice(): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(color.bold(color.magenta('\u2550\u2550\u2550 Choose Autonomy Mode \u2550\u2550\u2550')));\n lines.push('');\n lines.push(\n ` ${color.bold('1')}. ${color.red('Autonomy Eternal')}` +\n ` ${color.dim('\u2014 Single-agent sense/decide/execute/reflect loop')}`,\n );\n lines.push(\n ` ${color.bold('2')}. ${color.magenta('Eternal Parallel')}` +\n ` ${color.dim('\u2014 Multi-agent fan-out (4-8 subagents per tick)')}`,\n );\n lines.push('');\n lines.push(` ${color.dim('Enter 1 or 2 (or press Enter to skip):')}`);\n\n return lines.join('\\n');\n}\n\n/**\n * Parse a user choice for autonomy mode.\n * Returns 'eternal' | 'eternal-parallel' | null (skip).\n */\nexport function parseAutonomyChoice(input: string): 'eternal' | 'eternal-parallel' | null {\n const trimmed = input.trim().toLowerCase();\n if (trimmed === '1' || trimmed === 'eternal' || trimmed === 'e') return 'eternal';\n if (trimmed === '2' || trimmed === 'parallel' || trimmed === 'p') return 'eternal-parallel';\n if (trimmed === '' || trimmed === 'skip' || trimmed === 's' || trimmed === '0') return null;\n return null;\n}\n\n// \u2500\u2500 Internal helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction goalTag(goalFile: GoalFile): string {\n // Use the goal's first 24 chars as a stable tag component\n const goal = (goalFile.refinedGoal || goalFile.goal)\n .replace(/\\s+/g, '-')\n .slice(0, 24)\n .toLowerCase();\n return `${GOAL_BOARD_TAG_PREFIX}${goal}`;\n}\n\n// \u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Extended GoalFile with optional kanban board reference.\n */\nexport interface GoalFileWithKanban extends GoalFile {\n kanbanBoardId?: string | undefined;\n}\n", "import { updateTask } from '@wrongstack/kanban';\nimport type { BrainArbiter } from '../coordination/brain.js';\nimport type { EventBus } from '../kernel/events.js';\nimport { findGoalBoardByTag, findGoalKanbanBoard, type GoalFileWithKanban } from './goal-kanban.js';\nimport { appendJournal, type GoalFile, loadGoal, recordProgress, saveGoal } from './goal-store.js';\n\nconst DONE_MARKER = /\\[done:\\s*([^\\]]+)\\]/gi;\nconst DONE_PREFIX = /^\\s*(?:\u2705|\\[[x\u2713]\\]|\\(done\\))\\s*/i;\n\nexport interface CompletedGoalDeliverable {\n index: number;\n text: string;\n}\n\nexport interface GoalCoordinationResult {\n goal: GoalFile;\n completed: CompletedGoalDeliverable[];\n kanbanUpdatedTaskIds: string[];\n reached: boolean;\n}\n\nexport interface CoordinateGoalIterationOptions {\n projectRoot: string;\n goalPath: string;\n finalText: string;\n brain?: BrainArbiter | undefined;\n sessionId?: string | undefined;\n events?: EventBus | undefined;\n now?: (() => Date) | undefined;\n}\n\n/**\n * Parse optional `[DONE: index]` / `[DONE: text-prefix]` markers emitted by an\n * autonomy iteration. Numeric indices are human-facing and therefore 1-based;\n * `0` is accepted as an explicit first-item alias for backwards compatibility.\n */\nexport function parseCompletedGoalDeliverables(\n finalText: string,\n deliverables: readonly string[],\n): CompletedGoalDeliverable[] {\n if (!finalText || deliverables.length === 0) return [];\n const seen = new Set<number>();\n const completed: CompletedGoalDeliverable[] = [];\n\n for (const match of finalText.matchAll(DONE_MARKER)) {\n const raw = match[1]?.trim();\n if (!raw) continue;\n const numeric = /^\\d+$/.test(raw) ? Number.parseInt(raw, 10) : undefined;\n let index = numeric === undefined ? -1 : numeric === 0 ? 0 : numeric - 1;\n if (index < 0 || index >= deliverables.length) {\n const prefix = raw.toLowerCase();\n index = deliverables.findIndex((item) =>\n stripGoalDeliverableMarker(item).toLowerCase().startsWith(prefix),\n );\n }\n if (index < 0 || index >= deliverables.length || seen.has(index)) continue;\n const source = deliverables[index] ?? '';\n if (isGoalDeliverableComplete(source)) continue;\n seen.add(index);\n completed.push({ index, text: stripGoalDeliverableMarker(source) });\n }\n return completed;\n}\n\nexport function isGoalDeliverableComplete(value: string): boolean {\n return DONE_PREFIX.test(value);\n}\n\nexport function stripGoalDeliverableMarker(value: string): string {\n return value.replace(DONE_PREFIX, '').trim();\n}\n\n/** Mark newly completed deliverables and recompute progress from the checklist. */\nexport function applyGoalDeliverableCompletions(\n goal: GoalFile,\n completed: readonly CompletedGoalDeliverable[],\n): GoalFile {\n if (completed.length === 0) return recomputeGoalProgress(goal);\n const completedIndices = new Set(completed.map((item) => item.index));\n let next: GoalFile = {\n ...goal,\n deliverables: (goal.deliverables ?? []).map((item, index) =>\n completedIndices.has(index) && !isGoalDeliverableComplete(item) ? `\u2705 ${item}` : item,\n ),\n };\n next = appendGoalDeliverableJournals(next, completed);\n return recomputeGoalProgress(next);\n}\n\nfunction appendGoalDeliverableJournals(\n goal: GoalFile,\n completed: readonly CompletedGoalDeliverable[],\n): GoalFile {\n let next = goal;\n for (const item of completed) {\n next = appendJournal(next, {\n source: 'deliverable',\n task: `Deliverable completed: ${item.text}`.slice(0, 240),\n status: 'success',\n note: `[DONE: ${item.index + 1}]`,\n });\n }\n return next;\n}\n\nexport function recomputeGoalProgress(goal: GoalFile): GoalFile {\n const deliverables = goal.deliverables ?? [];\n if (deliverables.length === 0) return goal;\n const done = deliverables.filter(isGoalDeliverableComplete).length;\n const progress = Math.round((done / deliverables.length) * 100);\n return recordProgress(goal, progress, `${done}/${deliverables.length} deliverables complete`);\n}\n\n/**\n * Close the marker \u2192 Kanban \u2192 progress \u2192 Brain loop and persist the resulting\n * goal state. Brain is called exactly once, and only when every deliverable is\n * complete. A missing/negative Brain leaves the goal active at 100%.\n */\nexport async function coordinateGoalIteration(\n options: CoordinateGoalIterationOptions,\n): Promise<GoalCoordinationResult | null> {\n const current = await loadGoal(options.goalPath, options.events);\n if (!current) return null;\n\n const completed = parseCompletedGoalDeliverables(options.finalText, current.deliverables ?? []);\n const board = await resolveGoalBoard(options.projectRoot, current);\n let kanbanUpdatedTaskIds: string[] = [];\n let goal: GoalFile;\n if (board) {\n // Kanban owns durable deliverable status. Marker output is a requested\n // transition; goal.json is rebuilt from the committed board state.\n kanbanUpdatedTaskIds = await refreshGoalKanban(options.projectRoot, current, completed, board);\n goal = await recomputeGoalProgressFromKanban(options.projectRoot, current);\n const confirmed = completed.filter(\n (item) =>\n !isGoalDeliverableComplete(current.deliverables?.[item.index] ?? '') &&\n isGoalDeliverableComplete(goal.deliverables?.[item.index] ?? ''),\n );\n goal = appendGoalDeliverableJournals(goal, confirmed);\n } else {\n // Pre-Kanban goals keep their existing file-backed checklist semantics.\n goal = applyGoalDeliverableCompletions(current, completed);\n }\n\n let reached = false;\n const allComplete =\n (goal.deliverables?.length ?? 0) > 0 &&\n goal.deliverables?.every(isGoalDeliverableComplete) === true;\n if (allComplete && options.brain) {\n const decision = await options.brain.decide({\n id: `goal-reached-${goal.iterations}`,\n sessionId: options.sessionId,\n source: 'system',\n question: 'Have all goal deliverables been reached and verified?',\n context: [\n `Goal: ${goal.refinedGoal ?? goal.goal}`,\n `Progress: ${goal.progress ?? 0}%`,\n 'Completed deliverables:',\n ...(goal.deliverables ?? []).map(\n (item, index) => `${index + 1}. ${stripGoalDeliverableMarker(item)}`,\n ),\n ].join('\\n'),\n risk: 'high',\n fallback: 'continue',\n options: [\n {\n id: 'goal_reached',\n label: 'Goal reached',\n consequence: 'Persist the completed goal and stop the eternal loop.',\n },\n {\n id: 'keep_working',\n label: 'Keep working',\n consequence: 'Leave the goal active for another iteration.',\n },\n ],\n });\n reached = decision.type === 'answer' && decision.optionId === 'goal_reached';\n }\n\n if (reached) {\n const reachedAt = (options.now?.() ?? new Date()).toISOString();\n goal = appendJournal(\n {\n ...goal,\n goalState: 'completed',\n engineState: 'stopped',\n progress: 100,\n progressNote: 'goal reached',\n reachedAt,\n reachedNote: 'goal reached',\n },\n {\n source: 'deliverable',\n task: 'GOAL REACHED',\n status: 'success',\n note: 'goal reached',\n },\n );\n }\n await saveGoal(options.goalPath, goal, options.events);\n return { goal, completed, kanbanUpdatedTaskIds, reached };\n}\n\nasync function resolveGoalBoard(projectRoot: string, goal: GoalFile) {\n const boardId = (goal as GoalFileWithKanban).kanbanBoardId;\n if (boardId) {\n const board = await findGoalKanbanBoard(projectRoot, boardId);\n if (board) return board;\n }\n return findGoalBoardByTag(projectRoot, goal);\n}\n\nasync function refreshGoalKanban(\n projectRoot: string,\n goal: GoalFile,\n completed: readonly CompletedGoalDeliverable[],\n resolvedBoard?: Awaited<ReturnType<typeof resolveGoalBoard>>,\n): Promise<string[]> {\n if (completed.length === 0) return [];\n const board = resolvedBoard ?? (await resolveGoalBoard(projectRoot, goal));\n if (!board) return [];\n const doneColumn = board.columns.find((column) => column.title.toLowerCase() === 'done');\n if (!doneColumn) return [];\n\n // Build a set of deliverable origin keys from completed items.\n // Each item carries its index in the deliverables array, which matches\n // the `deliverable:${index}` origin.taskId set at board creation time.\n const originKeys = new Set(completed.map((item) => `deliverable:${item.index}`));\n\n // Build title-based fallback set for boards created before origin tracking.\n const completedTitles = new Set(completed.map((item) => normalizeTitle(item.text)));\n\n const updated: string[] = [];\n for (const task of board.tasks) {\n // Prefer origin-based matching (stable) over title-based (fragile).\n const originMatch =\n task.origin?.system === 'goal' && task.origin?.taskId\n ? originKeys.has(task.origin.taskId)\n : false;\n const titleMatch = !task.origin?.taskId && completedTitles.has(normalizeTitle(task.title));\n if (!originMatch && !titleMatch) continue;\n const result = await updateTask(\n projectRoot,\n board.id,\n task.id,\n { columnId: doneColumn.id, status: 'completed' },\n {\n actor: 'goal-coordinator',\n note: originMatch\n ? `deliverable-completed: Matched by origin ${task.origin!.taskId}.`\n : 'deliverable-completed: Updated from [DONE:] marker (title fallback).',\n },\n );\n if (result) updated.push(task.id);\n }\n return updated;\n}\n\nasync function recomputeGoalProgressFromKanban(\n projectRoot: string,\n goal: GoalFile,\n): Promise<GoalFile> {\n const board = await resolveGoalBoard(projectRoot, goal);\n if (!board || !goal.deliverables?.length) return recomputeGoalProgress(goal);\n const tasksByOrigin = new Map(\n board.tasks\n .filter((task) => task.origin?.system === 'goal' && task.origin.taskId)\n .map((task) => [task.origin!.taskId!, task]),\n );\n const completedLegacyTitles = new Set(\n board.tasks\n .filter((task) => !task.origin?.taskId && task.status === 'completed')\n .map((task) => normalizeTitle(task.title)),\n );\n const deliverables = goal.deliverables.map((item, index) => {\n const text = stripGoalDeliverableMarker(item);\n const originTask = tasksByOrigin.get(`deliverable:${index}`);\n const complete = originTask\n ? originTask.status === 'completed'\n : completedLegacyTitles.has(normalizeTitle(text)) || isGoalDeliverableComplete(item);\n return complete ? `\u2705 ${text}` : text;\n });\n return recomputeGoalProgress({ ...goal, deliverables });\n}\n\nfunction normalizeTitle(value: string): string {\n return stripGoalDeliverableMarker(value).replace(/\\s+/g, ' ').trim().toLowerCase();\n}\n", "import { createHash } from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { PromptEntry, PromptVariable } from '../types/prompt.js';\nimport { atomicWrite, ensureDir } from '../utils/atomic-write.js';\nimport { slugify } from '../utils/slug.js';\nimport { ulid } from '../utils/ulid.js';\nimport type { WstackPaths } from '../utils/wstack-paths.js';\n\nexport type { PromptEntry, PromptSource, PromptVariable } from '../types/prompt.js';\n\nexport interface PromptStore {\n list(): Promise<PromptEntry[]>;\n get(id: string): Promise<PromptEntry | null>;\n save(entry: PromptEntry): Promise<void>;\n delete(id: string): Promise<boolean>;\n find(query: string): Promise<PromptEntry[]>;\n}\n\n/** Current on-disk schema version. */\nconst SCHEMA_VERSION = 2;\n\ninterface RawPromptFile {\n version: number;\n entry: unknown;\n}\n\n/** sha256 of a prompt's content \u2014 used for builtin/synced integrity checks. */\nexport function promptChecksum(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/**\n * Upgrade any persisted prompt record (v1 or v2) to a fully-populated v2\n * `PromptEntry`. Pure \u2014 does not touch disk. v1 records (only\n * `id/title/content/tags/createdAt/updatedAt`) get sensible defaults:\n * slug from title, empty description, `uncategorized` category, `user` source.\n */\nexport function migratePromptEntry(raw: unknown): PromptEntry | null {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n if (typeof r['id'] !== 'string' || typeof r['title'] !== 'string') return null;\n\n const title = r['title'];\n const content = typeof r['content'] === 'string' ? r['content'] : '';\n const now = typeof r['createdAt'] === 'string' ? r['createdAt'] : new Date(0).toISOString();\n const tags = Array.isArray(r['tags'])\n ? (r['tags'].filter((t) => typeof t === 'string') as string[])\n : [];\n\n return {\n id: r['id'],\n slug: typeof r['slug'] === 'string' && r['slug'].length > 0 ? r['slug'] : slugify(title),\n title,\n description: typeof r['description'] === 'string' ? r['description'] : '',\n content,\n category:\n typeof r['category'] === 'string' && r['category'].length > 0\n ? r['category']\n : 'uncategorized',\n tags,\n source: isPromptSource(r['source']) ? r['source'] : 'user',\n favorite: r['favorite'] === true,\n variables: normalizeVariables(r['variables']),\n author: typeof r['author'] === 'string' ? r['author'] : undefined,\n version: typeof r['version'] === 'string' ? r['version'] : undefined,\n license: typeof r['license'] === 'string' ? r['license'] : undefined,\n checksum: typeof r['checksum'] === 'string' ? r['checksum'] : undefined,\n forkedFrom: typeof r['forkedFrom'] === 'string' ? r['forkedFrom'] : undefined,\n createdAt: now,\n updatedAt: typeof r['updatedAt'] === 'string' ? r['updatedAt'] : now,\n };\n}\n\nfunction isPromptSource(v: unknown): v is PromptEntry['source'] {\n return v === 'builtin' || v === 'user' || v === 'project' || v === 'synced';\n}\n\nfunction normalizeVariables(v: unknown): PromptVariable[] | undefined {\n if (!Array.isArray(v)) return undefined;\n const out: PromptVariable[] = [];\n for (const item of v) {\n if (\n item &&\n typeof item === 'object' &&\n typeof (item as Record<string, unknown>)['name'] === 'string'\n ) {\n const o = item as Record<string, unknown>;\n const enumVals =\n Array.isArray(o['enum']) && o['enum'].every((x) => typeof x === 'string')\n ? (o['enum'] as string[])\n : undefined;\n out.push({\n name: o['name'] as string,\n description: typeof o['description'] === 'string' ? o['description'] : undefined,\n default: typeof o['default'] === 'string' ? o['default'] : undefined,\n required: o['required'] === true ? true : undefined,\n enum: enumVals && enumVals.length > 0 ? enumVals : undefined,\n multiline: o['multiline'] === true ? true : undefined,\n });\n }\n }\n return out.length > 0 ? out : undefined;\n}\n\n/**\n * DefaultPromptStore \u2014 file-per-prompt JSON in a single directory (the global\n * the active profile's `prompts` directory by default, or a layer dir the loader passes in).\n * Reads tolerate legacy v1 files via `migratePromptEntry`; writes always emit\n * the current v2 schema.\n */\nexport class DefaultPromptStore implements PromptStore {\n private readonly dir: string;\n\n constructor(pathsOrDir: WstackPaths | string) {\n this.dir = typeof pathsOrDir === 'string' ? pathsOrDir : pathsOrDir.globalPrompts;\n }\n\n async list(): Promise<PromptEntry[]> {\n await ensureDir(this.dir);\n const entries: PromptEntry[] = [];\n try {\n const files = await fs.readdir(this.dir);\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n try {\n const raw: RawPromptFile = JSON.parse(\n await fs.readFile(path.join(this.dir, file), 'utf8'),\n );\n const migrated = migratePromptEntry(raw.entry);\n if (migrated) entries.push(migrated);\n } catch {\n // skip corrupt files\n }\n }\n } catch {\n // dir doesn't exist yet\n }\n return entries.sort(\n (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n );\n }\n\n async get(id: string): Promise<PromptEntry | null> {\n const file = path.join(this.dir, `${id}.json`);\n try {\n const raw: RawPromptFile = JSON.parse(await fs.readFile(file, 'utf8'));\n return migratePromptEntry(raw.entry);\n } catch {\n return null;\n }\n }\n\n async save(entry: PromptEntry): Promise<void> {\n await ensureDir(this.dir);\n const file = path.join(this.dir, `${entry.id}.json`);\n const raw: RawPromptFile = { version: SCHEMA_VERSION, entry };\n await atomicWrite(file, JSON.stringify(raw, null, 2));\n }\n\n async delete(id: string): Promise<boolean> {\n const file = path.join(this.dir, `${id}.json`);\n try {\n await fs.unlink(file);\n return true;\n } catch {\n return false;\n }\n }\n\n async find(query: string): Promise<PromptEntry[]> {\n const all = await this.list();\n const lower = query.toLowerCase();\n return all.filter(\n (e) =>\n e.title.toLowerCase().includes(lower) ||\n e.description.toLowerCase().includes(lower) ||\n e.content.toLowerCase().includes(lower) ||\n e.category.toLowerCase().includes(lower) ||\n e.tags.some((t) => t.toLowerCase().includes(lower)),\n );\n }\n\n /** Create a new v2 entry and return it. Does NOT persist \u2014 call save() afterwards. */\n createNew(\n title: string,\n content: string,\n tags: string[] = [],\n extra: Partial<\n Omit<PromptEntry, 'id' | 'title' | 'content' | 'tags' | 'createdAt' | 'updatedAt'>\n > = {},\n ): PromptEntry {\n const now = new Date().toISOString();\n return {\n id: ulid(),\n slug: extra.slug && extra.slug.length > 0 ? extra.slug : slugify(title),\n title,\n description: extra.description ?? '',\n content,\n category: extra.category ?? 'uncategorized',\n tags,\n source: extra.source ?? 'user',\n favorite: extra.favorite ?? false,\n variables: extra.variables,\n author: extra.author,\n version: extra.version,\n license: extra.license,\n checksum: extra.checksum,\n forkedFrom: extra.forkedFrom,\n createdAt: now,\n updatedAt: now,\n };\n }\n}\n", "import * as fs from 'node:fs/promises';\nimport { atomicWrite, withFileLock } from '../utils/atomic-write.js';\n\nexport interface PromptUsage {\n count: number;\n lastUsedAt: string;\n}\n\ninterface RawUsageFile {\n version: 1;\n usage: Record<string, PromptUsage>;\n}\n\ninterface FileSignature {\n size: number;\n mtimeMs: number;\n ctimeMs: number;\n}\n\ninterface PendingRecord {\n slug: string;\n at: string;\n resolve: (usage: PromptUsage) => void;\n reject: (reason?: unknown) => void;\n}\n\n/**\n * Tracks how often each prompt is inserted, keyed by slug, in a single JSON\n * file (`profiles/<name>/prompt-usage.json`). Kept SEPARATE from the prompt\n * entries so usage can be recorded for read-only builtin prompts without\n * copy-on-writing them into the user layer. Surfaces \"recent / most-used\"\n * views and a gentle search-ranking boost.\n */\nexport class PromptUsageStore {\n private cachedUsage: Record<string, PromptUsage> | null = null;\n private cachedSignature: FileSignature | null = null;\n private readonly pendingRecords: PendingRecord[] = [];\n private drainScheduled = false;\n private draining = false;\n\n constructor(private readonly file: string) {}\n\n async load(): Promise<Record<string, PromptUsage>> {\n return cloneUsage(await this.loadInternal());\n }\n\n private async loadInternal(): Promise<Record<string, PromptUsage>> {\n const signature = await this.fileSignature();\n if (this.cachedUsage && sameSignature(signature, this.cachedSignature)) {\n return this.cachedUsage;\n }\n\n try {\n const raw: RawUsageFile = JSON.parse(await fs.readFile(this.file, 'utf8'));\n if (raw && typeof raw === 'object' && raw.usage && typeof raw.usage === 'object') {\n this.cachedUsage = raw.usage;\n this.cachedSignature = signature;\n return this.cachedUsage;\n }\n } catch {\n // missing or corrupt \u2192 empty\n }\n this.cachedUsage = {};\n this.cachedSignature = signature;\n return this.cachedUsage;\n }\n\n async record(slug: string, at: string = new Date().toISOString()): Promise<PromptUsage> {\n return await new Promise<PromptUsage>((resolve, reject) => {\n this.pendingRecords.push({ slug, at, resolve, reject });\n this.scheduleDrain();\n });\n }\n\n async get(slug: string): Promise<PromptUsage | undefined> {\n return (await this.load())[slug];\n }\n\n /** Slugs ordered by most-recently-used (then by count), capped at `limit`. */\n async recent(limit = 15): Promise<{ slug: string; usage: PromptUsage }[]> {\n const usage = await this.load();\n return Object.entries(usage)\n .map(([slug, u]) => ({ slug, usage: u }))\n .sort(\n (a, b) =>\n new Date(b.usage.lastUsedAt).getTime() - new Date(a.usage.lastUsedAt).getTime() ||\n b.usage.count - a.usage.count,\n )\n .slice(0, limit);\n }\n\n /** Slugs ordered by usage count (then recency), capped at `limit`. */\n async top(limit = 15): Promise<{ slug: string; usage: PromptUsage }[]> {\n const usage = await this.load();\n return Object.entries(usage)\n .map(([slug, u]) => ({ slug, usage: u }))\n .sort(\n (a, b) =>\n b.usage.count - a.usage.count ||\n new Date(b.usage.lastUsedAt).getTime() - new Date(a.usage.lastUsedAt).getTime(),\n )\n .slice(0, limit);\n }\n\n private scheduleDrain(): void {\n if (this.drainScheduled || this.draining) return;\n this.drainScheduled = true;\n queueMicrotask(() => {\n this.drainScheduled = false;\n void this.drainPending();\n });\n }\n\n private async drainPending(): Promise<void> {\n if (this.draining || this.pendingRecords.length === 0) return;\n this.draining = true;\n const batch = this.pendingRecords.splice(0);\n\n try {\n const results = await withFileLock(this.file, async () => {\n // Validate the cache after taking the cross-process lock. A peer may\n // have updated the file since our last read; size/mtime/ctime makes\n // that visible without re-reading unchanged JSON on every record.\n const usage = { ...(await this.loadInternal()) };\n const nextValues: PromptUsage[] = [];\n for (const item of batch) {\n const previous = usage[item.slug];\n const next: PromptUsage = {\n count: (previous?.count ?? 0) + 1,\n lastUsedAt: item.at,\n };\n usage[item.slug] = next;\n nextValues.push(next);\n }\n\n await atomicWrite(\n this.file,\n JSON.stringify({ version: 1, usage } satisfies RawUsageFile, null, 2),\n );\n this.cachedUsage = usage;\n this.cachedSignature = await this.fileSignature();\n return nextValues;\n });\n\n for (let index = 0; index < batch.length; index++) {\n batch[index]!.resolve(results[index]!);\n }\n } catch (error) {\n for (const item of batch) item.reject(error);\n } finally {\n this.draining = false;\n if (this.pendingRecords.length > 0) this.scheduleDrain();\n }\n }\n\n private async fileSignature(): Promise<FileSignature | null> {\n try {\n const stat = await fs.stat(this.file);\n return { size: stat.size, mtimeMs: stat.mtimeMs, ctimeMs: stat.ctimeMs };\n } catch {\n return null;\n }\n }\n}\n\nfunction sameSignature(a: FileSignature | null, b: FileSignature | null): boolean {\n if (a === null || b === null) return a === b;\n return a.size === b.size && a.mtimeMs === b.mtimeMs && a.ctimeMs === b.ctimeMs;\n}\n\nfunction cloneUsage(usage: Record<string, PromptUsage>): Record<string, PromptUsage> {\n return Object.fromEntries(Object.entries(usage).map(([slug, value]) => [slug, { ...value }]));\n}\n", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { atomicWrite, ensureDir } from '../utils/atomic-write.js';\nimport type { SecretScrubber } from '../types/secret-scrubber.js';\n\n/**\n * On-disk shape of the per-project TUI input history file. Newest entry\n * first. `version` is forward-compat \u2014 a future schema bump can migrate.\n */\ninterface InputHistoryFile {\n version: 1;\n updatedAt: string;\n entries: string[];\n}\n\nexport const INPUT_HISTORY_DEFAULT_MAX = 100;\n\n/**\n * Per-project TUI prompt input history.\n *\n * Backed by a single JSON file at `~/.wrongstack/projects/<slug>/input-\n * history.json`. Entries are newest-first, capped at `maxEntries`. Before\n * any entry is written to disk it is passed through a `SecretScrubber` and\n * dropped if it still contains a redacted marker \u2014 leaking a pasted API\n * key into a history file would be a real incident, so the scrubber is\n * mandatory, not optional.\n *\n * The store is intentionally small and synchronous-ish (load once at TUI\n * boot, save debounced on change). It mirrors PromptUsageStore's shape so\n * the two read as a family.\n */\nexport class InputHistoryStore {\n /**\n * @param file Absolute path to the per-project input-history.json.\n * @param scrubber SecretScrubber used to filter secrets before write.\n * @param maxEntries Cap on the number of entries persisted. Default 100.\n */\n constructor(\n private readonly file: string,\n private readonly scrubber: SecretScrubber,\n private readonly maxEntries: number = INPUT_HISTORY_DEFAULT_MAX,\n ) {}\n\n /**\n * Load the persisted entries. Missing or corrupt file \u2192 empty list\n * (never throws \u2014 history is best-effort).\n */\n async load(): Promise<string[]> {\n try {\n const raw: InputHistoryFile = JSON.parse(await fs.readFile(this.file, 'utf8'));\n if (\n raw &&\n typeof raw === 'object' &&\n Array.isArray(raw.entries) &&\n raw.entries.every((e) => typeof e === 'string')\n ) {\n return raw.entries.slice(0, this.maxEntries);\n }\n } catch {\n // missing or corrupt \u2192 empty\n }\n return [];\n }\n\n /**\n * Persist `entries` to disk after scrubbing each one. Entries that still\n * contain a redaction marker AFTER scrubbing are dropped \u2014 a half-scrubbed\n * prompt like \"export API_KEY=***REDACTED***\" is useless as history and\n * keeping it risks confusing the agent. Dedup is the caller's job (the\n * reducer already dedups); this method only scrubs, caps, and writes.\n */\n async save(entries: string[]): Promise<void> {\n const cleaned = this.scrubAndFilter(entries);\n await ensureDir(path.dirname(this.file));\n const payload: InputHistoryFile = {\n version: 1,\n updatedAt: new Date().toISOString(),\n entries: cleaned,\n };\n await atomicWrite(this.file, JSON.stringify(payload, null, 2));\n }\n\n /** Truncate the file to an empty entry list (used by /clear). */\n async clear(): Promise<void> {\n await ensureDir(path.dirname(this.file));\n const payload: InputHistoryFile = { version: 1, updatedAt: new Date().toISOString(), entries: [] };\n await atomicWrite(this.file, JSON.stringify(payload, null, 2));\n }\n\n /**\n * Run each entry through the scrubber and drop any that still contain a\n * redaction marker. Internal so the test suite can pin the policy.\n */\n private scrubAndFilter(entries: string[]): string[] {\n const out: string[] = [];\n for (const entry of entries) {\n const scrubbed = this.scrubber.scrub(entry);\n if (this.looksRedacted(scrubbed)) continue;\n out.push(scrubbed);\n }\n return out.slice(0, this.maxEntries);\n }\n\n /**\n * Heuristic: did the scrubber actually redact anything? DefaultSecretScrubber\n * replaces matches with \"***REDACTED***\", so a residual marker means a secret\n * was detected. Subclasses of SecretScrubber that use a different sentinel\n * can be accommodated by widening this check in the future; for now the\n * marker check covers the shipped scrubber.\n */\n private looksRedacted(text: string): boolean {\n return text.includes('***REDACTED***');\n }\n}\n", "import { expectDefined } from '../utils/expect-defined.js';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { atomicWrite } from '../utils/atomic-write.js';\nimport { createHash } from 'node:crypto';\nimport type { WstackPaths } from '../utils/wstack-paths.js';\nimport type { SyncCategory, SyncConfig } from '../types/config.js';\nimport { FsError, WrongStackError, ERROR_CODES } from '../types/errors.js';\nexport const ALL_SYNC_CATEGORIES: SyncCategory[] = ['settings', 'skills', 'prompts', 'memory', 'history'];\n\nexport interface SyncResult {\n ok: boolean;\n action: 'push' | 'pull';\n categories: SyncCategory[];\n committedAt?: string | undefined;\n message: string;\n}\n\ntype LocalTreeEntry = { path: string; content: string; mode: string };\ntype GitTreeEntry = LocalTreeEntry | { path: string; sha: null; mode: string };\ntype RemoteTreeEntry = { path: string; sha: string; type: 'blob' | 'tree' };\n\ninterface SyncStateFile {\n version: 1;\n /** Last pushed/pulled commit SHA. */\n sha: string;\n /** Tree SHA paired with `sha`; absent in legacy state files. */\n treeSha?: string | undefined;\n lastSyncedAt: string;\n localRev: string;\n}\n\n/**\n * CloudSync \u2014 push/pull user-selected ~/.wrongstack categories to a\n * private GitHub repo. No git CLI needed; uses GitHub REST API via fetch.\n * The token is stored encrypted via SecretVault (field named `githubToken`\n * so the vault walker picks it up automatically).\n */\nexport class CloudSync {\n private readonly statePath: string;\n private state: SyncStateFile | null = null;\n private readonly getSettingsConfigPath: () => string;\n\n constructor(\n private readonly paths: WstackPaths,\n private readonly getConfig: () => SyncConfig | null,\n private readonly setConfig: (c: SyncConfig) => Promise<void>,\n getSettingsConfigPath?: (() => string) | undefined,\n ) {\n this.statePath = path.join(paths.configDir, 'sync-state.json');\n // Settings belong to the resolved profile. `configDir` is already pinned\n // to that profile, so narrow embedders do not need to implement the named\n // profile resolver and can never silently jump to `default`.\n this.getSettingsConfigPath =\n getSettingsConfigPath ??\n (() => path.join(this.paths.configDir, 'config.json'));\n }\n\n // \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async status(): Promise<string> {\n const cfg = this.getConfig();\n if (!cfg?.enabled) {\n return 'CloudSync: disabled. Run `/sync enable` to activate.';\n }\n const last = this.state?.lastSyncedAt;\n const since = last ? timeAgo(last) : 'never';\n return [\n `CloudSync: enabled`,\n ` repo: ${cfg.repo}`,\n ` categories: ${cfg.categories.join(', ')}`,\n ` last sync: ${since}`,\n ].join('\\n');\n }\n\n async enable(_repo: string, _categories: SyncCategory[]): Promise<string> {\n // Persisted by the slash command via configStore.update.\n return 'Enable via /sync enable.';\n }\n\n async disable(): Promise<string> {\n const cfg = this.getConfig();\n if (!cfg) return 'CloudSync is not configured.';\n const next = { ...cfg, enabled: false };\n await this.setConfig(next);\n return 'CloudSync disabled. Local data kept.';\n }\n\n async push(token: string): Promise<SyncResult> {\n const cfg = this.getConfig();\n if (!cfg?.enabled) return { ok: false, action: 'push', categories: [], message: 'Not enabled.' };\n\n const parts = cfg.repo.split('/');\n const owner = expectDefined(parts[0]);\n const repoName = expectDefined(parts[1]);\n const branch = 'main';\n const remoteHead = await this.getOptionalRef(token, owner, repoName, branch);\n let parentCommitSha = remoteHead?.object?.sha;\n let baseTreeSha: string | undefined;\n let remoteEntries: RemoteTreeEntry[] = [];\n if (parentCommitSha) {\n baseTreeSha =\n parentCommitSha === this.state?.sha && this.state.treeSha\n ? this.state.treeSha\n : (await this.getCommit(token, owner, repoName, parentCommitSha)).tree.sha;\n remoteEntries = await this.getTreeEntries(token, owner, repoName, baseTreeSha);\n }\n\n const { treeEntries: localEntries, rev } = await this.buildLocalTree(cfg.categories);\n let treeEntries = this.buildPushTree(localEntries, remoteEntries, cfg.categories);\n let newTreeSha = await this.createGitTree(token, owner, repoName, treeEntries, baseTreeSha);\n\n let commitSha = await this.createCommit(\n token, owner, repoName, newTreeSha,\n parentCommitSha,\n `Sync ${cfg.categories.join(', ')} \u2014 ${new Date().toISOString()}`,\n );\n\n try {\n if (parentCommitSha) {\n await this.updateRef(token, owner, repoName, branch, commitSha);\n } else {\n await this.createRef(token, owner, repoName, branch, commitSha);\n }\n } catch (err) {\n // 422 = not a fast forward \u2014 remote branch moved. Fetch latest SHA and retry.\n if (err instanceof Error && err.message.includes('422')) {\n const remote = await this.getRef(token, owner, repoName, branch);\n parentCommitSha = remote.object.sha;\n baseTreeSha = (await this.getCommit(token, owner, repoName, parentCommitSha)).tree.sha;\n // Rebuild on the latest remote tree so concurrent files/categories are\n // preserved and deletions are recalculated after the conflict.\n remoteEntries = await this.getTreeEntries(token, owner, repoName, baseTreeSha);\n treeEntries = this.buildPushTree(localEntries, remoteEntries, cfg.categories);\n newTreeSha = await this.createGitTree(token, owner, repoName, treeEntries, baseTreeSha);\n commitSha = await this.createCommit(\n token, owner, repoName, newTreeSha,\n parentCommitSha,\n `Sync ${cfg.categories.join(', ')} \u2014 ${new Date().toISOString()}`,\n );\n await this.updateRef(token, owner, repoName, branch, commitSha);\n } else {\n throw err;\n }\n }\n\n const syncState: SyncStateFile = {\n version: 1,\n sha: commitSha,\n treeSha: newTreeSha,\n lastSyncedAt: new Date().toISOString(),\n localRev: rev,\n };\n await atomicWrite(this.statePath, JSON.stringify(syncState, null, 2));\n this.state = syncState;\n\n return {\n ok: true,\n action: 'push',\n categories: cfg.categories,\n committedAt: commitSha,\n message: `Pushed ${cfg.categories.join(', ')} to ${cfg.repo}. Commit: ${commitSha.slice(0, 7)}`,\n };\n }\n\n async pull(token: string): Promise<SyncResult> {\n const cfg = this.getConfig();\n if (!cfg?.enabled) return { ok: false, action: 'pull', categories: [], message: 'Not enabled.' };\n\n const pullParts = cfg.repo.split('/');\n const owner = expectDefined(pullParts[0]);\n const repoName = expectDefined(pullParts[1]);\n\n const branchData = await this.getRef(token, owner, repoName, 'main');\n const currentSha = branchData.object.sha;\n\n const commitData = await this.getCommit(token, owner, repoName, currentSha);\n const treeSha = commitData.tree.sha;\n\n const treeEntries = await this.getTreeEntries(token, owner, repoName, treeSha);\n\n for (const entry of treeEntries) {\n if (entry.type !== 'blob') continue;\n\n // Paths look like \"data/{category}/...\" \u2014 extract the category\n const segments = entry.path.split('/');\n if (segments[0] !== 'data' || !segments[1]) continue;\n const cat = segments[1] as SyncCategory;\n if (!ALL_SYNC_CATEGORIES.includes(cat) || !cfg.categories.includes(cat)) continue;\n\n const localPath = this.categoryToPath(cat);\n if (!localPath) continue;\n\n // Reconstruct relative path under the category dir. Remote trees are\n // untrusted input: a compromised sync repo could contain paths like\n // data/skills/../../config.json. Keep every write inside the selected\n // category root, and only allow subpaths for directory-backed categories.\n const rel = segments.slice(2).join('/');\n const destPath = resolvePulledCategoryPath(cat, localPath, rel, entry.path);\n\n await preparePulledDestination(cat, localPath, destPath, entry.path);\n const blobData = await this.getBlob(token, owner, repoName, entry.sha);\n // Atomic write: pulled blobs land on live files (config.json, memory,\n // history). A bare writeFile truncated by a crash/ENOSPC mid-write would\n // corrupt the live file; atomicWrite writes a temp + fsync + rename.\n await atomicWrite(destPath, Buffer.from(blobData, 'base64'));\n }\n\n const localRev = await this.hashLocalCategories(cfg.categories);\n const syncState: SyncStateFile = {\n version: 1,\n sha: currentSha,\n treeSha,\n lastSyncedAt: new Date().toISOString(),\n localRev,\n };\n await atomicWrite(this.statePath, JSON.stringify(syncState, null, 2));\n this.state = syncState;\n\n return {\n ok: true,\n action: 'pull',\n categories: cfg.categories,\n committedAt: currentSha,\n message: `Pulled ${cfg.categories.join(', ')} from ${cfg.repo}. Commit: ${currentSha.slice(0, 7)}`,\n };\n }\n\n async hasLocalChanges(): Promise<boolean> {\n if (!this.state) return true;\n const cfg = this.getConfig();\n if (!cfg) return true;\n const current = await this.hashLocalCategories(cfg.categories);\n return current !== this.state.localRev;\n }\n\n async loadState(): Promise<void> {\n try {\n const raw = await fs.readFile(this.statePath, 'utf8');\n this.state = JSON.parse(raw) as SyncStateFile;\n } catch {\n this.state = null;\n }\n }\n\n // \u2500\u2500 GitHub API helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async githubFetch(\n token: string,\n owner: string,\n repo: string,\n method: 'GET' | 'POST' | 'PUT' | 'PATCH',\n pathSegment: string,\n body?: unknown | undefined,\n ): Promise<unknown> {\n const url = `https://api.github.com/repos/${owner}/${repo}${pathSegment}`;\n const init: RequestInit = {\n signal: AbortSignal.timeout(15_000),\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n 'Content-Type': 'application/json',\n },\n };\n if (body !== undefined) init.body = JSON.stringify(body);\n const res = await fetch(url, init);\n\n if (!res.ok) {\n const errText = await res.text();\n throw new WrongStackError({\n message: `GitHub API ${method} ${pathSegment} failed (${res.status}): ${errText}`,\n code: ERROR_CODES.UNKNOWN,\n subsystem: 'general',\n context: { method, pathSegment, status: res.status, repo: `${owner}/${repo}` },\n });\n }\n\n const text = await res.text();\n return text ? JSON.parse(text) : {};\n }\n\n private async getRef(token: string, owner: string, repo: string, ref: string) {\n return (await this.githubFetch(token, owner, repo, 'GET', `/git/refs/heads/${ref}`)) as {\n object: { sha: string };\n };\n }\n\n private async getOptionalRef(token: string, owner: string, repo: string, ref: string) {\n try {\n return await this.getRef(token, owner, repo, ref);\n } catch (err) {\n if (err instanceof Error && err.message.includes('failed (404)')) return null;\n if (err instanceof Error && err.message.includes('failed (409)')) {\n throw new WrongStackError({\n message: 'GitHub repository is empty. Initialize it with a README or first commit, then retry `/sync push`.',\n code: ERROR_CODES.UNKNOWN,\n subsystem: 'general',\n context: { repo: `${owner}/${repo}`, ref },\n cause: err,\n });\n }\n throw err;\n }\n }\n\n private async createRef(token: string, owner: string, repo: string, ref: string, sha: string) {\n await this.githubFetch(token, owner, repo, 'POST', '/git/refs', {\n ref: `refs/heads/${ref}`,\n sha,\n });\n }\n\n private async updateRef(token: string, owner: string, repo: string, ref: string, sha: string) {\n await this.githubFetch(token, owner, repo, 'PATCH', `/git/refs/heads/${ref}`, {\n sha,\n force: false,\n });\n }\n\n private async getCommit(token: string, owner: string, repo: string, sha: string) {\n return (await this.githubFetch(token, owner, repo, 'GET', `/git/commits/${sha}`)) as {\n tree: { sha: string };\n message: string;\n };\n }\n\n private async getTreeEntries(\n token: string,\n owner: string,\n repo: string,\n treeSha: string,\n ): Promise<RemoteTreeEntry[]> {\n // GitHub returns `{ sha, tree: [...], truncated }`, NOT a bare array \u2014 the\n // previous cast made `for (const entry of ...)` throw \"not iterable\", so\n // pull never actually ran. Unwrap `.tree`, and refuse a truncated result\n // rather than silently syncing a partial file set.\n const res = (await this.githubFetch(\n token,\n owner,\n repo,\n 'GET',\n `/git/trees/${treeSha}?recursive=1`,\n )) as {\n tree?: Array<{ path: string; sha: string; type: 'blob' | 'tree' }>;\n truncated?: boolean;\n };\n if (res.truncated) {\n throw new WrongStackError({\n message:\n 'Sync tree is too large \u2014 GitHub truncated the result; refusing a partial pull.',\n code: ERROR_CODES.UNKNOWN,\n subsystem: 'general',\n });\n }\n return res.tree ?? [];\n }\n\n private async createCommit(\n token: string, owner: string, repo: string,\n treeSha: string, parentSha?: string | undefined, message = 'sync',\n ) {\n const body: Record<string, unknown> = { message, tree: treeSha };\n if (parentSha) body.parents = [parentSha];\n const result = (await this.githubFetch(token, owner, repo, 'POST', '/git/commits', body)) as { sha: string };\n return result.sha;\n }\n\n private async createGitTree(\n token: string, owner: string, repo: string,\n entries: GitTreeEntry[],\n baseTreeSha?: string | undefined,\n ): Promise<string> {\n const tree = entries.map((entry) =>\n 'sha' in entry\n ? { path: entry.path, mode: entry.mode, type: 'blob', sha: entry.sha }\n : { path: entry.path, mode: entry.mode, type: 'blob', content: entry.content },\n );\n const body: Record<string, unknown> = { tree };\n if (baseTreeSha) body.base_tree = baseTreeSha;\n const result = (await this.githubFetch(token, owner, repo, 'POST', '/git/trees', body)) as { sha: string };\n return result.sha;\n }\n\n private async getBlob(token: string, owner: string, repo: string, sha: string): Promise<string> {\n const result = (await this.githubFetch(token, owner, repo, 'GET', `/git/blobs/${sha}`)) as { content: string };\n return result.content;\n }\n\n // \u2500\u2500 Local file helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private buildPushTree(\n localEntries: LocalTreeEntry[],\n remoteEntries: RemoteTreeEntry[],\n categories: SyncCategory[],\n ): GitTreeEntry[] {\n const selected = new Set(categories.map((category) => `data/${category}`));\n const localPaths = new Set(localEntries.map((entry) => entry.path));\n const deletions: GitTreeEntry[] = [];\n for (const entry of remoteEntries) {\n if (entry.type !== 'blob' || localPaths.has(entry.path)) continue;\n const selectedCategory = [...selected].some(\n (prefix) => entry.path === prefix || entry.path.startsWith(`${prefix}/`),\n );\n if (selectedCategory) deletions.push({ path: entry.path, sha: null, mode: '100644' });\n }\n return [...localEntries, ...deletions];\n }\n\n private async buildLocalTree(categories: SyncCategory[]): Promise<{\n treeEntries: LocalTreeEntry[];\n rev: string;\n }> {\n const entries: Array<{ path: string; content: string; mode: string }> = [];\n const hashes: string[] = [];\n\n for (const cat of categories) {\n const localPath = this.categoryToPath(cat);\n if (!localPath) continue;\n try {\n const stat = await fs.lstat(localPath);\n if (stat.isSymbolicLink()) continue;\n if (stat.isDirectory()) {\n const files = await this.walkDir(localPath, localPath);\n for (const file of files) {\n const content = await fs.readFile(file, 'utf8');\n const rel = path.relative(localPath, file).replace(/\\\\/g, '/');\n entries.push({ path: `data/${cat}/${rel}`, content, mode: '100644' });\n hashes.push(`${cat}/${rel}\\0${content}`);\n }\n } else {\n const content = await fs.readFile(localPath, 'utf8');\n entries.push({ path: `data/${cat}`, content, mode: '100644' });\n hashes.push(`${cat}\\0${content}`);\n }\n } catch {\n // skip missing files/dirs\n }\n }\n\n const rev = createHash('sha256').update(hashes.join('')).digest('hex').slice(0, 12);\n return { treeEntries: entries, rev };\n }\n\n private async hashLocalCategories(categories: SyncCategory[]): Promise<string> {\n const hashes: string[] = [];\n for (const cat of categories) {\n const localPath = this.categoryToPath(cat);\n if (!localPath) continue;\n try {\n const stat = await fs.lstat(localPath);\n if (stat.isSymbolicLink()) continue;\n if (stat.isDirectory()) {\n const files = await this.walkDir(localPath, localPath);\n for (const file of files) {\n const content = await fs.readFile(file, 'utf8');\n const rel = path.relative(localPath, file).replace(/\\\\/g, '/');\n hashes.push(`${cat}/${rel}\\0${content}`);\n }\n } else {\n const content = await fs.readFile(localPath, 'utf8');\n hashes.push(`${cat}\\0${content}`);\n }\n } catch {\n // skip\n }\n }\n return createHash('sha256').update(hashes.join('')).digest('hex').slice(0, 12);\n }\n\n private categoryToPath(cat: SyncCategory): string | null {\n switch (cat) {\n case 'settings': return this.getSettingsConfigPath();\n case 'skills': return this.paths.globalSkills;\n case 'prompts': return this.paths.globalPrompts;\n case 'memory': return this.paths.globalMemory;\n case 'history': return this.paths.historyFile;\n /* v8 ignore next -- unreachable: SyncCategory is exhaustively matched above */\n default: return null;\n }\n }\n\n private async walkDir(dir: string, base: string): Promise<string[]> {\n const results: string[] = [];\n const entries = await fs.readdir(dir, { withFileTypes: true });\n entries.sort((a, b) => a.name.localeCompare(b.name));\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isSymbolicLink()) continue;\n if (entry.isDirectory()) {\n results.push(...(await this.walkDir(full, base)));\n } else if (entry.isFile()) {\n results.push(full);\n }\n }\n return results;\n }\n}\n\nasync function preparePulledDestination(\n cat: SyncCategory,\n localPath: string,\n destPath: string,\n remotePath: string,\n): Promise<void> {\n const directoryBacked = cat === 'skills' || cat === 'prompts';\n const rootPath = directoryBacked ? localPath : path.dirname(localPath);\n const rootStat = await lstatIfExists(rootPath);\n if (rootStat?.isSymbolicLink()) {\n throw unsafePulledSymlinkError(remotePath, rootPath);\n }\n\n if (directoryBacked) {\n const relativeParent = path.relative(rootPath, path.dirname(destPath));\n let cursor = rootPath;\n if (relativeParent) {\n for (const segment of relativeParent.split(path.sep)) {\n cursor = path.join(cursor, segment);\n const stat = await lstatIfExists(cursor);\n if (stat?.isSymbolicLink()) throw unsafePulledSymlinkError(remotePath, cursor);\n }\n }\n }\n\n const destStat = await lstatIfExists(destPath);\n if (destStat?.isSymbolicLink()) throw unsafePulledSymlinkError(remotePath, destPath);\n await fs.mkdir(path.dirname(destPath), { recursive: true });\n}\n\nasync function lstatIfExists(filePath: string): Promise<Awaited<ReturnType<typeof fs.lstat>> | null> {\n try {\n return await fs.lstat(filePath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n}\n\nfunction unsafePulledSymlinkError(remotePath: string, symlinkPath: string): FsError {\n return new FsError({\n message: `Refusing CloudSync write through symlink: ${remotePath}`,\n code: ERROR_CODES.FS_DELETE_FAILED,\n path: remotePath,\n context: { reason: 'symlink_destination', symlinkPath },\n });\n}\n\nfunction resolvePulledCategoryPath(\n cat: SyncCategory,\n localPath: string,\n rel: string,\n remotePath: string,\n): string {\n const directoryBacked = cat === 'skills' || cat === 'prompts';\n if (!directoryBacked) {\n if (rel) throw new FsError({\n message: `Refusing nested CloudSync path for file category: ${remotePath}`,\n code: ERROR_CODES.FS_DELETE_FAILED,\n path: remotePath,\n context: { reason: 'nested_file_category', category: cat },\n });\n return localPath;\n }\n\n if (!rel) return localPath;\n const normalizedRel = path.normalize(rel);\n const traversesUp = normalizedRel === '..' || normalizedRel.startsWith(`..${path.sep}`);\n if (path.isAbsolute(normalizedRel) || traversesUp) {\n throw new FsError({\n message: `Refusing CloudSync path traversal: ${remotePath}`,\n code: ERROR_CODES.FS_DELETE_FAILED,\n path: remotePath,\n context: { reason: 'path_traversal', normalizedRel },\n });\n }\n\n const dest = path.resolve(localPath, normalizedRel);\n const root = path.resolve(localPath);\n const relative = path.relative(root, dest);\n // On POSIX, a backslash is not a path separator, so an input such as\n // `..\\\\secret` reaches this containment check even though the first guard\n // rejects ordinary `../secret` traversal.\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new FsError({\n message: `Refusing CloudSync path outside category root: ${remotePath}`,\n code: ERROR_CODES.FS_DELETE_FAILED,\n path: remotePath,\n context: { reason: 'outside_category_root', category: cat },\n });\n }\n return dest;\n}\n\nfunction timeAgo(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60_000);\n if (mins < 1) return 'just now';\n if (mins < 60) return `${mins}m ago`;\n const hrs = Math.floor(mins / 60);\n if (hrs < 24) return `${hrs}h ago`;\n const days = Math.floor(hrs / 24);\n return `${days}d ago`;\n}\n", "import type { SessionEvent, SessionWriter } from '../types/session.js';\n\nexport type AuditLevel = 'minimal' | 'standard' | 'full';\n\n/**\n * Configuration for sampling high-volume events inside the bridge.\n * This allows callers (CLI, TUI, WebUI, plugins) to tune how aggressively\n * noisy events like tool progress are persisted.\n */\nexport interface ToolProgressSamplingOptions {\n /**\n * How often to persist 'log' and 'partial_output' progress events.\n * - 1 = every message (no sampling)\n * - 8 = keep the first message + every 8th after that (default)\n */\n sampleRate?: number | undefined;\n}\n\nexport interface SessionSamplingOptions {\n /** Controls sampling behavior for `tool_progress` events (only relevant at auditLevel 'full'). */\n toolProgress?: ToolProgressSamplingOptions | undefined;\n}\n\nexport interface SessionEventBridgeOptions {\n /** Sampling rules for high-volume audit events. */\n sampling?: SessionSamplingOptions | undefined;\n}\n\n/**\n * Small, safe helper that wraps a SessionWriter and enforces the\n * configured auditLevel.\n *\n * All appends are best-effort. Failures are swallowed (with optional\n * diagnostics) so they never crash the agent loop.\n */\nexport interface SessionEventBridge {\n /** Append an event if allowed by the current audit level. */\n append(event: SessionEvent): Promise<void>;\n /** Batch-append events allowed by the current audit level. */\n appendBatch(events: SessionEvent[]): Promise<void>;\n\n /** Current audit level. Reflects the latest {@link setAuditLevel} value. */\n readonly level: AuditLevel;\n\n /**\n * Change the audit level on a live bridge. Subsequent appends are filtered\n * by the new level \u2014 used by the TUI `/settings` picker to apply an\n * `auditLevel` change to the running session without a restart.\n */\n setAuditLevel(level: AuditLevel): void;\n\n /** Returns true if an event of this type should be written at the current level. */\n allows(type: SessionEvent['type']): boolean;\n}\n\n/** Core events that are always written regardless of auditLevel. */\nconst CORE_RECONSTRUCT_EVENTS = new Set<SessionEvent['type']>([\n 'session_start',\n 'session_resumed',\n 'session_forked',\n 'user_input',\n 'llm_response',\n 'tool_result',\n 'message_appended',\n 'message_updated',\n 'messages_replaced',\n 'context_snapshot',\n 'checkpoint',\n 'file_snapshot',\n 'file_observation',\n 'rewound',\n 'in_flight_start',\n 'in_flight_end',\n 'session_end',\n]);\n\n/**\n * Events that are considered \"standard\" audit detail.\n * These are lightweight and high-value for forensics.\n */\nconst STANDARD_AUDIT_EVENTS = new Set<SessionEvent['type']>([\n 'llm_request',\n 'tool_use',\n 'tool_call_start',\n 'tool_call_end',\n 'compaction',\n 'error',\n 'message_truncated',\n 'provider_retry',\n 'provider_error',\n 'skill_activated',\n 'skill_deactivated',\n // Resume markers and fleet lifecycle. These reach disk today only because\n // every producer appends to the raw SessionWriter and bypasses this bridge \u2014\n // routed through it while absent from both sets, `isAllowed` would drop them\n // at the DEFAULT level and the resume timeline would lose them silently.\n 'mode_changed',\n 'agent_spawned',\n 'agent_stopped',\n 'agent_error',\n 'task_created',\n 'task_updated',\n 'task_completed',\n 'task_failed',\n 'side_effect',\n]);\n\n/**\n * Events that are only allowed at 'full' audit level because they can be\n * very high volume (e.g. streaming tool output).\n */\nconst FULL_ONLY_EVENTS = new Set<SessionEvent['type']>(['tool_progress']);\n\n/**\n * \"full\" level allows everything (including potentially heavy events\n * that plugins or future code may emit).\n */\nfunction isAllowed(type: SessionEvent['type'], level: AuditLevel): boolean {\n if (CORE_RECONSTRUCT_EVENTS.has(type)) return true;\n if (level === 'minimal') return false;\n\n if (STANDARD_AUDIT_EVENTS.has(type)) return true;\n if (level === 'standard') return false;\n\n // 'full' level events (high volume)\n if (FULL_ONLY_EVENTS.has(type)) {\n return level === 'full';\n }\n\n // 'full' \u2014 allow everything else\n return true;\n}\n\n/**\n * Create a safe, audit-level-aware bridge around a SessionWriter.\n *\n * The bridge can also apply sampling for high-volume events (e.g. `tool_progress`)\n * when `auditLevel` is set to `'full'`.\n *\n * @example\n * const bridge = createSessionEventBridge(sessionWriter, 'full', {\n * sampling: {\n * toolProgress: { sampleRate: 5 } // more aggressive sampling\n * }\n * });\n */\nexport function createSessionEventBridge(\n writer: SessionWriter | (() => SessionWriter | undefined | null) | undefined | null,\n level: AuditLevel = 'standard',\n options: SessionEventBridgeOptions = {},\n): SessionEventBridge {\n // Mutable so setAuditLevel() can re-tune a live bridge in place.\n let currentLevel: AuditLevel = level ?? 'standard';\n\n // Accept either a writer instance or a getter. A getter lets long-lived\n // hosts (CLI/TUI/WebUI) resolve the CURRENT writer on every append \u2014 when\n // the user resumes another session mid-run, audit events follow the swap\n // instead of being silently dropped into the old, closed writer.\n const resolveWriter: () => SessionWriter | undefined | null =\n typeof writer === 'function' ? writer : () => writer;\n\n // Internal sampling state for high-volume events (e.g. tool_progress).\n // Keyed by tool call id (or name as fallback) to keep sampling per-call.\n const progressCounters = new Map<string, number>();\n\n const toolProgressConfig = options.sampling?.toolProgress ?? {};\n const TOOL_PROGRESS_SAMPLE_RATE = toolProgressConfig.sampleRate ?? 8;\n\n /**\n * Decide whether a high-volume event should be sampled in.\n * Currently only implements sampling for 'tool_progress'.\n */\n function shouldSample(event: SessionEvent): boolean {\n if (event.type !== 'tool_progress') return true;\n\n const progEvent = event as Extract<SessionEvent, { type: 'tool_progress' }>;\n const innerType = progEvent.event?.type;\n\n // Always let through high-signal structured events\n if (innerType === 'warning' || innerType === 'metric' || innerType === 'file_changed') {\n return true;\n }\n\n // Sample noisy text streams (log / partial_output)\n if (innerType === 'log' || innerType === 'partial_output') {\n const key = progEvent.id || progEvent.name;\n const count = (progressCounters.get(key) || 0) + 1;\n progressCounters.set(key, count);\n\n // Always keep the first message + every Nth after that\n return count === 1 || count % TOOL_PROGRESS_SAMPLE_RATE === 0;\n }\n\n return true;\n }\n\n return {\n get level() {\n return currentLevel;\n },\n\n setAuditLevel(next) {\n currentLevel = next ?? 'standard';\n },\n\n allows(type) {\n return isAllowed(type, currentLevel);\n },\n\n async append(event) {\n const target = resolveWriter();\n if (!target) return;\n if (!isAllowed(event.type, currentLevel)) return;\n\n // Apply sampling for high-volume events (only at 'full' level)\n if (!shouldSample(event)) return;\n\n try {\n await target.append(event);\n } catch (_err) {\n // Best-effort: never let session logging break the agent.\n // The existing FileSessionWriter already does throttled warnings,\n // but we keep this wrapper silent by default to avoid log spam.\n // Callers that care can listen to EventBus 'session.damaged' etc.\n }\n },\n\n async appendBatch(events) {\n const target = resolveWriter();\n if (!target || events.length === 0) return;\n const allowed = events.filter((e) => isAllowed(e.type, currentLevel) && shouldSample(e));\n if (allowed.length === 0) return;\n try {\n await target.appendBatch(allowed);\n } catch {\n // best-effort \u2014 same contract as append()\n }\n },\n };\n}\n\n/** Convenience re-export of the allowed core set for tests/docs. */\nexport { CORE_RECONSTRUCT_EVENTS, STANDARD_AUDIT_EVENTS };\n\n/**\n * Safely extract the auditLevel from a (possibly partial) Config object.\n * Falls back to 'standard' if not present or invalid.\n */\nexport function resolveAuditLevel(\n cfg?: { session?: { auditLevel?: AuditLevel | undefined } | undefined } | null,\n): AuditLevel {\n const raw = cfg?.session?.auditLevel;\n if (raw === 'minimal' || raw === 'standard' || raw === 'full') {\n return raw;\n }\n return 'standard';\n}\n\n/**\n * Fully resolves the session logging configuration with sensible defaults.\n * This is the recommended way for the CLI / TUI / WebUI to read session config.\n */\nexport function resolveSessionLoggingConfig(\n cfg?: {\n session?: {\n auditLevel?: AuditLevel | undefined;\n sampling?: {\n toolProgress?: { sampleRate?: number | undefined };\n };\n };\n } | null,\n): {\n auditLevel: AuditLevel;\n sampling: {\n toolProgress: { sampleRate: number };\n };\n} {\n const session = cfg?.session ?? {};\n\n const auditLevel = resolveAuditLevel(cfg);\n\n const toolProgressSampleRate = session.sampling?.toolProgress?.sampleRate ?? 8;\n\n return {\n auditLevel,\n sampling: {\n toolProgress: {\n sampleRate: Math.max(1, Math.floor(toolProgressSampleRate)),\n },\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["fsp", "path", "open", "path", "path", "fs", "path", "slugify", "projectHash", "fsp", "path", "createHash", "fsp", "path", "createHash", "relative", "stat", "resolve", "path", "createHash", "fsp", "path", "isInside", "relative", "stat", "createHash", "fsp", "fsp", "path", "fsp", "path", "path", "stat", "fsp", "path", "stat", "createHash", "createHash", "createReadStream", "createInterface", "createReadStream", "createInterface", "createReadStream", "createInterface", "createReadStream", "createInterface", "fsp", "stat", "fsp", "fsp", "stat", "stat", "summaries", "fsp", "path", "stat", "randomBytes", "fsp", "path", "randomBytes", "randomUUID", "fs", "path", "stat", "randomUUID", "fs", "path", "relative", "fs", "path", "fs", "path", "path", "deepMerge", "stat", "fsp", "os", "path", "matched", "randomUUID", "fs", "randomUUID", "fs", "path", "createHash", "storageErrorString", "FILE_VERSION", "offset", "stat", "hasNonWhitespace", "createReadStream", "fs", "path", "createInterface", "stat", "createReadStream", "createInterface", "CHUNK_SIZE", "boundary", "createHash", "randomUUID", "fs", "randomUUID", "createHash", "stableStringify", "sortKeys", "fs", "path", "randomUUID", "fs", "path", "stat", "randomUUID", "resolve", "projectSlug", "fs", "path", "pidAlive", "createReadStream", "fsp", "path", "createInterface", "createReadStream", "createInterface", "fsp", "fsp", "fsp", "randomUUID", "randomUUID", "fsp", "fsp", "hostname", "lock", "hostname", "fsp", "createHash", "fs", "path", "createHash", "fs", "resolve", "stat", "fs", "path", "fs", "path", "createHash", "stat", "createHash", "relative"]
|
|
7
|
-
}
|