@cortexkit/opencode-magic-context 0.41.3 → 0.42.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/README.md +21 -3
- package/dist/agents/hidden-agent-registrations.d.ts +1 -3
- package/dist/agents/hidden-agent-registrations.d.ts.map +1 -1
- package/dist/agents/magic-context-prompt.d.ts +1 -1
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/agents/permissions.d.ts +1 -22
- package/dist/agents/permissions.d.ts.map +1 -1
- package/dist/config/agent-disable.d.ts +0 -5
- package/dist/config/agent-disable.d.ts.map +1 -1
- package/dist/config/index.d.ts +14 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/migrate-config-location.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +17 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/removed-agent-config.d.ts +8 -0
- package/dist/config/removed-agent-config.d.ts.map +1 -0
- package/dist/config/schema/magic-context.d.ts +23 -201
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-z5sd697e.js → conflict-warning-hook-6p3xqeac.js} +4 -4
- package/dist/{derive-budgets-901a0pk0.js → derive-budgets-s2pgnnm4.js} +4 -2
- package/dist/features/builtin-commands/commands.d.ts +0 -4
- package/dist/features/builtin-commands/commands.d.ts.map +1 -1
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts +7 -0
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/context-authority.d.ts +31 -4
- package/dist/features/magic-context/context-authority.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/open-opencode-db.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/retrospective-orphan-sweep.d.ts +19 -9
- package/dist/features/magic-context/dreamer/retrospective-orphan-sweep.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +13 -0
- package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts +7 -0
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts.map +1 -1
- package/dist/features/magic-context/index.d.ts +0 -1
- package/dist/features/magic-context/index.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +13 -0
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-synapse.d.ts +43 -10
- package/dist/features/magic-context/memory/embedding-synapse.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts +1 -1
- package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity.d.ts +1 -0
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/memory/promotion.d.ts +7 -1
- package/dist/features/magic-context/memory/promotion.d.ts.map +1 -1
- package/dist/features/magic-context/merged-reasoning-decisions.d.ts +6 -0
- package/dist/features/magic-context/merged-reasoning-decisions.d.ts.map +1 -0
- package/dist/features/magic-context/message-fts-rowid-map.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-async.d.ts +12 -0
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
- package/dist/features/magic-context/mural/render-trigger.d.ts +6 -0
- package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
- package/dist/features/magic-context/no-content-compartment.d.ts +12 -0
- package/dist/features/magic-context/no-content-compartment.d.ts.map +1 -0
- package/dist/features/magic-context/pi-content-decisions.d.ts +9 -0
- package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -0
- package/dist/features/magic-context/project-docs-hash.d.ts +4 -0
- package/dist/features/magic-context/project-docs-hash.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +13 -4
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/protection-window.d.ts +79 -0
- package/dist/features/magic-context/protection-window.d.ts.map +1 -0
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/session-project-backfill.d.ts.map +1 -1
- package/dist/features/magic-context/shadow-backfill-state.d.ts +26 -0
- package/dist/features/magic-context/shadow-backfill-state.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +4 -0
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/storage.d.ts.map +1 -1
- package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-embedding-measurements.d.ts +2 -2
- package/dist/features/magic-context/storage-embedding-measurements.d.ts.map +1 -1
- package/dist/features/magic-context/storage-historian-runs.d.ts +4 -0
- package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
- package/dist/features/magic-context/storage-memory-mutation-log.d.ts +1 -0
- package/dist/features/magic-context/storage-memory-mutation-log.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +81 -15
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage-native-replay.d.ts +29 -0
- package/dist/features/magic-context/storage-native-replay.d.ts.map +1 -0
- package/dist/features/magic-context/storage-notes.d.ts +6 -0
- package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
- package/dist/features/magic-context/storage-replay-document.d.ts +33 -0
- package/dist/features/magic-context/storage-replay-document.d.ts.map +1 -0
- package/dist/features/magic-context/storage-subagent-invocations.d.ts +1 -1
- package/dist/features/magic-context/storage-subagent-invocations.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +19 -11
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +3 -3
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -1
- package/dist/features/magic-context/tagger.d.ts +13 -0
- package/dist/features/magic-context/tagger.d.ts.map +1 -1
- package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
- package/dist/features/magic-context/workspaces.d.ts.map +1 -1
- package/dist/hooks/magic-context/apply-operations.d.ts +11 -5
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +9 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -1
- package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/child-session-spawn.d.ts +3 -3
- package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts +5 -10
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +10 -21
- package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +4 -0
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +26 -2
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
- package/dist/hooks/magic-context/decay-render.d.ts.map +1 -1
- package/dist/hooks/magic-context/derive-budgets.d.ts +1 -0
- package/dist/hooks/magic-context/derive-budgets.d.ts.map +1 -1
- package/dist/hooks/magic-context/dropped-input-guard.d.ts +9 -0
- package/dist/hooks/magic-context/dropped-input-guard.d.ts.map +1 -0
- package/dist/hooks/magic-context/emergency-drop.d.ts +9 -2
- package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts +0 -1
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-payloads.d.ts +7 -6
- package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/execute-status.d.ts +8 -2
- package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +11 -4
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts +5 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +17 -5
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +14 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/issue-423-fixture.d.ts +37 -0
- package/dist/hooks/magic-context/issue-423-fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/issue-424-fixture.d.ts +11 -0
- package/dist/hooks/magic-context/issue-424-fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/live-session-state.d.ts +1 -1
- package/dist/hooks/magic-context/lkg-slot.d.ts +34 -0
- package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-state-sync-timing.d.ts +21 -0
- package/dist/hooks/magic-context/module-state-sync-timing.d.ts.map +1 -0
- package/dist/hooks/magic-context/module-state-sync.d.ts +21 -7
- package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-transport.d.ts +12 -0
- package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-wire.d.ts +8 -0
- package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
- package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
- package/dist/hooks/magic-context/persist-filtered-noise.d.ts +5 -0
- package/dist/hooks/magic-context/persist-filtered-noise.d.ts.map +1 -0
- package/dist/hooks/magic-context/producer-window-guard.d.ts +8 -0
- package/dist/hooks/magic-context/producer-window-guard.d.ts.map +1 -0
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts +65 -0
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +20 -18
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts +22 -12
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-formatting.d.ts +8 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-raw.d.ts +10 -1
- package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts +4 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts +2 -0
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
- package/dist/hooks/magic-context/reference-retrieval.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +79 -11
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/send-session-notification.d.ts +11 -10
- package/dist/hooks/magic-context/send-session-notification.d.ts.map +1 -1
- package/dist/hooks/magic-context/sentinel.d.ts +3 -3
- package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +7 -0
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/supersession-reclaim.d.ts +13 -0
- package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts +2 -3
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +2 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +16 -6
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +1 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-context-state.d.ts +7 -1
- package/dist/hooks/magic-context/transform-context-state.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +11 -4
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +11 -2
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/upgrade-reminder.d.ts +7 -9
- package/dist/hooks/magic-context/upgrade-reminder.d.ts.map +1 -1
- package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
- package/dist/index-1974jvn3.js +783 -0
- package/dist/{index-npx8vhmy.js → index-36wd1d6k.js} +7 -30
- package/dist/{index-kby1sng5.js → index-avwt563y.js} +8 -1
- package/dist/{index-vmmjdpc0.js → index-c9hamh36.js} +18357 -16010
- package/dist/{index-0bycg0np.js → index-jmtjh2d3.js} +4 -4
- package/dist/{index-q264erdp.js → index-nqgjwfzn.js} +70 -38
- package/dist/index-sd3egckz.js +1583 -0
- package/dist/{index-dn1rwpd8.js → index-xdtge3y2.js} +58 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6826 -6660
- package/dist/plugin/conflict-warning-hook.d.ts +5 -6
- package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
- package/dist/plugin/dream-timer.d.ts +1 -0
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/embedding-routing.d.ts +4 -1
- package/dist/plugin/embedding-routing.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +19 -2
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +31 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/rust-tool-backends.d.ts +1 -0
- package/dist/plugin/rust-tool-backends.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/{recomp-orchestrator-f5m2wk74.js → recomp-orchestrator-3atsasjy.js} +6 -6
- package/dist/{send-session-notification-hdh02wy9.js → send-session-notification-1qeqkswb.js} +6 -4
- package/dist/shared/cache-ttl-display.d.ts +23 -0
- package/dist/shared/cache-ttl-display.d.ts.map +1 -0
- package/dist/shared/cache-ttl-seed.d.ts +10 -0
- package/dist/shared/cache-ttl-seed.d.ts.map +1 -0
- package/dist/shared/child-session-teardown.d.ts +34 -0
- package/dist/shared/child-session-teardown.d.ts.map +1 -0
- package/dist/shared/config-diagnostics.d.ts +30 -0
- package/dist/shared/config-diagnostics.d.ts.map +1 -0
- package/dist/shared/config-warning-surface.d.ts +3 -0
- package/dist/shared/config-warning-surface.d.ts.map +1 -0
- package/dist/shared/data-path.d.ts +10 -9
- package/dist/shared/data-path.d.ts.map +1 -1
- package/dist/shared/harness-provider-map.d.ts +7 -0
- package/dist/shared/harness-provider-map.d.ts.map +1 -1
- package/dist/shared/jsonc-parser.d.ts +12 -0
- package/dist/shared/jsonc-parser.d.ts.map +1 -1
- package/dist/shared/keep-subagents.d.ts +1 -1
- package/dist/shared/keep-subagents.d.ts.map +1 -1
- package/dist/shared/model-suggestion-retry.d.ts +15 -11
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/opencode-db-path.d.ts +24 -0
- package/dist/shared/opencode-db-path.d.ts.map +1 -0
- package/dist/shared/pi-executable.d.ts +6 -0
- package/dist/shared/pi-executable.d.ts.map +1 -0
- package/dist/shared/prompt-surface-runtime.d.ts +10 -5
- package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
- package/dist/shared/resolve-fallbacks.d.ts +1 -1
- package/dist/shared/rpc-types.d.ts +117 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/shared/sqlite.d.ts +42 -0
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/status-detail-text.d.ts.map +1 -1
- package/dist/shared/subagent-runner.d.ts +17 -10
- package/dist/shared/subagent-runner.d.ts.map +1 -1
- package/dist/shared/tag-transcript.d.ts.map +1 -1
- package/dist/shared/transcript.d.ts +6 -0
- package/dist/shared/transcript.d.ts.map +1 -1
- package/dist/shared/window-geometry.d.ts +23 -0
- package/dist/shared/window-geometry.d.ts.map +1 -1
- package/dist/shared/write-transaction-timing.d.ts +1 -1
- package/dist/shared/write-transaction-timing.d.ts.map +1 -1
- package/dist/{storage-meta-session-3rxx9ekm.js → storage-meta-session-e6g0mmec.js} +2 -2
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-memory/verification-recording.d.ts.map +1 -1
- package/dist/tools/ctx-note/constants.d.ts +1 -1
- package/dist/tools/ctx-note/constants.d.ts.map +1 -1
- package/dist/tools/ctx-note/tools.d.ts.map +1 -1
- package/dist/tools/ctx-note/types.d.ts +1 -0
- package/dist/tools/ctx-note/types.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/tools.d.ts +10 -1
- package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/constants.d.ts +1 -1
- package/dist/tools/ctx-search/constants.d.ts.map +1 -1
- package/dist/tools/light-descriptions.d.ts +2 -2
- package/dist/tools/light-descriptions.d.ts.map +1 -1
- package/package.json +2 -3
- package/src/agents/hidden-agent-registrations.ts +3 -15
- package/src/agents/magic-context-prompt.test.ts +7 -5
- package/src/agents/magic-context-prompt.ts +22 -30
- package/src/agents/permissions.test.ts +1 -42
- package/src/agents/permissions.ts +1 -24
- package/src/config/agent-disable.ts +5 -22
- package/src/config/index.test.ts +169 -14
- package/src/config/index.ts +137 -21
- package/src/config/latch-permanence-guard.test.ts +4 -0
- package/src/config/migrate-config-location.test.ts +39 -6
- package/src/config/migrate-config-location.ts +18 -31
- package/src/config/profiles.ts +1 -1
- package/src/config/project-security.hostile-matrix.test.ts +0 -8
- package/src/config/project-security.test.ts +11 -22
- package/src/config/project-security.ts +103 -9
- package/src/config/protected-tokens.test.ts +235 -0
- package/src/config/removed-agent-config.ts +45 -0
- package/src/config/removed-agent-source-fence.test.ts +56 -0
- package/src/config/schema/magic-context.test.ts +21 -42
- package/src/config/schema/magic-context.ts +46 -37
- package/src/shared/cache-ttl-display.test.ts +84 -0
- package/src/shared/cache-ttl-display.ts +64 -0
- package/src/shared/cache-ttl-seed.ts +23 -0
- package/src/shared/child-session-teardown.test.ts +218 -0
- package/src/shared/child-session-teardown.ts +66 -0
- package/src/shared/config-diagnostics.test.ts +29 -0
- package/src/shared/config-diagnostics.ts +64 -0
- package/src/shared/config-warning-surface.ts +28 -0
- package/src/shared/data-path.ts +10 -9
- package/src/shared/harness-provider-map.test.ts +16 -0
- package/src/shared/harness-provider-map.ts +15 -0
- package/src/shared/jsonc-parser.test.ts +14 -1
- package/src/shared/jsonc-parser.ts +93 -0
- package/src/shared/keep-subagents.ts +9 -14
- package/src/shared/model-suggestion-retry.ts +144 -29
- package/src/shared/opencode-db-path.test.ts +196 -0
- package/src/shared/opencode-db-path.ts +215 -0
- package/src/shared/pi-executable.ts +20 -0
- package/src/shared/prompt-surface-a1-golden.md +50 -35
- package/src/shared/prompt-surface-runtime.test.ts +28 -0
- package/src/shared/prompt-surface-runtime.ts +22 -5
- package/src/shared/resolve-fallbacks.ts +1 -1
- package/src/shared/rpc-types.ts +124 -0
- package/src/shared/rpc-utils.ts +2 -3
- package/src/shared/session-messages-bounded.test.ts +1 -1
- package/src/shared/sqlite-import-fence.test.ts +19 -0
- package/src/shared/sqlite.test.ts +27 -1
- package/src/shared/sqlite.ts +204 -1
- package/src/shared/status-detail-text.test.ts +8 -4
- package/src/shared/status-detail-text.ts +20 -1
- package/src/shared/subagent-runner.ts +17 -10
- package/src/shared/tag-transcript.ts +53 -22
- package/src/shared/transcript.ts +7 -0
- package/src/shared/window-geometry.test.ts +44 -0
- package/src/shared/window-geometry.ts +89 -11
- package/src/shared/write-transaction-attribution.test.ts +241 -0
- package/src/shared/write-transaction-timing.ts +2 -1
- package/src/tui/index.tsx +18 -4
- package/src/tui/slots/sidebar-content.tsx +12 -7
- package/src/tui-compiled/index.tsx +237 -225
- package/src/tui-compiled/slots/sidebar-content.tsx +301 -305
- package/dist/agents/sidekick.d.ts +0 -2
- package/dist/agents/sidekick.d.ts.map +0 -1
- package/dist/features/magic-context/defaults.d.ts +0 -2
- package/dist/features/magic-context/defaults.d.ts.map +0 -1
- package/dist/features/magic-context/sidekick/agent.d.ts +0 -14
- package/dist/features/magic-context/sidekick/agent.d.ts.map +0 -1
- package/dist/features/magic-context/sidekick/core.d.ts +0 -38
- package/dist/features/magic-context/sidekick/core.d.ts.map +0 -1
- package/dist/features/magic-context/sidekick/index.d.ts +0 -3
- package/dist/features/magic-context/sidekick/index.d.ts.map +0 -1
- package/dist/hooks/magic-context/boundary-execution.d.ts +0 -27
- package/dist/hooks/magic-context/boundary-execution.d.ts.map +0 -1
- package/dist/index-jfpk81yt.js +0 -146
- package/dist/index-raatkvw5.js +0 -360
- package/src/agents/sidekick.ts +0 -1
- package/src/features/magic-context/defaults.ts +0 -1
|
@@ -3,6 +3,7 @@ import type { createOpencodeClient } from "@opencode-ai/sdk";
|
|
|
3
3
|
import { detectOverflow } from "../features/magic-context/overflow-detection";
|
|
4
4
|
import { log } from "./logger";
|
|
5
5
|
import type { ModelInput } from "./model-resolution";
|
|
6
|
+
import { sanitizeDiagnosticText } from "./redaction";
|
|
6
7
|
import { parseProviderModel, toModelEntry } from "./resolve-fallbacks";
|
|
7
8
|
|
|
8
9
|
type Client = ReturnType<typeof createOpencodeClient>;
|
|
@@ -70,7 +71,7 @@ export interface PromptRetryOptions {
|
|
|
70
71
|
fallbackModels?: readonly ModelInput[];
|
|
71
72
|
/**
|
|
72
73
|
* Identifier for structured logging (e.g. "dreamer:consolidate",
|
|
73
|
-
* "historian", "compressor", "
|
|
74
|
+
* "historian", "compressor", "dreamer"). Helps correlate fallback
|
|
74
75
|
* attempts to a specific call site in `magic-context.log`. Defaults to
|
|
75
76
|
* "subagent" if not provided.
|
|
76
77
|
*/
|
|
@@ -100,6 +101,33 @@ export interface ValidatedPromptRetryResult<TOutput, TValidated> {
|
|
|
100
101
|
attempt: PromptAttemptInfo;
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
export type PromptFailureClass =
|
|
105
|
+
| "provider_timeout"
|
|
106
|
+
| "provider_error"
|
|
107
|
+
| "empty_completion"
|
|
108
|
+
| "no_models"
|
|
109
|
+
| "child_aborted"
|
|
110
|
+
| "parse_failed"
|
|
111
|
+
| "unknown";
|
|
112
|
+
|
|
113
|
+
/** Diagnostic facts retained when a validated child prompt ultimately fails. */
|
|
114
|
+
export interface PromptFailureDetail {
|
|
115
|
+
failureClass: PromptFailureClass;
|
|
116
|
+
modelAttempted: string | null;
|
|
117
|
+
modelsTried: string[];
|
|
118
|
+
providerError: string | null;
|
|
119
|
+
timeoutMs: number | null;
|
|
120
|
+
childSessionId: string | null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const promptFailureDetails = new WeakMap<object, PromptFailureDetail>();
|
|
124
|
+
|
|
125
|
+
export function getPromptFailureDetail(error: unknown): PromptFailureDetail | null {
|
|
126
|
+
return error !== null && typeof error === "object"
|
|
127
|
+
? (promptFailureDetails.get(error) ?? null)
|
|
128
|
+
: null;
|
|
129
|
+
}
|
|
130
|
+
|
|
103
131
|
export interface ModelSuggestionInfo {
|
|
104
132
|
providerID: string;
|
|
105
133
|
modelID: string;
|
|
@@ -279,6 +307,61 @@ function shortErr(error: unknown): string {
|
|
|
279
307
|
return extractMessage(error);
|
|
280
308
|
}
|
|
281
309
|
|
|
310
|
+
type PromptFailurePhase = "prompt" | "output" | "validation";
|
|
311
|
+
|
|
312
|
+
interface FailedAttempt {
|
|
313
|
+
error: unknown;
|
|
314
|
+
failureClass: PromptFailureClass;
|
|
315
|
+
attempt: PromptAttemptInfo;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function classifyPromptFailure(
|
|
319
|
+
error: unknown,
|
|
320
|
+
phase: PromptFailurePhase,
|
|
321
|
+
externalSignal?: AbortSignal,
|
|
322
|
+
): PromptFailureClass {
|
|
323
|
+
const message = extractMessage(error);
|
|
324
|
+
if (externalSignal?.aborted || message === "prompt aborted by external signal") {
|
|
325
|
+
return "child_aborted";
|
|
326
|
+
}
|
|
327
|
+
if (/^prompt timed out after \d+ms$/.test(message)) return "provider_timeout";
|
|
328
|
+
if (phase === "validation") {
|
|
329
|
+
if (/returned no (?:assistant )?output|no assistant output/i.test(message)) {
|
|
330
|
+
return "empty_completion";
|
|
331
|
+
}
|
|
332
|
+
if (error instanceof Error && error.name === "DreamerProviderOutputFailureError") {
|
|
333
|
+
return "provider_error";
|
|
334
|
+
}
|
|
335
|
+
return "parse_failed";
|
|
336
|
+
}
|
|
337
|
+
return phase === "prompt" || phase === "output" ? "provider_error" : "unknown";
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function throwWithPromptFailure(
|
|
341
|
+
legacyError: unknown,
|
|
342
|
+
failedAttempts: FailedAttempt[],
|
|
343
|
+
args: PromptArgs,
|
|
344
|
+
timeoutMs: number,
|
|
345
|
+
): never {
|
|
346
|
+
const error =
|
|
347
|
+
legacyError instanceof Error ? legacyError : new Error(extractMessage(legacyError));
|
|
348
|
+
const last = failedAttempts.at(-1);
|
|
349
|
+
const failureClass = last?.failureClass ?? "unknown";
|
|
350
|
+
const providerError =
|
|
351
|
+
failureClass === "provider_error"
|
|
352
|
+
? sanitizeDiagnosticText(shortErr(last?.error ?? legacyError)).slice(0, 500)
|
|
353
|
+
: null;
|
|
354
|
+
promptFailureDetails.set(error, {
|
|
355
|
+
failureClass,
|
|
356
|
+
modelAttempted: last?.attempt.label ?? null,
|
|
357
|
+
modelsTried: failedAttempts.map((failure) => failure.attempt.label),
|
|
358
|
+
providerError,
|
|
359
|
+
timeoutMs: failureClass === "provider_timeout" ? timeoutMs : null,
|
|
360
|
+
childSessionId: args.path.id || null,
|
|
361
|
+
});
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
|
|
282
365
|
/**
|
|
283
366
|
* Try a single prompt attempt against the supplied body, with the existing
|
|
284
367
|
* single-suggestion retry layered inside (so "did you mean X?" still self-heals
|
|
@@ -449,23 +532,43 @@ async function attemptAndValidate<TOutput, TValidated>(
|
|
|
449
532
|
attempt: PromptAttemptInfo,
|
|
450
533
|
options: ValidatedPromptRetryOptions<TOutput, TValidated>,
|
|
451
534
|
): Promise<ValidatedPromptRetryResult<TOutput, TValidated>> {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
535
|
+
try {
|
|
536
|
+
await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
throw {
|
|
539
|
+
error,
|
|
540
|
+
failureClass: classifyPromptFailure(error, "prompt", signal),
|
|
541
|
+
attempt,
|
|
542
|
+
} satisfies FailedAttempt;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
let output: TOutput;
|
|
546
|
+
try {
|
|
547
|
+
output = await options.fetchOutput(args, attempt);
|
|
548
|
+
} catch (error) {
|
|
549
|
+
throw {
|
|
550
|
+
error,
|
|
551
|
+
failureClass: classifyPromptFailure(error, "output", signal),
|
|
552
|
+
attempt,
|
|
553
|
+
} satisfies FailedAttempt;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
try {
|
|
557
|
+
const validated = await options.validateOutput(output, attempt);
|
|
558
|
+
return { output, validated, attempt };
|
|
559
|
+
} catch (error) {
|
|
560
|
+
throw {
|
|
561
|
+
error,
|
|
562
|
+
failureClass: classifyPromptFailure(error, "validation", signal),
|
|
563
|
+
attempt,
|
|
564
|
+
} satisfies FailedAttempt;
|
|
565
|
+
}
|
|
456
566
|
}
|
|
457
567
|
|
|
458
568
|
/**
|
|
459
|
-
* Run a prompt
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
* produced no usable assistant text / JSON.
|
|
463
|
-
*
|
|
464
|
-
* The happy path is still one prompt + one caller-owned output fetch: callers
|
|
465
|
-
* should use the returned output instead of fetching messages a second time.
|
|
466
|
-
* Validation failures are retryable across configured fallback models. If every
|
|
467
|
-
* attempt produces invalid output (or otherwise fails retryably), the first
|
|
468
|
-
* failure is re-thrown so callers surface the original failure semantics.
|
|
569
|
+
* Run a prompt across the configured model chain and retain which attempt failed.
|
|
570
|
+
* Output validation remains caller-owned so empty completions and malformed
|
|
571
|
+
* responses can be distinguished from provider transport failures.
|
|
469
572
|
*/
|
|
470
573
|
export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = TOutput>(
|
|
471
574
|
client: Client,
|
|
@@ -486,7 +589,7 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
486
589
|
? `${baseBody.model.providerID}/${baseBody.model.modelID}`
|
|
487
590
|
: "primary";
|
|
488
591
|
const totalAttempts = fallbacks.length + 1;
|
|
489
|
-
|
|
592
|
+
const failedAttempts: FailedAttempt[] = [];
|
|
490
593
|
let firstError: unknown = null;
|
|
491
594
|
let lastError: unknown = null;
|
|
492
595
|
|
|
@@ -506,17 +609,21 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
506
609
|
},
|
|
507
610
|
options,
|
|
508
611
|
);
|
|
509
|
-
} catch (
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
612
|
+
} catch (caught) {
|
|
613
|
+
const failure = caught as FailedAttempt;
|
|
614
|
+
failedAttempts.push(failure);
|
|
615
|
+
firstError = failure.error;
|
|
616
|
+
lastError = failure.error;
|
|
617
|
+
if (isNonRetryable(failure.error, options.signal)) {
|
|
618
|
+
throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
|
|
619
|
+
}
|
|
513
620
|
|
|
514
621
|
if (fallbacks.length === 0) {
|
|
515
|
-
|
|
622
|
+
throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
|
|
516
623
|
}
|
|
517
624
|
|
|
518
625
|
log(
|
|
519
|
-
`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`,
|
|
626
|
+
`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(failure.error)}; trying ${fallbacks.length} fallback(s)`,
|
|
520
627
|
);
|
|
521
628
|
}
|
|
522
629
|
|
|
@@ -557,22 +664,30 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
557
664
|
`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`,
|
|
558
665
|
);
|
|
559
666
|
return result;
|
|
560
|
-
} catch (
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
667
|
+
} catch (caught) {
|
|
668
|
+
const failure = caught as FailedAttempt;
|
|
669
|
+
failedAttempts.push(failure);
|
|
670
|
+
lastError = failure.error;
|
|
671
|
+
if (isNonRetryable(failure.error, options.signal)) {
|
|
672
|
+
throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
|
|
673
|
+
}
|
|
564
674
|
|
|
565
675
|
const remaining = fallbacks.length - i - 1;
|
|
566
676
|
if (remaining > 0) {
|
|
567
677
|
log(
|
|
568
|
-
`[${callContext}] ${label} failed validation/prompt: ${shortErr(error)}; ${remaining} fallback(s) left`,
|
|
678
|
+
`[${callContext}] ${label} failed validation/prompt: ${shortErr(failure.error)}; ${remaining} fallback(s) left`,
|
|
569
679
|
);
|
|
570
680
|
}
|
|
571
681
|
}
|
|
572
682
|
}
|
|
573
683
|
|
|
574
684
|
log(
|
|
575
|
-
`[${callContext}] all models exhausted; tried: ${
|
|
685
|
+
`[${callContext}] all models exhausted; tried: ${failedAttempts.map((failure) => failure.attempt.label).join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`,
|
|
686
|
+
);
|
|
687
|
+
throwWithPromptFailure(
|
|
688
|
+
firstError ?? lastError ?? new Error("All fallback models failed validation"),
|
|
689
|
+
failedAttempts,
|
|
690
|
+
args,
|
|
691
|
+
timeoutMs,
|
|
576
692
|
);
|
|
577
|
-
throw firstError ?? lastError ?? new Error("All fallback models failed validation");
|
|
578
693
|
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/// <reference types="bun-types" />
|
|
2
|
+
|
|
3
|
+
import { afterEach, describe, expect, it } from "bun:test";
|
|
4
|
+
import { mkdirSync, mkdtempSync, rmSync, unlinkSync, utimesSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import {
|
|
8
|
+
formatOpenCodeDbDoctorLine,
|
|
9
|
+
formatOpenCodeDbMissingBanner,
|
|
10
|
+
formatOpenCodeDbMissingStatusLine,
|
|
11
|
+
openCodeDbPathExists,
|
|
12
|
+
resetOpenCodeDbPathStateForTesting,
|
|
13
|
+
resolveOpenCodeDbPath,
|
|
14
|
+
} from "./opencode-db-path";
|
|
15
|
+
|
|
16
|
+
const ORIGINAL_ENV = {
|
|
17
|
+
XDG_DATA_HOME: process.env.XDG_DATA_HOME,
|
|
18
|
+
OPENCODE_DB: process.env.OPENCODE_DB,
|
|
19
|
+
OPENCODE_DISABLE_CHANNEL_DB: process.env.OPENCODE_DISABLE_CHANNEL_DB,
|
|
20
|
+
OPENCODE_CHANNEL: process.env.OPENCODE_CHANNEL,
|
|
21
|
+
};
|
|
22
|
+
const tempDirs: string[] = [];
|
|
23
|
+
|
|
24
|
+
function restore(name: keyof typeof ORIGINAL_ENV): void {
|
|
25
|
+
const value = ORIGINAL_ENV[name];
|
|
26
|
+
if (value === undefined) delete process.env[name];
|
|
27
|
+
else process.env[name] = value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
restore("XDG_DATA_HOME");
|
|
32
|
+
restore("OPENCODE_DB");
|
|
33
|
+
restore("OPENCODE_DISABLE_CHANNEL_DB");
|
|
34
|
+
restore("OPENCODE_CHANNEL");
|
|
35
|
+
resetOpenCodeDbPathStateForTesting();
|
|
36
|
+
for (const directory of tempDirs.splice(0)) rmSync(directory, { recursive: true, force: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function useDataHome(): { dataHome: string; openCodeDir: string } {
|
|
40
|
+
const dataHome = mkdtempSync(join(tmpdir(), "opencode-db-path-"));
|
|
41
|
+
const openCodeDir = join(dataHome, "opencode");
|
|
42
|
+
mkdirSync(openCodeDir, { recursive: true });
|
|
43
|
+
tempDirs.push(dataHome);
|
|
44
|
+
process.env.XDG_DATA_HOME = dataHome;
|
|
45
|
+
delete process.env.OPENCODE_DB;
|
|
46
|
+
delete process.env.OPENCODE_DISABLE_CHANNEL_DB;
|
|
47
|
+
delete process.env.OPENCODE_CHANNEL;
|
|
48
|
+
resetOpenCodeDbPathStateForTesting();
|
|
49
|
+
return { dataHome, openCodeDir };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createCandidate(openCodeDir: string, name: string, mtimeMs: number): string {
|
|
53
|
+
const path = join(openCodeDir, name);
|
|
54
|
+
writeFileSync(path, "sqlite fixture");
|
|
55
|
+
const time = new Date(mtimeMs);
|
|
56
|
+
utimesSync(path, time, time);
|
|
57
|
+
return path;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe("resolveOpenCodeDbPath", () => {
|
|
61
|
+
it("honors absolute and relative OPENCODE_DB overrides before every other source", () => {
|
|
62
|
+
const { openCodeDir } = useDataHome();
|
|
63
|
+
const absolute = join(openCodeDir, "elsewhere.db");
|
|
64
|
+
process.env.OPENCODE_DB = absolute;
|
|
65
|
+
process.env.OPENCODE_DISABLE_CHANNEL_DB = "true";
|
|
66
|
+
process.env.OPENCODE_CHANNEL = "dev";
|
|
67
|
+
expect(resolveOpenCodeDbPath()).toEqual({
|
|
68
|
+
path: absolute,
|
|
69
|
+
source: "OPENCODE_DB",
|
|
70
|
+
channel: null,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
process.env.OPENCODE_DB = "relative.db";
|
|
74
|
+
expect(resolveOpenCodeDbPath()).toEqual({
|
|
75
|
+
path: join(openCodeDir, "relative.db"),
|
|
76
|
+
source: "OPENCODE_DB",
|
|
77
|
+
channel: null,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("treats OPENCODE_DB=:memory: as an explicit but absent store", () => {
|
|
82
|
+
useDataHome();
|
|
83
|
+
process.env.OPENCODE_DB = ":memory:";
|
|
84
|
+
const resolution = resolveOpenCodeDbPath();
|
|
85
|
+
expect(resolution).toEqual({ path: ":memory:", source: "OPENCODE_DB", channel: null });
|
|
86
|
+
expect(openCodeDbPathExists(resolution)).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("uses the default DB when channel databases are disabled", () => {
|
|
90
|
+
const { openCodeDir } = useDataHome();
|
|
91
|
+
process.env.OPENCODE_DISABLE_CHANNEL_DB = "1";
|
|
92
|
+
process.env.OPENCODE_CHANNEL = "dev";
|
|
93
|
+
expect(resolveOpenCodeDbPath()).toEqual({
|
|
94
|
+
path: join(openCodeDir, "opencode.db"),
|
|
95
|
+
source: "default",
|
|
96
|
+
channel: null,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("honors an OPENCODE_CHANNEL visible to the plugin", () => {
|
|
101
|
+
const { openCodeDir } = useDataHome();
|
|
102
|
+
process.env.OPENCODE_CHANNEL = "dev";
|
|
103
|
+
expect(resolveOpenCodeDbPath()).toEqual({
|
|
104
|
+
path: join(openCodeDir, "opencode-dev.db"),
|
|
105
|
+
source: "channel",
|
|
106
|
+
channel: "dev",
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
for (const selectedName of [
|
|
111
|
+
"opencode.db",
|
|
112
|
+
"opencode-local.db",
|
|
113
|
+
"opencode-dev.db",
|
|
114
|
+
"opencode-nightly.db",
|
|
115
|
+
]) {
|
|
116
|
+
it(`discovers ${selectedName} when it is the newest existing candidate`, () => {
|
|
117
|
+
const { openCodeDir } = useDataHome();
|
|
118
|
+
const names = [
|
|
119
|
+
"opencode.db",
|
|
120
|
+
"opencode-local.db",
|
|
121
|
+
"opencode-dev.db",
|
|
122
|
+
"opencode-nightly.db",
|
|
123
|
+
];
|
|
124
|
+
for (const [index, name] of names.entries()) {
|
|
125
|
+
createCandidate(openCodeDir, name, name === selectedName ? 10_000 : 1_000 + index);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
expect(resolveOpenCodeDbPath()).toEqual({
|
|
129
|
+
path: join(openCodeDir, selectedName),
|
|
130
|
+
source: "discovered",
|
|
131
|
+
channel:
|
|
132
|
+
selectedName === "opencode.db"
|
|
133
|
+
? null
|
|
134
|
+
: selectedName.slice("opencode-".length, -".db".length),
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
it("uses candidate order as the deterministic tie-breaker for equal mtimes", () => {
|
|
140
|
+
const { openCodeDir } = useDataHome();
|
|
141
|
+
for (const name of ["opencode-local.db", "opencode-dev.db", "opencode.db"]) {
|
|
142
|
+
createCandidate(openCodeDir, name, 5_000);
|
|
143
|
+
}
|
|
144
|
+
expect(resolveOpenCodeDbPath()).toEqual({
|
|
145
|
+
path: join(openCodeDir, "opencode.db"),
|
|
146
|
+
source: "discovered",
|
|
147
|
+
channel: null,
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("returns the default path by value when no candidate exists", () => {
|
|
152
|
+
const { openCodeDir } = useDataHome();
|
|
153
|
+
const resolution = resolveOpenCodeDbPath();
|
|
154
|
+
expect(resolution).toEqual({
|
|
155
|
+
path: join(openCodeDir, "opencode.db"),
|
|
156
|
+
source: "default",
|
|
157
|
+
channel: null,
|
|
158
|
+
});
|
|
159
|
+
expect(openCodeDbPathExists(resolution)).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("re-probes discovery when the memoized chosen file disappears", () => {
|
|
163
|
+
const { openCodeDir } = useDataHome();
|
|
164
|
+
const primary = createCandidate(openCodeDir, "opencode-local.db", 2_000);
|
|
165
|
+
const fallback = createCandidate(openCodeDir, "opencode.db", 1_000);
|
|
166
|
+
expect(resolveOpenCodeDbPath().path).toBe(primary);
|
|
167
|
+
|
|
168
|
+
unlinkSync(primary);
|
|
169
|
+
expect(resolveOpenCodeDbPath()).toEqual({
|
|
170
|
+
path: fallback,
|
|
171
|
+
source: "discovered",
|
|
172
|
+
channel: null,
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("formats the missing banner, status, and doctor lines by value", () => {
|
|
177
|
+
const { openCodeDir } = useDataHome();
|
|
178
|
+
const lookedFor = [
|
|
179
|
+
join(openCodeDir, "opencode.db"),
|
|
180
|
+
join(openCodeDir, "opencode-local.db"),
|
|
181
|
+
join(openCodeDir, "opencode-dev.db"),
|
|
182
|
+
join(openCodeDir, "opencode-<channel>.db"),
|
|
183
|
+
].join(", ");
|
|
184
|
+
const resolution = resolveOpenCodeDbPath();
|
|
185
|
+
|
|
186
|
+
expect(formatOpenCodeDbMissingBanner(resolution)).toBe(
|
|
187
|
+
`Magic Context cannot find OpenCode's session database (looked for ${lookedFor}). History compaction (historian) and the mid-turn valve are disabled until it is found; set OPENCODE_DB if OpenCode stores it elsewhere.`,
|
|
188
|
+
);
|
|
189
|
+
expect(formatOpenCodeDbMissingStatusLine(resolution)).toBe(
|
|
190
|
+
`OpenCode DB: MISSING (looked for ${lookedFor}). History compaction (historian) and the mid-turn valve are disabled; set OPENCODE_DB if OpenCode stores it elsewhere.`,
|
|
191
|
+
);
|
|
192
|
+
expect(formatOpenCodeDbDoctorLine(resolution)).toBe(
|
|
193
|
+
`FAIL OpenCode session database: not found (looked for ${lookedFor}); set OPENCODE_DB if OpenCode stores it elsewhere.`,
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { isAbsolute, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
export type OpenCodeDbPathSource = "OPENCODE_DB" | "channel" | "default" | "discovered";
|
|
6
|
+
|
|
7
|
+
export interface OpenCodeDbPathResolution {
|
|
8
|
+
path: string;
|
|
9
|
+
source: OpenCodeDbPathSource;
|
|
10
|
+
channel: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface OpenCodeDbReadFailure extends OpenCodeDbPathResolution {
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface CachedResolution {
|
|
18
|
+
key: string;
|
|
19
|
+
resolution: OpenCodeDbPathResolution;
|
|
20
|
+
existed: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let cachedResolution: CachedResolution | null = null;
|
|
24
|
+
let lastReadFailure: OpenCodeDbReadFailure | null = null;
|
|
25
|
+
const claimedDiagnostics = new Set<string>();
|
|
26
|
+
|
|
27
|
+
function openCodeDataDir(): string {
|
|
28
|
+
return join(process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), "opencode");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function environmentKey(dataDir: string): string {
|
|
32
|
+
return [
|
|
33
|
+
dataDir,
|
|
34
|
+
process.env.OPENCODE_DB ?? "",
|
|
35
|
+
process.env.OPENCODE_DISABLE_CHANNEL_DB ?? "",
|
|
36
|
+
process.env.OPENCODE_CHANNEL ?? "",
|
|
37
|
+
].join("\0");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function channelPath(dataDir: string, channel: string): string {
|
|
41
|
+
return ["latest", "beta", "prod"].includes(channel)
|
|
42
|
+
? join(dataDir, "opencode.db")
|
|
43
|
+
: join(dataDir, `opencode-${channel}.db`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function discoveredCandidateNames(dataDir: string): string[] {
|
|
47
|
+
const names = ["opencode.db", "opencode-local.db", "opencode-dev.db"];
|
|
48
|
+
try {
|
|
49
|
+
const discovered = readdirSync(dataDir, { withFileTypes: true })
|
|
50
|
+
.filter((entry) => /^opencode-.+\.db$/.test(entry.name) && !names.includes(entry.name))
|
|
51
|
+
.map((entry) => entry.name)
|
|
52
|
+
.sort();
|
|
53
|
+
names.push(...discovered);
|
|
54
|
+
} catch {
|
|
55
|
+
// A missing or unreadable data directory has no discovered candidates.
|
|
56
|
+
}
|
|
57
|
+
return names;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function discoverOpenCodeDb(dataDir: string): OpenCodeDbPathResolution {
|
|
61
|
+
const candidates = discoveredCandidateNames(dataDir).map((name, order) => {
|
|
62
|
+
const path = join(dataDir, name);
|
|
63
|
+
try {
|
|
64
|
+
const metadata = statSync(path);
|
|
65
|
+
return metadata.isFile() ? { path, order, mtimeMs: metadata.mtimeMs } : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const existing = candidates
|
|
71
|
+
.filter((candidate): candidate is NonNullable<typeof candidate> => candidate !== null)
|
|
72
|
+
.sort((left, right) => right.mtimeMs - left.mtimeMs || left.order - right.order)[0];
|
|
73
|
+
|
|
74
|
+
if (!existing) {
|
|
75
|
+
return { path: join(dataDir, "opencode.db"), source: "default", channel: null };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const name = existing.path.slice(dataDir.length + 1);
|
|
79
|
+
const channel =
|
|
80
|
+
name === "opencode.db" ? null : name.slice("opencode-".length, -".db".length) || null;
|
|
81
|
+
return { path: existing.path, source: "discovered", channel };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveFresh(dataDir: string): OpenCodeDbPathResolution {
|
|
85
|
+
const explicit = process.env.OPENCODE_DB;
|
|
86
|
+
if (explicit !== undefined && explicit.length > 0) {
|
|
87
|
+
if (explicit === ":memory:") {
|
|
88
|
+
return { path: explicit, source: "OPENCODE_DB", channel: null };
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
path: isAbsolute(explicit) ? explicit : join(dataDir, explicit),
|
|
92
|
+
source: "OPENCODE_DB",
|
|
93
|
+
channel: null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const disableChannelDb = process.env.OPENCODE_DISABLE_CHANNEL_DB;
|
|
98
|
+
if (disableChannelDb === "1" || disableChannelDb === "true") {
|
|
99
|
+
return { path: join(dataDir, "opencode.db"), source: "default", channel: null };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const channel = process.env.OPENCODE_CHANNEL;
|
|
103
|
+
if (channel !== undefined && channel.length > 0) {
|
|
104
|
+
return { path: channelPath(dataDir, channel), source: "channel", channel };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return discoverOpenCodeDb(dataDir);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Resolve an explicit DB override first, then channel paths; discover candidates when the compiled channel is unknown. */
|
|
111
|
+
export function resolveOpenCodeDbPath(): OpenCodeDbPathResolution {
|
|
112
|
+
const dataDir = openCodeDataDir();
|
|
113
|
+
const key = environmentKey(dataDir);
|
|
114
|
+
if (
|
|
115
|
+
cachedResolution?.key === key &&
|
|
116
|
+
(!cachedResolution.existed || existsSync(cachedResolution.resolution.path))
|
|
117
|
+
) {
|
|
118
|
+
if (cachedResolution.existed) return cachedResolution.resolution;
|
|
119
|
+
// An absent result is re-probed so a DB created after plugin boot is detected.
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const resolution = resolveFresh(dataDir);
|
|
123
|
+
cachedResolution = {
|
|
124
|
+
key,
|
|
125
|
+
resolution,
|
|
126
|
+
existed: resolution.path !== ":memory:" && existsSync(resolution.path),
|
|
127
|
+
};
|
|
128
|
+
return resolution;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function openCodeDbPathExists(
|
|
132
|
+
resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
|
|
133
|
+
): boolean {
|
|
134
|
+
return resolution.path !== ":memory:" && existsSync(resolution.path);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function getOpenCodeDbProbeDescriptions(
|
|
138
|
+
resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
|
|
139
|
+
): string[] {
|
|
140
|
+
if (resolution.source === "OPENCODE_DB") return [resolution.path];
|
|
141
|
+
if (
|
|
142
|
+
resolution.source === "channel" ||
|
|
143
|
+
process.env.OPENCODE_DISABLE_CHANNEL_DB === "1" ||
|
|
144
|
+
process.env.OPENCODE_DISABLE_CHANNEL_DB === "true"
|
|
145
|
+
) {
|
|
146
|
+
return [resolution.path];
|
|
147
|
+
}
|
|
148
|
+
const dataDir = openCodeDataDir();
|
|
149
|
+
return [
|
|
150
|
+
join(dataDir, "opencode.db"),
|
|
151
|
+
join(dataDir, "opencode-local.db"),
|
|
152
|
+
join(dataDir, "opencode-dev.db"),
|
|
153
|
+
join(dataDir, "opencode-<channel>.db"),
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function lookedForText(resolution: OpenCodeDbPathResolution): string {
|
|
158
|
+
return getOpenCodeDbProbeDescriptions(resolution).join(", ");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function formatOpenCodeDbMissingBanner(
|
|
162
|
+
resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
|
|
163
|
+
): string {
|
|
164
|
+
return `Magic Context cannot find OpenCode's session database (looked for ${lookedForText(resolution)}). History compaction (historian) and the mid-turn valve are disabled until it is found; set OPENCODE_DB if OpenCode stores it elsewhere.`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function formatOpenCodeDbMissingStatusLine(
|
|
168
|
+
resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
|
|
169
|
+
): string {
|
|
170
|
+
return `OpenCode DB: MISSING (looked for ${lookedForText(resolution)}). History compaction (historian) and the mid-turn valve are disabled; set OPENCODE_DB if OpenCode stores it elsewhere.`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function formatOpenCodeDbDoctorLine(
|
|
174
|
+
resolution: OpenCodeDbPathResolution = resolveOpenCodeDbPath(),
|
|
175
|
+
): string {
|
|
176
|
+
return `FAIL OpenCode session database: not found (looked for ${lookedForText(resolution)}); set OPENCODE_DB if OpenCode stores it elsewhere.`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function formatOpenCodeDbReadFailureStatusLine(failure: OpenCodeDbReadFailure): string {
|
|
180
|
+
return `OpenCode DB: READ FAILED (path=${failure.path}, source=${failure.source}) — ${failure.message}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function recordOpenCodeDbReadFailure(
|
|
184
|
+
resolution: OpenCodeDbPathResolution,
|
|
185
|
+
error: unknown,
|
|
186
|
+
): OpenCodeDbReadFailure {
|
|
187
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
188
|
+
lastReadFailure = { ...resolution, message };
|
|
189
|
+
return lastReadFailure;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function clearOpenCodeDbReadFailure(path?: string): void {
|
|
193
|
+
if (path === undefined || lastReadFailure?.path === path) lastReadFailure = null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function getOpenCodeDbReadFailure(): OpenCodeDbReadFailure | null {
|
|
197
|
+
return lastReadFailure;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function claimOpenCodeDbDiagnosticOnce(
|
|
201
|
+
surface: string,
|
|
202
|
+
resolution: OpenCodeDbPathResolution,
|
|
203
|
+
): boolean {
|
|
204
|
+
const key = `${surface}\0${resolution.path}\0${resolution.source}`;
|
|
205
|
+
if (claimedDiagnostics.has(key)) return false;
|
|
206
|
+
claimedDiagnostics.add(key);
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Test-only reset for process memoization and once-only diagnostics. */
|
|
211
|
+
export function resetOpenCodeDbPathStateForTesting(): void {
|
|
212
|
+
cachedResolution = null;
|
|
213
|
+
lastReadFailure = null;
|
|
214
|
+
claimedDiagnostics.clear();
|
|
215
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type PiExecutableHarnessKind = "pi" | "omp";
|
|
2
|
+
|
|
3
|
+
/** Executable images recognized as Pi-compatible hosts. */
|
|
4
|
+
export const PI_IMAGE_NAMES = new Set(["pi", "pi.cmd", "omp", "oh-my-pi"]);
|
|
5
|
+
|
|
6
|
+
/** Classify a process title or executable path using the shared Pi image vocabulary. */
|
|
7
|
+
export function piHarnessKindFromExecutable(
|
|
8
|
+
value: string | undefined,
|
|
9
|
+
): PiExecutableHarnessKind | undefined {
|
|
10
|
+
const executable = (value ?? "")
|
|
11
|
+
.trim()
|
|
12
|
+
.replace(/^['"]|['"]$/g, "")
|
|
13
|
+
.replaceAll("\\", "/")
|
|
14
|
+
.split("/")
|
|
15
|
+
.at(-1)
|
|
16
|
+
?.toLowerCase()
|
|
17
|
+
.replace(/\.(?:exe|cmd)$/, "");
|
|
18
|
+
if (!executable || !PI_IMAGE_NAMES.has(executable)) return undefined;
|
|
19
|
+
return executable === "pi" ? "pi" : "omp";
|
|
20
|
+
}
|