@cortexkit/opencode-magic-context 0.45.0 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/{announcement-f75t58v9.js → announcement-y35wzg2r.js} +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/live-run-config.d.ts.map +1 -1
- package/dist/config/migrate-config-location.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +13 -7
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-5yrz2r24.js → conflict-warning-hook-7fj2b9jw.js} +6 -5
- package/dist/context-sh2m2vde.js +4525 -0
- package/dist/{derive-budgets-a63zsm5c.js → derive-budgets-7nj7r1rv.js} +2 -2
- package/dist/embedding-worker.js +4 -4
- package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts +45 -0
- package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts.map +1 -0
- package/dist/features/magic-context/checkout-claim.d.ts +155 -0
- package/dist/features/magic-context/checkout-claim.d.ts.map +1 -0
- package/dist/features/magic-context/compaction-marker.d.ts +20 -3
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-storage.d.ts +23 -2
- package/dist/features/magic-context/compartment-storage.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/provider-output-failure.d.ts +6 -2
- package/dist/features/magic-context/dreamer/provider-output-failure.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/provider-output-failure.test-support.d.ts +30 -0
- package/dist/features/magic-context/dreamer/provider-output-failure.test-support.d.ts.map +1 -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 +9 -0
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify-diff.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify-gate.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/git-commits/search-git-commits.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/project-identity-cache.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-verifications.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory.d.ts +3 -0
- package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
- package/dist/features/magic-context/memory/verification-paths.d.ts +2 -2
- package/dist/features/magic-context/memory/verification-paths.d.ts.map +1 -1
- package/dist/features/magic-context/merged-reasoning-decisions.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-async.d.ts +3 -3
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts +2 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/migration-v95-perf-indexes.d.ts +12 -0
- package/dist/features/magic-context/migration-v95-perf-indexes.d.ts.map +1 -0
- package/dist/features/magic-context/migration-worker-client.d.ts +4 -4
- package/dist/features/magic-context/migration-worker-client.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts +1 -0
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
- package/dist/features/magic-context/mural/resolve-mural.d.ts +15 -4
- package/dist/features/magic-context/mural/resolve-mural.d.ts.map +1 -1
- package/dist/features/magic-context/mural/source-revision.d.ts +7 -0
- package/dist/features/magic-context/mural/source-revision.d.ts.map +1 -0
- package/dist/features/magic-context/mural/storage-mural-cues.d.ts.map +1 -1
- package/dist/features/magic-context/reclaim-protection.d.ts +15 -10
- package/dist/features/magic-context/reclaim-protection.d.ts.map +1 -1
- package/dist/features/magic-context/session-cache-ttl.d.ts +4 -2
- package/dist/features/magic-context/session-cache-ttl.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/capabilities.d.ts +3 -0
- package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/github-token.d.ts +5 -0
- package/dist/features/magic-context/smart-notes/github-token.d.ts.map +1 -0
- package/dist/features/magic-context/smart-notes/runner.d.ts +1 -0
- package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +12 -0
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts +6 -0
- package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/storage.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/types.d.ts +2 -0
- package/dist/features/magic-context/smart-notes/types.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 +6 -9
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
- package/dist/features/magic-context/storage-ops.d.ts +8 -1
- package/dist/features/magic-context/storage-ops.d.ts.map +1 -1
- package/dist/features/magic-context/storage-replay-document.d.ts +7 -0
- package/dist/features/magic-context/storage-replay-document.d.ts.map +1 -1
- package/dist/features/magic-context/storage-schema-helpers.d.ts +2 -0
- package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
- package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
- package/dist/features/magic-context/storage-source.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +11 -3
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/temporal-decisions.d.ts +10 -0
- package/dist/features/magic-context/temporal-decisions.d.ts.map +1 -0
- package/dist/features/magic-context/tool-definition-tokens.d.ts +2 -0
- package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
- package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
- package/dist/features/magic-context/types.d.ts +7 -6
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/features/magic-context/window-report-ledger.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/index.d.ts.map +1 -1
- package/dist/hooks/magic-context/apply-operations.d.ts +8 -0
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +6 -3
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-cycle.d.ts +2 -0
- package/dist/hooks/magic-context/channel2-cycle.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts +7 -0
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-prompt.d.ts +2 -2
- package/dist/hooks/magic-context/compartment-prompt.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts +8 -0
- package/dist/hooks/magic-context/compartment-render-epoch.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-drop-queue.d.ts +18 -4
- 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-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts +12 -0
- package/dist/hooks/magic-context/ctx-reduce-availability.d.ts.map +1 -1
- package/dist/hooks/magic-context/decision-calibration.d.ts +3 -0
- package/dist/hooks/magic-context/decision-calibration.d.ts.map +1 -1
- package/dist/hooks/magic-context/default-protection-pressure.test-support.d.ts +37 -0
- package/dist/hooks/magic-context/default-protection-pressure.test-support.d.ts.map +1 -0
- package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts +4 -0
- package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts.map +1 -1
- package/dist/hooks/magic-context/edit-marker.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-drop.d.ts +2 -0
- package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts +21 -1
- package/dist/hooks/magic-context/emergency-fail-closed.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts +5 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +30 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -0
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/historian-prompt-fit.d.ts +4 -2
- package/dist/hooks/magic-context/historian-prompt-fit.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +2 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +9 -10
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-measured-request.d.ts +69 -0
- package/dist/hooks/magic-context/lkg-measured-request.d.ts.map +1 -0
- package/dist/hooks/magic-context/lkg-persist.d.ts +2 -0
- package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-replay-fit.d.ts +14 -0
- package/dist/hooks/magic-context/lkg-replay-fit.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-replay.d.ts +10 -1
- package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-slot.d.ts +4 -0
- package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-state-sync.d.ts +2 -0
- package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
- package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
- package/dist/hooks/magic-context/note-nudger.d.ts +17 -5
- package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts +1 -0
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-formatting.d.ts +7 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-raw.d.ts +4 -0
- 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.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 +27 -17
- package/dist/hooks/magic-context/reference-retrieval.d.ts.map +1 -1
- package/dist/hooks/magic-context/request-hook-order.d.ts +56 -0
- package/dist/hooks/magic-context/request-hook-order.d.ts.map +1 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +22 -3
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +4 -2
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/supersession-reclaim.d.ts +4 -0
- package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts +7 -0
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +12 -3
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-awareness.d.ts +6 -3
- package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-served-projection.d.ts +4 -0
- package/dist/hooks/magic-context/temporal-served-projection.d.ts.map +1 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-reclaim.d.ts +2 -0
- package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-message-helpers.d.ts +5 -0
- package/dist/hooks/magic-context/transform-message-helpers.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +41 -17
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +12 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
- package/dist/{index-vvtfyw1z.js → index-0d3g3461.js} +5 -13
- package/dist/index-1bc61ans.js +154 -0
- package/dist/{index-n96ag4rk.js → index-1zx87sdm.js} +5 -1
- package/dist/{index-4krbw8z9.js → index-3h0ybwah.js} +608 -102
- package/dist/{index-gd5qx1j9.js → index-5eqk6z3s.js} +62 -1
- package/dist/index-5gq3b2ya.js +494 -0
- package/dist/{index-8fgkmxmy.js → index-5p8jdswx.js} +4 -3
- package/dist/{index-75zjgwga.js → index-7qw742ev.js} +1 -1
- package/dist/{index-f85teqck.js → index-8da2zjhz.js} +3 -256
- package/dist/{index-q7yesyk9.js → index-8x2v7gg9.js} +25 -24
- package/dist/{index-w9jwmntv.js → index-b35pxds2.js} +9 -1
- package/dist/{v2/server-a4m2b1yv.js → index-fg34g6a5.js} +55 -1
- package/dist/index-hpecdzec.js +28506 -0
- package/dist/{index-2ntg5sd4.js → index-k5bdx1bx.js} +9 -5
- package/dist/{index-bmzf7zxv.js → index-majxda46.js} +23 -5
- package/dist/{index-2aq21j41.js → index-pkbcrdqs.js} +418 -127
- package/dist/{index-remp7e2g.js → index-q4yp4b52.js} +16 -1
- package/dist/{index-fr2z4qy8.js → index-r5hmbx3t.js} +132 -169
- package/dist/index-sfnj6h8h.js +259 -0
- package/dist/{index-xgt9k6c6.js → index-sgthv3f7.js} +2 -1
- package/dist/index-sj29b3z2.js +30 -0
- package/dist/{index-16ny736w.js → index-sjthx78w.js} +20526 -19817
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1983 -33118
- package/dist/{message-time-backfill-an311pmk.js → message-time-backfill-68gj6t3e.js} +2 -2
- package/dist/migration-worker.js +12 -7
- package/dist/plugin/dream-timer.d.ts +25 -1
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +2 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/messages-transform.d.ts +13 -0
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts +4 -0
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/subagent-tool-policy.d.ts +14 -0
- package/dist/plugin/subagent-tool-policy.d.ts.map +1 -0
- package/dist/plugin/tool-registry.d.ts +1 -0
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/{read-session-chunk-8rxs0tc9.js → read-session-chunk-egz7r68d.js} +8 -8
- package/dist/{recomp-orchestrator-nzamtw29.js → recomp-orchestrator-y15j0rb9.js} +15 -15
- package/dist/{send-session-notification-nqpv7cne.js → send-session-notification-dyw42dtr.js} +4 -4
- package/dist/shared/announcement.d.ts.map +1 -1
- package/dist/shared/cache-ttl-display.d.ts +6 -4
- package/dist/shared/cache-ttl-display.d.ts.map +1 -1
- package/dist/shared/model-cache-ttl.d.ts +1 -1
- package/dist/shared/model-cache-ttl.d.ts.map +1 -1
- package/dist/shared/model-suggestion-retry.d.ts +2 -0
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/protected-tools-policy.d.ts +6 -0
- package/dist/shared/protected-tools-policy.d.ts.map +1 -0
- package/dist/shared/quota-diagnostic.d.ts +4 -0
- package/dist/shared/quota-diagnostic.d.ts.map +1 -0
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/sqlite-context-pragmas.d.ts +12 -0
- package/dist/shared/sqlite-context-pragmas.d.ts.map +1 -0
- package/dist/shared/sqlite-helpers.d.ts +9 -0
- package/dist/shared/sqlite-helpers.d.ts.map +1 -1
- package/dist/shared/sqlite.d.ts +10 -0
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/status-summary.d.ts +1 -0
- package/dist/shared/status-summary.d.ts.map +1 -1
- package/dist/shared/status-view-check.d.ts.map +1 -1
- package/dist/shared/status-view.d.ts +4 -0
- package/dist/shared/status-view.d.ts.map +1 -1
- package/dist/shared/storage-permissions.d.ts +24 -0
- package/dist/shared/storage-permissions.d.ts.map +1 -1
- package/dist/shared/temporal-upgrade-fixture.d.ts +19 -0
- package/dist/shared/temporal-upgrade-fixture.d.ts.map +1 -0
- package/dist/shared/user-facing-codes.d.ts +5 -0
- package/dist/shared/user-facing-codes.d.ts.map +1 -1
- package/dist/shared/write-transaction-timing.d.ts +8 -1
- package/dist/shared/write-transaction-timing.d.ts.map +1 -1
- package/dist/tools/ctx-expand/render.d.ts.map +1 -1
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/constants.d.ts +2 -0
- package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/tools.d.ts +4 -4
- package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
- package/dist/{transform-postprocess-phase-414phndq.js → transform-postprocess-phase-bqhz6vdr.js} +15 -15
- package/dist/v2/context-zecmzk5s.js +52209 -0
- package/dist/v2/{derive-budgets-v0krf3t7.js → derive-budgets-kdgp4s3r.js} +4 -3
- package/dist/v2/embedding-worker.js +6 -5
- package/dist/v2/fold/boundary.d.ts +2 -0
- package/dist/v2/fold/boundary.d.ts.map +1 -1
- package/dist/v2/fold/owner.d.ts.map +1 -1
- package/dist/v2/hidden-child-native.d.ts +7 -0
- package/dist/v2/hidden-child-native.d.ts.map +1 -1
- package/dist/v2/hidden-child-record.d.ts +8 -0
- package/dist/v2/hidden-child-record.d.ts.map +1 -1
- package/dist/v2/hidden-completion.d.ts.map +1 -1
- package/dist/v2/hooks/channel2.d.ts +2 -0
- package/dist/v2/hooks/channel2.d.ts.map +1 -1
- package/dist/v2/hooks/context.d.ts +2 -2
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/dream-timer.d.ts +39 -0
- package/dist/v2/hooks/dream-timer.d.ts.map +1 -0
- package/dist/v2/hooks/dream-trigger.d.ts +15 -1
- package/dist/v2/hooks/dream-trigger.d.ts.map +1 -1
- package/dist/v2/hooks/hidden-child.d.ts +54 -3
- package/dist/v2/hooks/hidden-child.d.ts.map +1 -1
- package/dist/v2/hooks/payload.d.ts.map +1 -1
- package/dist/v2/hooks/store.d.ts.map +1 -1
- package/dist/v2/hooks/tools.d.ts +1 -1
- package/dist/v2/hooks/tools.d.ts.map +1 -1
- package/dist/v2/hooks/types.d.ts +12 -1
- package/dist/v2/hooks/types.d.ts.map +1 -1
- package/dist/v2/hooks/update-check.d.ts.map +1 -1
- package/dist/v2/{message-time-backfill-0n93vrd7.js → message-time-backfill-r6qkem1x.js} +3 -3
- package/dist/v2/migration-worker.js +13 -8
- package/dist/v2/{read-session-chunk-hjza38hr.js → read-session-chunk-8vjp6581.js} +9 -9
- package/dist/v2/recomp-orchestrator-8ejxvrzz.js +39 -0
- package/dist/v2/{safe-notification-target-wtj0bbc2.js → safe-notification-target-eb1n1vfb.js} +2 -2
- package/dist/v2/{send-session-notification-mq9frrtm.js → send-session-notification-qs0j78v6.js} +6 -6
- package/dist/v2/server-14vq4kpm.js +30 -0
- package/dist/v2/server-1bc61ans.js +154 -0
- package/dist/v2/{server-n96ag4rk.js → server-1zx87sdm.js} +5 -1
- package/dist/v2/server-3n4q9xme.js +79 -0
- package/dist/v2/{server-rhdgxp1t.js → server-51dv1pr5.js} +63 -2
- package/dist/v2/{server-5317ykaf.js → server-6r257mf5.js} +1 -1
- package/dist/v2/server-84w6rvvf.js +14 -0
- package/dist/v2/{server-qpg6wf72.js → server-ajzeteqw.js} +9 -1
- package/dist/v2/server-c5gz8wph.js +3892 -0
- package/dist/v2/{server-v5xgft5v.js → server-e29d7p7c.js} +188 -3533
- package/dist/{index-a4m2b1yv.js → v2/server-fg34g6a5.js} +55 -1
- package/dist/v2/{server-e36nf2p1.js → server-frbm99q5.js} +1595 -1599
- package/dist/v2/{server-remp7e2g.js → server-g0fjrkta.js} +16 -1
- package/dist/v2/{server-c4g2y1z8.js → server-jc7y02q0.js} +2816 -5392
- package/dist/v2/{server-z19x7eqz.js → server-mcfawjgc.js} +4 -4
- package/dist/v2/{server-hfjfbg59.js → server-qp5hx1ad.js} +608 -105
- package/dist/v2/server-vs5gpnd4.js +493 -0
- package/dist/v2/{server-w99cyt6a.js → server-wvjvfhck.js} +14 -6
- package/dist/v2/server-xa4xe3rv.js +3351 -0
- package/dist/v2/{server-0jqw05sa.js → server-y8maxeft.js} +6 -84
- package/dist/v2/server.d.ts.map +1 -1
- package/dist/v2/server.js +104 -49288
- package/dist/v2/store-reader.d.ts +39 -1
- package/dist/v2/store-reader.d.ts.map +1 -1
- package/dist/v2/tool-result.d.ts +8 -0
- package/dist/v2/tool-result.d.ts.map +1 -0
- package/dist/v2/tui/index.d.ts.map +1 -1
- package/dist/v2/{wrapup-orchestrator-tbyx78dz.js → wrapup-orchestrator-0snhvwcv.js} +20 -16
- package/dist/wrapup-orchestrator-39amhv9s.js +26 -0
- package/package.json +26 -10
- package/src/config/index.ts +21 -4
- package/src/config/latch-permanence-guard.test.ts +6 -1
- package/src/config/live-run-config.ts +1 -0
- package/src/config/migrate-config-location.test.ts +31 -1
- package/src/config/migrate-config-location.ts +8 -41
- package/src/config/protected-tools.test.ts +70 -0
- package/src/config/raw-loader.test.ts +15 -0
- package/src/config/raw-loader.ts +1 -1
- package/src/config/removed-agent-source-fence.test.ts +10 -1
- package/src/config/schema/magic-context.test.ts +20 -1
- package/src/config/schema/magic-context.ts +26 -7
- package/src/shared/announcement.ts +6 -5
- package/src/shared/cache-ttl-display.test.ts +1 -1
- package/src/shared/cache-ttl-display.ts +17 -16
- package/src/shared/fold-render-lock-fence.test.ts +84 -0
- package/src/shared/fold-render-lock-fixtures.test.ts +276 -0
- package/src/shared/model-cache-ttl.test.ts +2 -2
- package/src/shared/model-cache-ttl.ts +8 -4
- package/src/shared/model-suggestion-retry.test.ts +104 -1
- package/src/shared/model-suggestion-retry.ts +63 -6
- package/src/shared/models-dev-cache.ts +3 -11
- package/src/shared/protected-tools-policy.ts +23 -0
- package/src/shared/quota-diagnostic.ts +9 -0
- package/src/shared/rpc-server.ts +6 -54
- package/src/shared/sqlite-context-pragmas.test.ts +105 -0
- package/src/shared/sqlite-context-pragmas.ts +24 -0
- package/src/shared/sqlite-helpers.ts +17 -0
- package/src/shared/sqlite.ts +75 -0
- package/src/shared/status-detail-text.test.ts +18 -1
- package/src/shared/status-summary.ts +11 -0
- package/src/shared/status-view-check.ts +5 -0
- package/src/shared/status-view.test.ts +11 -2
- package/src/shared/status-view.ts +12 -1
- package/src/shared/storage-permissions.test.ts +204 -0
- package/src/shared/storage-permissions.ts +192 -0
- package/src/shared/temporal-upgrade-fixture.ts +50 -0
- package/src/shared/user-facing-codes.ts +6 -0
- package/src/shared/write-transaction-timing.test.ts +39 -0
- package/src/shared/write-transaction-timing.ts +25 -1
- package/src/tui/dialogs/status-dialog-render.test.ts +1 -1
- package/src/tui/index.tsx +3 -1
- package/src/tui/startup-order.test.ts +16 -0
- package/src/tui-compiled/index.tsx +3 -1
- package/src/v2/tui/index.ts +9 -2
- package/dist/index-anq4nn6f.js +0 -131
- package/dist/index-e44zzy84.js +0 -10
- package/dist/index-x23gah8c.js +0 -16
- package/dist/v2/recomp-orchestrator-1aq7h44c.js +0 -36
- package/dist/v2/server-e44zzy84.js +0 -10
- package/dist/v2/server-fgmxwrgy.js +0 -16
- package/dist/v2/server-r7cmye3k.js +0 -131
- package/dist/wrapup-orchestrator-h28bqsy6.js +0 -26
- package/dist/v2/{server-ckpc8pxy.js → server-1qbnzc80.js} +8 -8
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
1
|
+
import { describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
|
+
import { withDreamerModelCooldown } from "../features/magic-context/dreamer/provider-output-failure";
|
|
3
|
+
import { recordedGeminiQuotaMessages } from "../features/magic-context/dreamer/provider-output-failure.test-support";
|
|
2
4
|
|
|
3
5
|
import { HiddenAgentStepLimit } from "../v2/hooks/hidden-child";
|
|
4
6
|
import {
|
|
@@ -406,6 +408,107 @@ describe("promptSyncWithModelSuggestionRetry", () => {
|
|
|
406
408
|
});
|
|
407
409
|
});
|
|
408
410
|
|
|
411
|
+
describe("run-scoped primary quota cooldown", () => {
|
|
412
|
+
test("preserves fallback order, retries the primary after reset, and forgets it in a new run", async () => {
|
|
413
|
+
let now = 1791425104275;
|
|
414
|
+
const clock = spyOn(Date, "now").mockImplementation(() => now);
|
|
415
|
+
const models: string[] = [];
|
|
416
|
+
const primary = { providerID: "google", modelID: "antigravity-gemini-3.8-flash" };
|
|
417
|
+
let latestModel = "";
|
|
418
|
+
let primaryCalls = 0;
|
|
419
|
+
const client = createClient(
|
|
420
|
+
mock(async (args: PromptCall) => {
|
|
421
|
+
latestModel = `${args.body.model?.providerID}/${args.body.model?.modelID}`;
|
|
422
|
+
models.push(latestModel);
|
|
423
|
+
if (latestModel.startsWith("google/")) primaryCalls++;
|
|
424
|
+
if (latestModel === "deepseek/first") throw new Error("fallback unavailable");
|
|
425
|
+
return {};
|
|
426
|
+
}),
|
|
427
|
+
);
|
|
428
|
+
const run = () =>
|
|
429
|
+
promptSyncWithValidatedOutputRetry(client, createArgs(primary), {
|
|
430
|
+
fallbackModels: ["deepseek/first", "deepseek/second"],
|
|
431
|
+
fetchOutput: async () =>
|
|
432
|
+
latestModel.startsWith("google/") && primaryCalls <= 2
|
|
433
|
+
? recordedGeminiQuotaMessages()
|
|
434
|
+
: [
|
|
435
|
+
{
|
|
436
|
+
info: { role: "assistant" },
|
|
437
|
+
parts: [{ type: "text", text: "valid" }],
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
validateOutput: () => "valid",
|
|
441
|
+
});
|
|
442
|
+
try {
|
|
443
|
+
await withDreamerModelCooldown(async () => {
|
|
444
|
+
await run();
|
|
445
|
+
await run();
|
|
446
|
+
});
|
|
447
|
+
await withDreamerModelCooldown(async () => {
|
|
448
|
+
await run();
|
|
449
|
+
await run();
|
|
450
|
+
now += 5_700_000;
|
|
451
|
+
await run();
|
|
452
|
+
});
|
|
453
|
+
expect(models).toEqual([
|
|
454
|
+
"google/antigravity-gemini-3.8-flash",
|
|
455
|
+
"deepseek/first",
|
|
456
|
+
"deepseek/second",
|
|
457
|
+
"deepseek/first",
|
|
458
|
+
"deepseek/second",
|
|
459
|
+
"google/antigravity-gemini-3.8-flash",
|
|
460
|
+
"deepseek/first",
|
|
461
|
+
"deepseek/second",
|
|
462
|
+
"deepseek/first",
|
|
463
|
+
"deepseek/second",
|
|
464
|
+
"google/antigravity-gemini-3.8-flash",
|
|
465
|
+
]);
|
|
466
|
+
} finally {
|
|
467
|
+
clock.mockRestore();
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("records primary quota exhaustion alongside a failing fallback, not a manifest error", async () => {
|
|
472
|
+
let model = "";
|
|
473
|
+
const client = createClient(
|
|
474
|
+
mock(async (args: PromptCall) => {
|
|
475
|
+
model = args.body.model?.providerID ?? "";
|
|
476
|
+
if (model === "deepseek") throw new Error("SessionExecutionFence.Lost");
|
|
477
|
+
return {};
|
|
478
|
+
}),
|
|
479
|
+
);
|
|
480
|
+
let caught: unknown;
|
|
481
|
+
await withDreamerModelCooldown(async () => {
|
|
482
|
+
try {
|
|
483
|
+
await promptSyncWithValidatedOutputRetry(
|
|
484
|
+
client,
|
|
485
|
+
createArgs({ providerID: "google", modelID: "antigravity-gemini-3.8-flash" }),
|
|
486
|
+
{
|
|
487
|
+
fallbackModels: ["deepseek/deepseek-flash"],
|
|
488
|
+
fetchOutput: async () => recordedGeminiQuotaMessages(),
|
|
489
|
+
validateOutput: () => {
|
|
490
|
+
throw new Error("verify manifest missing complete root element");
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
caught = error;
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
expect(String(caught)).toContain("primary quota exhausted until");
|
|
499
|
+
expect(String(caught)).toContain("SessionExecutionFence.Lost");
|
|
500
|
+
expect(String(caught)).not.toContain("manifest missing");
|
|
501
|
+
expect(getPromptFailureDetail(caught)).toMatchObject({
|
|
502
|
+
failureClass: "provider_error",
|
|
503
|
+
modelAttempted: "deepseek/deepseek-flash",
|
|
504
|
+
modelsTried: ["google/antigravity-gemini-3.8-flash", "deepseek/deepseek-flash"],
|
|
505
|
+
});
|
|
506
|
+
expect(getPromptFailureDetail(caught)?.providerError).toContain(
|
|
507
|
+
"primary quota exhausted until",
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
409
512
|
describe("promptSyncWithValidatedOutputRetry", () => {
|
|
410
513
|
test("records a step cap as step_limit without a provider error", async () => {
|
|
411
514
|
const prompt = mock(async () => {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { createOpencodeClient } from "@opencode-ai/sdk";
|
|
2
|
+
import {
|
|
3
|
+
DreamerProviderOutputFailureError,
|
|
4
|
+
primaryQuotaFailure,
|
|
5
|
+
providerOutputFailureFromInvalidManifest,
|
|
6
|
+
rememberPrimaryQuota,
|
|
7
|
+
} from "../features/magic-context/dreamer/provider-output-failure";
|
|
2
8
|
import { DreamTokenBudgetExceeded } from "../features/magic-context/dreamer/token-budget";
|
|
3
9
|
import { detectOverflow } from "../features/magic-context/overflow-detection";
|
|
4
10
|
import { HiddenCompletionRefusal } from "../hooks/magic-context/compartment-runner-types";
|
|
@@ -59,6 +65,8 @@ export interface PromptAttemptInfo {
|
|
|
59
65
|
totalAttempts: number;
|
|
60
66
|
/** Explicit model override for this attempt, when one was supplied. */
|
|
61
67
|
model?: { providerID: string; modelID: string };
|
|
68
|
+
/** A run-local quota deadline prevented dispatch; not an actual model call. */
|
|
69
|
+
skipped?: boolean;
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
export interface PromptRetryOptions {
|
|
@@ -436,14 +444,25 @@ function throwWithPromptFailure(
|
|
|
436
444
|
legacyError instanceof Error ? legacyError : new Error(extractMessage(legacyError));
|
|
437
445
|
const last = failedAttempts.at(-1);
|
|
438
446
|
const failureClass = last?.failureClass ?? "unknown";
|
|
447
|
+
const quota = failedAttempts.find(
|
|
448
|
+
(failure) =>
|
|
449
|
+
failure.error instanceof DreamerProviderOutputFailureError &&
|
|
450
|
+
failure.error.quotaResetAt !== undefined,
|
|
451
|
+
)?.error as DreamerProviderOutputFailureError | undefined;
|
|
452
|
+
if (quota && error !== quota && !error.message.includes(quota.message))
|
|
453
|
+
error.message = `${quota.message}; ${error.message}`;
|
|
439
454
|
const providerError =
|
|
440
|
-
failureClass === "provider_error"
|
|
441
|
-
? sanitizeDiagnosticText(
|
|
455
|
+
failureClass === "provider_error" || quota
|
|
456
|
+
? sanitizeDiagnosticText(
|
|
457
|
+
`${quota ? `${quota.message}; ` : ""}${shortErr(last?.error ?? legacyError)}`,
|
|
458
|
+
).slice(0, 500)
|
|
442
459
|
: null;
|
|
443
460
|
promptFailureDetails.set(error, {
|
|
444
461
|
failureClass,
|
|
445
462
|
modelAttempted: last?.attempt.label ?? null,
|
|
446
|
-
modelsTried: failedAttempts
|
|
463
|
+
modelsTried: failedAttempts
|
|
464
|
+
.filter((failure) => !failure.attempt.skipped)
|
|
465
|
+
.map((failure) => failure.attempt.label),
|
|
447
466
|
providerError,
|
|
448
467
|
timeoutMs: failureClass === "provider_timeout" ? timeoutMs : null,
|
|
449
468
|
childSessionId: transport ? (transport.childSessionId ?? null) : args.path.id || null,
|
|
@@ -667,6 +686,16 @@ async function attemptAndValidate<TOutput, TValidated>(
|
|
|
667
686
|
}
|
|
668
687
|
|
|
669
688
|
try {
|
|
689
|
+
// Only the structured account-pool notice bypasses caller validation.
|
|
690
|
+
// The near-zero-token heuristic remains owned by manifest callers.
|
|
691
|
+
const messages = Array.isArray(output)
|
|
692
|
+
? output
|
|
693
|
+
: (output as { messages?: unknown } | null)?.messages;
|
|
694
|
+
const text = extractLatestAssistantText(messages);
|
|
695
|
+
if (text) {
|
|
696
|
+
const failure = providerOutputFailureFromInvalidManifest(messages, text);
|
|
697
|
+
if (failure?.quotaResetAt !== undefined) throw failure;
|
|
698
|
+
}
|
|
670
699
|
if (!extractLatestAssistantText(output)) {
|
|
671
700
|
const assistantFailure = extractLatestAssistantFailure(output);
|
|
672
701
|
if (assistantFailure) {
|
|
@@ -721,8 +750,22 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
721
750
|
const failedAttempts: FailedAttempt[] = [];
|
|
722
751
|
let firstError: unknown = null;
|
|
723
752
|
let lastError: unknown = null;
|
|
753
|
+
const cooledPrimary = primaryQuotaFailure(explicitPrimaryLabel);
|
|
724
754
|
|
|
725
755
|
try {
|
|
756
|
+
if (cooledPrimary)
|
|
757
|
+
throw {
|
|
758
|
+
error: cooledPrimary,
|
|
759
|
+
failureClass: "provider_error",
|
|
760
|
+
attempt: {
|
|
761
|
+
label: explicitPrimaryLabel,
|
|
762
|
+
attemptIndex: 0,
|
|
763
|
+
isFallback: false,
|
|
764
|
+
totalAttempts,
|
|
765
|
+
model: baseBody.model,
|
|
766
|
+
skipped: true,
|
|
767
|
+
},
|
|
768
|
+
} satisfies FailedAttempt;
|
|
726
769
|
return await attemptAndValidate(
|
|
727
770
|
client,
|
|
728
771
|
baseArgs,
|
|
@@ -743,6 +786,19 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
743
786
|
failedAttempts.push(failure);
|
|
744
787
|
firstError = failure.error;
|
|
745
788
|
lastError = failure.error;
|
|
789
|
+
if (
|
|
790
|
+
failure.error instanceof DreamerProviderOutputFailureError &&
|
|
791
|
+
failure.error.quotaResetAt !== undefined
|
|
792
|
+
)
|
|
793
|
+
failure.error.message = failure.error.message.replace(
|
|
794
|
+
/^provider quota exhausted/,
|
|
795
|
+
"primary quota exhausted",
|
|
796
|
+
);
|
|
797
|
+
const quota = !cooledPrimary && rememberPrimaryQuota(explicitPrimaryLabel, failure.error);
|
|
798
|
+
if (quota)
|
|
799
|
+
log(
|
|
800
|
+
`[${callContext}] ${quota.message}; skipping ${explicitPrimaryLabel} for this run until reset`,
|
|
801
|
+
);
|
|
746
802
|
if (isNonRetryable(failure.error, options.signal)) {
|
|
747
803
|
throwWithPromptFailure(
|
|
748
804
|
failure.error,
|
|
@@ -763,9 +819,10 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
|
|
|
763
819
|
);
|
|
764
820
|
}
|
|
765
821
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
822
|
+
if (!cooledPrimary && !quota)
|
|
823
|
+
log(
|
|
824
|
+
`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(failure.error)}; trying ${fallbacks.length} fallback(s)`,
|
|
825
|
+
);
|
|
769
826
|
}
|
|
770
827
|
|
|
771
828
|
for (let i = 0; i < fallbacks.length; i += 1) {
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
* for Pi and Pi's own path is used.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { readFileSync } from "node:fs";
|
|
29
29
|
import { join } from "node:path";
|
|
30
30
|
import type { ContextLimitProvenance } from "./context-limit-provenance";
|
|
31
31
|
import { getMagicContextStorageDir } from "./data-path";
|
|
32
32
|
import { getHarness } from "./harness";
|
|
33
33
|
import { modelRefLookupOrder } from "./harness-provider-map";
|
|
34
34
|
import { sessionLog } from "./logger";
|
|
35
|
-
import {
|
|
35
|
+
import { writeStorageFileAtomicSync } from "./storage-permissions";
|
|
36
36
|
import {
|
|
37
37
|
deriveWindowGeometry,
|
|
38
38
|
getWindowOverlay,
|
|
@@ -182,16 +182,8 @@ function persistApiCache(): void {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
try {
|
|
185
|
-
const dir = getMagicContextStorageDir();
|
|
186
|
-
mkdirSync(dir, { recursive: true });
|
|
187
185
|
const target = persistFilePath();
|
|
188
|
-
|
|
189
|
-
if (shouldEnforcePrivateStoragePermissions()) {
|
|
190
|
-
writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 0o600 });
|
|
191
|
-
} else {
|
|
192
|
-
writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8" });
|
|
193
|
-
}
|
|
194
|
-
renameSync(tmp, target);
|
|
186
|
+
writeStorageFileAtomicSync(target, JSON.stringify(obj));
|
|
195
187
|
} catch {
|
|
196
188
|
// best-effort — a failed persist only loses cold-start warmth, not correctness
|
|
197
189
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Default result keep counts shared by runtime and configuration; this database-free module is bundled with the TUI. */
|
|
2
|
+
export const DEFAULT_PROTECTED_TOOLS: Readonly<Record<string, number>> = {
|
|
3
|
+
todowrite: 1,
|
|
4
|
+
ctx_reduce: 3,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function normalizeProtectedToolName(name: string | null): string {
|
|
8
|
+
return (name ?? "").toLowerCase().replace(/^mcp_/, "");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Normalize before merging tiers so aliases cannot defeat a project override. */
|
|
12
|
+
export function mergeProtectedTools(
|
|
13
|
+
...maps: (Readonly<Record<string, number>> | undefined)[]
|
|
14
|
+
): Record<string, number> {
|
|
15
|
+
return Object.fromEntries(
|
|
16
|
+
[DEFAULT_PROTECTED_TOOLS, ...maps].flatMap((map) =>
|
|
17
|
+
Object.entries(map ?? {}).map(([name, count]) => [
|
|
18
|
+
normalizeProtectedToolName(name),
|
|
19
|
+
count,
|
|
20
|
+
]),
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Only expose the generated quota deadline, not arbitrary provider output,
|
|
2
|
+
* account identifiers, or private paths from the surrounding failure text. */
|
|
3
|
+
export function primaryQuotaDiagnostic(error: string | null | undefined): string | null {
|
|
4
|
+
return (
|
|
5
|
+
error?.match(
|
|
6
|
+
/primary quota exhausted until \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/,
|
|
7
|
+
)?.[0] ?? null
|
|
8
|
+
);
|
|
9
|
+
}
|
package/src/shared/rpc-server.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
chmodSync,
|
|
4
|
-
mkdirSync,
|
|
5
|
-
readdirSync,
|
|
6
|
-
readFileSync,
|
|
7
|
-
renameSync,
|
|
8
|
-
rmSync,
|
|
9
|
-
unlinkSync,
|
|
10
|
-
writeFileSync,
|
|
11
|
-
} from "node:fs";
|
|
12
|
-
import { dirname } from "node:path";
|
|
2
|
+
import { readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
13
3
|
import type { Server, ServerWebSocket } from "bun";
|
|
14
4
|
import { log } from "./logger";
|
|
15
5
|
import {
|
|
@@ -25,7 +15,7 @@ import {
|
|
|
25
15
|
rpcPortDir,
|
|
26
16
|
rpcPortFilePath,
|
|
27
17
|
} from "./rpc-utils";
|
|
28
|
-
import {
|
|
18
|
+
import { writeStorageFileAtomicSync } from "./storage-permissions";
|
|
29
19
|
|
|
30
20
|
type RpcHandler = (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
31
21
|
|
|
@@ -177,38 +167,11 @@ export class MagicContextRpcServer {
|
|
|
177
167
|
// recent instead of cross-wiring via one shared project file.
|
|
178
168
|
try {
|
|
179
169
|
void this.warnIfOtherLiveInstance();
|
|
180
|
-
|
|
181
|
-
//
|
|
182
|
-
// it owner-only; a trusted-group deployment explicitly delegates every
|
|
183
|
-
// storage mode to its operator, so this path must not chmod or supply a
|
|
184
|
-
// restrictive creation mode in that case.
|
|
185
|
-
const enforcePrivatePermissions = shouldEnforcePrivateStoragePermissions();
|
|
186
|
-
if (enforcePrivatePermissions) {
|
|
187
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
188
|
-
try {
|
|
189
|
-
chmodSync(dir, 0o700);
|
|
190
|
-
} catch {
|
|
191
|
-
// Continue RPC startup when directory tightening is rejected.
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
mkdirSync(dir, { recursive: true });
|
|
195
|
-
}
|
|
196
|
-
// Registered before the file appears, so the storage migration guard in
|
|
197
|
-
// this process never sees its own discovery file as another live host.
|
|
170
|
+
// The discovery file carries the RPC bearer token. The shared writer
|
|
171
|
+
// creates its directory and temporary file with owner-only modes.
|
|
198
172
|
this.unregisterOwnInstance ??= registerOwnRpcServerInstance(this.instanceId);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// writeFileSync's mode only applies on create, so remove it first.
|
|
202
|
-
try {
|
|
203
|
-
rmSync(tmpPath, { force: true });
|
|
204
|
-
} catch {
|
|
205
|
-
// best-effort
|
|
206
|
-
}
|
|
207
|
-
// Synchronous write so the renameSync below sees a fully-written file.
|
|
208
|
-
// The private mode keeps the bearer token out of other local accounts;
|
|
209
|
-
// externally managed storage intentionally leaves its mode to the umask.
|
|
210
|
-
writeFileSync(
|
|
211
|
-
tmpPath,
|
|
173
|
+
writeStorageFileAtomicSync(
|
|
174
|
+
this.portFilePath,
|
|
212
175
|
JSON.stringify({
|
|
213
176
|
port: this.port,
|
|
214
177
|
pid: process.pid,
|
|
@@ -217,18 +180,7 @@ export class MagicContextRpcServer {
|
|
|
217
180
|
token: this.token,
|
|
218
181
|
instance_id: this.instanceId,
|
|
219
182
|
}),
|
|
220
|
-
enforcePrivatePermissions
|
|
221
|
-
? { encoding: "utf-8", mode: 0o600 }
|
|
222
|
-
: { encoding: "utf-8" },
|
|
223
183
|
);
|
|
224
|
-
renameSync(tmpPath, this.portFilePath);
|
|
225
|
-
if (enforcePrivatePermissions) {
|
|
226
|
-
try {
|
|
227
|
-
chmodSync(this.portFilePath, 0o600);
|
|
228
|
-
} catch {
|
|
229
|
-
// Continue RPC startup when port-file tightening is rejected.
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
184
|
log(`[rpc] server listening on 127.0.0.1:${this.port}`);
|
|
233
185
|
} catch (err) {
|
|
234
186
|
log(`[rpc] failed to write port file: ${err}`);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, spyOn } from "bun:test";
|
|
2
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
closeDatabase,
|
|
7
|
+
openDatabase,
|
|
8
|
+
openDatabaseAsync,
|
|
9
|
+
} from "../features/magic-context/storage-db";
|
|
10
|
+
import { Database } from "./sqlite";
|
|
11
|
+
import { configureContextDatabasePragmas } from "./sqlite-context-pragmas";
|
|
12
|
+
import { createTestTempDirFromPath } from "./test-temp-dir";
|
|
13
|
+
|
|
14
|
+
const roots: string[] = [];
|
|
15
|
+
function fixturePath(): string {
|
|
16
|
+
const root = createTestTempDirFromPath(join(tmpdir(), "mc-context-pragmas-"));
|
|
17
|
+
roots.push(root);
|
|
18
|
+
return join(root, "context.db");
|
|
19
|
+
}
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
closeDatabase();
|
|
22
|
+
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("context database durability", () => {
|
|
26
|
+
it("fresh synchronous open uses WAL and synchronous=NORMAL", () => {
|
|
27
|
+
const db = openDatabase(fixturePath());
|
|
28
|
+
expect(db?.prepare("PRAGMA journal_mode").get()).toEqual({ journal_mode: "wal" });
|
|
29
|
+
expect(db?.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 1 });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("fresh async open uses WAL and synchronous=NORMAL", async () => {
|
|
33
|
+
const db = await openDatabaseAsync({ dbPath: fixturePath() });
|
|
34
|
+
expect(db?.prepare("PRAGMA journal_mode").get()).toEqual({ journal_mode: "wal" });
|
|
35
|
+
expect(db?.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 1 });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("refuses NORMAL when SQLite cannot activate WAL", () => {
|
|
39
|
+
const db = new Database(fixturePath());
|
|
40
|
+
const prepare = db.prepare.bind(db);
|
|
41
|
+
const spy = spyOn(db, "prepare").mockImplementation((sql: string) =>
|
|
42
|
+
prepare(sql === "PRAGMA journal_mode=WAL" ? "PRAGMA journal_mode" : sql),
|
|
43
|
+
);
|
|
44
|
+
try {
|
|
45
|
+
expect(() => configureContextDatabasePragmas(db)).toThrow("requires WAL");
|
|
46
|
+
expect(prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 2 });
|
|
47
|
+
} finally {
|
|
48
|
+
spy.mockRestore();
|
|
49
|
+
db.close();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("read-only inspection leaves a rollback-journal store unchanged", () => {
|
|
54
|
+
const path = fixturePath();
|
|
55
|
+
const writer = new Database(path);
|
|
56
|
+
writer.exec("CREATE TABLE probe(id INTEGER)");
|
|
57
|
+
writer.close();
|
|
58
|
+
const bytes = readFileSync(path);
|
|
59
|
+
const reader = new Database(path, { readonly: true });
|
|
60
|
+
try {
|
|
61
|
+
configureContextDatabasePragmas(reader, true);
|
|
62
|
+
expect(reader.prepare("PRAGMA journal_mode").get()).toEqual({ journal_mode: "delete" });
|
|
63
|
+
expect(reader.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 2 });
|
|
64
|
+
} finally {
|
|
65
|
+
reader.close();
|
|
66
|
+
}
|
|
67
|
+
expect(readFileSync(path)).toEqual(bytes);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("an independently reopened WAL writer receives the connection-local policy", () => {
|
|
71
|
+
const path = fixturePath();
|
|
72
|
+
const first = new Database(path);
|
|
73
|
+
configureContextDatabasePragmas(first);
|
|
74
|
+
first.close();
|
|
75
|
+
const reopened = new Database(path);
|
|
76
|
+
try {
|
|
77
|
+
// Bun defaults WAL to NORMAL; Node defaults it to FULL. Exercise an
|
|
78
|
+
// explicit FULL connection so the assertion is portable to both.
|
|
79
|
+
reopened.exec("PRAGMA synchronous=FULL");
|
|
80
|
+
expect(reopened.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 2 });
|
|
81
|
+
configureContextDatabasePragmas(reopened);
|
|
82
|
+
expect(reopened.prepare("PRAGMA synchronous").get()).toEqual({ synchronous: 1 });
|
|
83
|
+
} finally {
|
|
84
|
+
reopened.close();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("WAL configuration releases its read cursor before a sibling commits", () => {
|
|
89
|
+
const path = fixturePath();
|
|
90
|
+
const db = new Database(path);
|
|
91
|
+
const sibling = new Database(path);
|
|
92
|
+
try {
|
|
93
|
+
configureContextDatabasePragmas(db);
|
|
94
|
+
db.exec(
|
|
95
|
+
"CREATE TABLE probe(id INTEGER PRIMARY KEY, value INTEGER); INSERT INTO probe VALUES (1, 0)",
|
|
96
|
+
);
|
|
97
|
+
sibling.exec("UPDATE probe SET value = 1 WHERE id = 1");
|
|
98
|
+
db.exec("UPDATE probe SET value = 2 WHERE id = 1");
|
|
99
|
+
expect(db.prepare("SELECT value FROM probe").get()).toEqual({ value: 2 });
|
|
100
|
+
} finally {
|
|
101
|
+
db.close();
|
|
102
|
+
sibling.close();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Database } from "./sqlite";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NORMAL avoids a WAL sync on each commit. WAL still recovers consistently after
|
|
5
|
+
* a crash, but the latest committed work may be lost on power failure. Never use
|
|
6
|
+
* this durability policy in a rollback journal. In-memory test stores cannot use
|
|
7
|
+
* WAL and keep their existing synchronous mode.
|
|
8
|
+
*
|
|
9
|
+
* Call after the schema fence and busy timeout, before any context-store writes.
|
|
10
|
+
* Read-only diagnostics must not switch a legacy store's journal mode.
|
|
11
|
+
*/
|
|
12
|
+
export function configureContextDatabasePragmas(db: Database, readonly = false): void {
|
|
13
|
+
const [row] = db
|
|
14
|
+
.prepare(readonly ? "PRAGMA journal_mode" : "PRAGMA journal_mode=WAL")
|
|
15
|
+
.all() as { journal_mode?: string }[];
|
|
16
|
+
const mode = row?.journal_mode?.toLowerCase();
|
|
17
|
+
if (mode === "memory" || (readonly && mode !== "wal")) return;
|
|
18
|
+
if (mode !== "wal") {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`context.db requires WAL before synchronous=NORMAL (got ${mode ?? "unknown"})`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
db.exec("PRAGMA synchronous=NORMAL");
|
|
24
|
+
}
|
|
@@ -25,3 +25,20 @@ export function closeQuietly(db: Database | null | undefined): void {
|
|
|
25
25
|
// intentional: caller wants quiet close
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* True only when the connection reports that no transaction is open. Caches
|
|
31
|
+
* keyed on SQLite's change counters must not keep a value read inside a
|
|
32
|
+
* transaction: a rollback restores the rows without moving total_changes(),
|
|
33
|
+
* data_version or schema_version, so the stale entry would look current.
|
|
34
|
+
* Unknown state (a proxy that cannot read the native getter) counts as "in a
|
|
35
|
+
* transaction" so callers fall back to a fresh read.
|
|
36
|
+
*/
|
|
37
|
+
export function isKnownAutocommit(db: Database): boolean {
|
|
38
|
+
const state = db as unknown as { inTransaction?: boolean; isTransaction?: boolean };
|
|
39
|
+
try {
|
|
40
|
+
return state.inTransaction === false || state.isTransaction === false;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/shared/sqlite.ts
CHANGED
|
@@ -365,6 +365,12 @@ function trackSqliteConnection(
|
|
|
365
365
|
...metadata,
|
|
366
366
|
reference: new WeakRef(db),
|
|
367
367
|
});
|
|
368
|
+
const prepare = db.prepare.bind(db);
|
|
369
|
+
Object.defineProperty(db, "prepare", {
|
|
370
|
+
configurable: true,
|
|
371
|
+
writable: true,
|
|
372
|
+
value: (sql: string) => ownPreparedStatement(db, prepare(sql)),
|
|
373
|
+
});
|
|
368
374
|
return db;
|
|
369
375
|
}
|
|
370
376
|
|
|
@@ -540,6 +546,75 @@ export type Database = BetterSqlite3.Database;
|
|
|
540
546
|
*/
|
|
541
547
|
export type Statement = BetterSqlite3.Statement<unknown[], unknown>;
|
|
542
548
|
|
|
549
|
+
type FinalizableStatement = Statement & { finalize(): void };
|
|
550
|
+
interface StatementOwner {
|
|
551
|
+
references: Set<WeakRef<FinalizableStatement>>;
|
|
552
|
+
registered: WeakSet<FinalizableStatement>;
|
|
553
|
+
/** Set size at which collected references are next swept out. */
|
|
554
|
+
pruneAt: number;
|
|
555
|
+
}
|
|
556
|
+
const statementOwners = new WeakMap<Database, StatementOwner>();
|
|
557
|
+
const FIRST_PRUNE_AT = 256;
|
|
558
|
+
|
|
559
|
+
function ownPreparedStatement(db: Database, statement: Statement): Statement {
|
|
560
|
+
const finalizable = statement as FinalizableStatement;
|
|
561
|
+
// Node finalizes its statements on close and exposes no finalize() method.
|
|
562
|
+
if (typeof finalizable.finalize !== "function") return statement;
|
|
563
|
+
let owner = statementOwners.get(db);
|
|
564
|
+
if (!owner) {
|
|
565
|
+
const references = new Set<WeakRef<FinalizableStatement>>();
|
|
566
|
+
owner = { references, registered: new WeakSet(), pruneAt: FIRST_PRUNE_AT };
|
|
567
|
+
statementOwners.set(db, owner);
|
|
568
|
+
const close = db.close.bind(db) as (...args: unknown[]) => unknown;
|
|
569
|
+
Object.defineProperty(db, "close", {
|
|
570
|
+
configurable: true,
|
|
571
|
+
writable: true,
|
|
572
|
+
value: (...args: unknown[]) => {
|
|
573
|
+
let failure: unknown;
|
|
574
|
+
for (const reference of references) {
|
|
575
|
+
try {
|
|
576
|
+
reference.deref()?.finalize();
|
|
577
|
+
} catch (error) {
|
|
578
|
+
failure ??= error;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
references.clear();
|
|
582
|
+
const result = close(...args);
|
|
583
|
+
if (failure) throw failure;
|
|
584
|
+
return result;
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (!owner.registered.has(finalizable)) {
|
|
589
|
+
// No FinalizationRegistry: its callbacks run at GC time, where a throw
|
|
590
|
+
// is uncaught (seen in Bun's parallel test runner). Sweep references to
|
|
591
|
+
// collected statements whenever the set doubles instead, so one-shot
|
|
592
|
+
// statements in a long-running host stay bounded.
|
|
593
|
+
if (owner.references.size >= owner.pruneAt) {
|
|
594
|
+
for (const reference of owner.references) {
|
|
595
|
+
if (reference.deref() === undefined) owner.references.delete(reference);
|
|
596
|
+
}
|
|
597
|
+
owner.pruneAt = Math.max(FIRST_PRUNE_AT, owner.references.size * 2);
|
|
598
|
+
}
|
|
599
|
+
owner.references.add(new WeakRef(finalizable));
|
|
600
|
+
owner.registered.add(finalizable);
|
|
601
|
+
}
|
|
602
|
+
return statement;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Cached statements must be finalized before closing their connection. Bun's
|
|
607
|
+
* prepare() leaves SQLite's native close deferred until all statements finish,
|
|
608
|
+
* even after the JS Database reports closed. Delayed WAL checkpoints can then
|
|
609
|
+
* overwrite a restored database if a caller reuses the file in the meantime.
|
|
610
|
+
* The shared Database owns every preparation; use this for caches that also
|
|
611
|
+
* accept direct bun:sqlite handles. Weak references avoid retaining one-shot
|
|
612
|
+
* statements for the lifetime of a long-running host.
|
|
613
|
+
*/
|
|
614
|
+
export function prepareCachedStatement(db: Database, sql: string): Statement {
|
|
615
|
+
return ownPreparedStatement(db, db.prepare(sql));
|
|
616
|
+
}
|
|
617
|
+
|
|
543
618
|
const privilegeDepth = new WeakMap<Database, number>();
|
|
544
619
|
const transformPassScope = new AsyncLocalStorage<
|
|
545
620
|
{ active: boolean; remainingWaitMs: number } | undefined
|
|
@@ -100,7 +100,7 @@ describe("status detail text", () => {
|
|
|
100
100
|
expect(formatStatusDetailMarkdown(STATUS_FIXTURE)).toBe(`## Magic Context Status
|
|
101
101
|
|
|
102
102
|
- **Context:** 75.0% of usable context (96,000 / 128,000 tokens)
|
|
103
|
-
- **Cache lifetime:** 1h (config
|
|
103
|
+
- **Cache lifetime:** 1h (your config)
|
|
104
104
|
- **Automatic compression:** at 65.0% of usable context
|
|
105
105
|
- **History compression:** Compressing history · 12 history blocks
|
|
106
106
|
- **Reclaimable:** 3 spent tool outputs (~14k tokens)
|
|
@@ -148,6 +148,23 @@ describe("status detail text", () => {
|
|
|
148
148
|
).not.toContain("MC-S05");
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
test("shows the primary quota deadline without exposing surrounding account diagnostics", () => {
|
|
152
|
+
const summary = formatStatusDetailMarkdown({
|
|
153
|
+
...STATUS_FIXTURE,
|
|
154
|
+
dreamerFailures: [
|
|
155
|
+
{
|
|
156
|
+
task: "verify",
|
|
157
|
+
error: "primary quota exhausted until 2026-10-08T03:40:04.275Z; account=private@example.com",
|
|
158
|
+
lastSucceededAt: null,
|
|
159
|
+
retryCount: 1,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
});
|
|
163
|
+
expect(summary).toContain("primary quota exhausted until 2026-10-08T03:40:04.275Z");
|
|
164
|
+
expect(summary).not.toContain("private@example.com");
|
|
165
|
+
expect(summary).not.toContain("manifest missing");
|
|
166
|
+
});
|
|
167
|
+
|
|
151
168
|
test("keeps internal vocabulary and identifiers out of the summary", () => {
|
|
152
169
|
const summary = formatStatusDetailMarkdown({
|
|
153
170
|
...STATUS_FIXTURE,
|