@cortexkit/opencode-magic-context 0.46.1 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/magic-context-prompt.d.ts +1 -1
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/{announcement-y35wzg2r.js → announcement-fp14dw0n.js} +3 -2
- package/dist/auto-search-worker.js +112 -0
- 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/mural-model-deprecation.d.ts +15 -0
- package/dist/config/mural-model-deprecation.d.ts.map +1 -0
- package/dist/config/project-security.d.ts +0 -3
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +21 -9
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-7fj2b9jw.js → conflict-warning-hook-w8cykrnf.js} +12 -9
- package/dist/{context-sh2m2vde.js → context-m0cy262m.js} +1785 -913
- package/dist/{derive-budgets-7nj7r1rv.js → derive-budgets-sg0xsq7s.js} +4 -3
- package/dist/embedding-activity-g1dhes6g.js +10 -0
- package/dist/embedding-backfill-9rcm1mfy.js +21 -0
- package/dist/embedding-cache-mgyr73ef.js +16 -0
- package/dist/embedding-worker.js +11 -9
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +1 -0
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-config.d.ts +3 -3
- package/dist/features/magic-context/dreamer/task-config.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts +1 -1
- 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-registry.d.ts +1 -0
- package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-async.d.ts +16 -3
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-bounded.d.ts +17 -0
- package/dist/features/magic-context/message-index-bounded.d.ts.map +1 -0
- package/dist/features/magic-context/message-index.d.ts +7 -1
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/mural/render-trigger.d.ts +7 -4
- package/dist/features/magic-context/mural/render-trigger.d.ts.map +1 -1
- package/dist/features/magic-context/overflow-detection.d.ts +2 -0
- package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
- package/dist/features/magic-context/pi-content-decisions.d.ts +7 -1
- package/dist/features/magic-context/pi-content-decisions.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +7 -0
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts +31 -0
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/session-activity.d.ts.map +1 -1
- package/dist/features/magic-context/session-project-storage.d.ts +8 -2
- package/dist/features/magic-context/session-project-storage.d.ts.map +1 -1
- 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/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-historian-runs.d.ts +2 -5
- package/dist/features/magic-context/storage-historian-runs.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +12 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts +6 -0
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tag-identity-revision.d.ts +30 -0
- package/dist/features/magic-context/storage-tag-identity-revision.d.ts.map +1 -0
- package/dist/features/magic-context/storage-tags.d.ts +148 -3
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +1 -0
- 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/transform-decision-log.d.ts +4 -2
- package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
- package/dist/harness-e4avh192.js +10 -0
- package/dist/hooks/magic-context/active-anthropic-turn.d.ts +10 -0
- package/dist/hooks/magic-context/active-anthropic-turn.d.ts.map +1 -0
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-blocking.fixture.d.ts +2 -0
- package/dist/hooks/magic-context/auto-search-blocking.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-deadline.d.ts +7 -0
- package/dist/hooks/magic-context/auto-search-deadline.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-host-review.fixture.d.ts +3 -0
- package/dist/hooks/magic-context/auto-search-host-review.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-lock-review.fixture.d.ts +2 -0
- package/dist/hooks/magic-context/auto-search-lock-review.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-long-lock-review.fixture.d.ts +2 -0
- package/dist/hooks/magic-context/auto-search-long-lock-review.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-omp-host-review.fixture.d.ts +4 -0
- package/dist/hooks/magic-context/auto-search-omp-host-review.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-omp-loader.fixture.d.ts +3 -0
- package/dist/hooks/magic-context/auto-search-omp-loader.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-runner.d.ts +1 -1
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-snapshot.fixture.d.ts +4 -0
- package/dist/hooks/magic-context/auto-search-snapshot.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-worker-client.d.ts +98 -0
- package/dist/hooks/magic-context/auto-search-worker-client.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-worker.d.ts +2 -0
- package/dist/hooks/magic-context/auto-search-worker.d.ts.map +1 -0
- package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts +1 -0
- package/dist/hooks/magic-context/command-handler.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 +3 -5
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts +2 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts +3 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +2 -0
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/historian-drain-gate.d.ts +11 -0
- package/dist/hooks/magic-context/historian-drain-gate.d.ts.map +1 -0
- package/dist/hooks/magic-context/hook.d.ts +3 -2
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/latest-assistant-turn.d.ts +25 -0
- package/dist/hooks/magic-context/latest-assistant-turn.d.ts.map +1 -0
- package/dist/hooks/magic-context/latest-thinking-recovery.d.ts +61 -0
- package/dist/hooks/magic-context/latest-thinking-recovery.d.ts.map +1 -0
- package/dist/hooks/magic-context/lkg-persist.d.ts +4 -3
- package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-replay.d.ts +13 -8
- package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-slot.d.ts +139 -2
- package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
- package/dist/hooks/magic-context/message-search-accepting-silent-worker.fixture.d.ts +2 -0
- package/dist/hooks/magic-context/message-search-accepting-silent-worker.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
- package/dist/hooks/magic-context/oc-stall-r2-warmup-worker.fixture.d.ts +2 -0
- package/dist/hooks/magic-context/oc-stall-r2-warmup-worker.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/oc-stall-search-review.fixture.d.ts +2 -0
- package/dist/hooks/magic-context/oc-stall-search-review.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/opencode-proven-floor.d.ts +6 -0
- package/dist/hooks/magic-context/opencode-proven-floor.d.ts.map +1 -0
- package/dist/hooks/magic-context/opencode1-to-model-messages.fixture.d.ts +63 -0
- package/dist/hooks/magic-context/opencode1-to-model-messages.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/pass-outcome.d.ts.map +1 -1
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
- package/dist/hooks/magic-context/provider-visible-parts.d.ts +45 -0
- package/dist/hooks/magic-context/provider-visible-parts.d.ts.map +1 -0
- package/dist/hooks/magic-context/raw-ordinal-warmup-silent-worker.fixture.d.ts +2 -0
- package/dist/hooks/magic-context/raw-ordinal-warmup-silent-worker.fixture.d.ts.map +1 -0
- package/dist/hooks/magic-context/raw-ordinal-warmup-worker.d.ts +2 -0
- package/dist/hooks/magic-context/raw-ordinal-warmup-worker.d.ts.map +1 -0
- package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts +87 -0
- package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts.map +1 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts +12 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-formatting.d.ts +5 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-raw.d.ts +83 -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.map +1 -1
- package/dist/hooks/magic-context/reasoning-budget-status.d.ts +15 -0
- package/dist/hooks/magic-context/reasoning-budget-status.d.ts.map +1 -0
- package/dist/hooks/magic-context/reasoning-budget.d.ts +40 -0
- package/dist/hooks/magic-context/reasoning-budget.d.ts.map +1 -0
- package/dist/hooks/magic-context/reasoning-removal.d.ts +6 -6
- package/dist/hooks/magic-context/reasoning-removal.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/sentinel.d.ts +45 -1
- package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +21 -5
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts +1 -0
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +21 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-owner-warmup.d.ts +6 -0
- package/dist/hooks/magic-context/tag-owner-warmup.d.ts.map +1 -0
- package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -1
- package/dist/hooks/magic-context/token-count-exact.d.ts +35 -0
- package/dist/hooks/magic-context/token-count-exact.d.ts.map +1 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts +5 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +3 -28
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +28 -6
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +11 -3
- 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-hpecdzec.js → index-0an7742d.js} +6820 -6587
- package/dist/index-0qhce371.js +120 -0
- package/dist/{index-q6hef7fh.js → index-0sahzkwr.js} +6 -19
- package/dist/{index-k5bdx1bx.js → index-0w56v4fm.js} +3 -5
- package/dist/index-14cjxt20.js +819 -0
- package/dist/{v2/server-51dv1pr5.js → index-1qb0z7wa.js} +301 -27
- package/dist/index-2bvekkfy.js +11 -0
- package/dist/{index-sj29b3z2.js → index-2y0s2hep.js} +3 -3
- package/dist/index-4dhv7mqz.js +131 -0
- package/dist/{index-b35pxds2.js → index-58e3y90n.js} +1 -18
- package/dist/index-5zygecde.js +152 -0
- package/dist/{index-sjthx78w.js → index-692jzy0p.js} +16879 -22907
- package/dist/index-72nhv486.js +2307 -0
- package/dist/index-7jzrrxap.js +931 -0
- package/dist/index-7mx8v0pw.js +3591 -0
- package/dist/index-8ajstryz.js +18 -0
- package/dist/{index-sfpzqvdz.js → index-8cd2j99z.js} +2 -3
- package/dist/{index-hybnrwq0.js → index-8j7gqj40.js} +131 -128
- package/dist/index-bdckkfw6.js +3261 -0
- package/dist/{index-8x2v7gg9.js → index-bhvjj0ys.js} +158 -2455
- package/dist/index-c43kr12f.js +3030 -0
- package/dist/index-cb7emxgj.js +984 -0
- package/dist/index-d9vfe7xq.js +16 -0
- package/dist/{index-5gq3b2ya.js → index-db7zwa3v.js} +147 -209
- package/dist/index-dhwzyd54.js +1142 -0
- package/dist/{index-8da2zjhz.js → index-eeezr5zz.js} +9 -7
- package/dist/{index-pkbcrdqs.js → index-f0h6xwqm.js} +3412 -4552
- package/dist/index-fb3zxn06.js +13 -0
- package/dist/index-fbv1dn35.js +171 -0
- package/dist/index-fr0wbhdn.js +43 -0
- package/dist/{index-mc22ayvf.js → index-ghc9f0n7.js} +11 -9
- package/dist/{index-fzbvakh0.js → index-jcbd3znx.js} +3 -3
- package/dist/{index-5p8jdswx.js → index-k1xb52kd.js} +20 -13
- package/dist/index-k9nnwad5.js +3532 -0
- package/dist/{v2/server-fg34g6a5.js → index-kb9nn0a0.js} +309 -52
- package/dist/index-knh4y330.js +566 -0
- package/dist/{index-q4yp4b52.js → index-mfkzp7qj.js} +28 -28
- package/dist/{index-sgthv3f7.js → index-mmyh6d8f.js} +4 -4
- package/dist/index-pavzsdc6.js +17 -0
- package/dist/{index-0d3g3461.js → index-pdecwzrd.js} +19 -16
- package/dist/index-pg50n748.js +1060 -0
- package/dist/index-q2tqeqde.js +2106 -0
- package/dist/index-q7h7csab.js +3469 -0
- package/dist/index-qt1qqasc.js +22 -0
- package/dist/{index-majxda46.js → index-ty64eqcn.js} +28 -18
- package/dist/{index-7qw742ev.js → index-yq8n9qcb.js} +25 -44
- package/dist/{index-sfnj6h8h.js → index-z8e0psj8.js} +20 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1061 -260
- package/dist/logger-d3wex3dz.js +20 -0
- package/dist/message-index-bounded-7ezbgddp.js +124 -0
- package/dist/{v2/message-time-backfill-r6qkem1x.js → message-time-backfill-brbd63hv.js} +8 -6
- package/dist/migration-worker.js +25 -16
- package/dist/{model-resolution-xhfmyyvy.js → model-resolution-nx97z3x7.js} +1 -1
- package/dist/plugin/dream-timer.d.ts +1 -1
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/host-stall-profiler-report.d.ts +2 -0
- package/dist/plugin/host-stall-profiler-report.d.ts.map +1 -0
- package/dist/plugin/host-stall-profiler.d.ts +3 -0
- package/dist/plugin/host-stall-profiler.d.ts.map +1 -0
- package/dist/plugin/messages-transform.d.ts +21 -19
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/project-embedding-registry-7qzptcw7.js +88 -0
- package/dist/raw-ordinal-warmup-worker.js +60 -0
- package/dist/{read-session-chunk-egz7r68d.js → read-session-chunk-6k83m4fj.js} +15 -9
- package/dist/read-session-raw-3g9qy1xa.js +58 -0
- package/dist/recomp-orchestrator-d3fp1cy0.js +57 -0
- package/dist/{v2/safe-notification-target-eb1n1vfb.js → safe-notification-target-5ffqq32m.js} +4 -2
- package/dist/search-epax1ecv.js +45 -0
- package/dist/{v2/send-session-notification-qs0j78v6.js → send-session-notification-erecz4y4.js} +10 -6
- package/dist/shared/auto-search-lifecycle.d.ts +7 -0
- package/dist/shared/auto-search-lifecycle.d.ts.map +1 -0
- package/dist/shared/conflict-detector.d.ts.map +1 -1
- package/dist/shared/historian-tool-expansions.d.ts +1 -1
- package/dist/shared/historian-tool-expansions.d.ts.map +1 -1
- package/dist/shared/historian-tool-template.d.ts +2 -2
- package/dist/shared/historian-tool-template.d.ts.map +1 -1
- package/dist/shared/host-stall-profiler-bun.d.ts +46 -0
- package/dist/shared/host-stall-profiler-bun.d.ts.map +1 -0
- package/dist/shared/host-stall-profiler-common.d.ts +58 -0
- package/dist/shared/host-stall-profiler-common.d.ts.map +1 -0
- package/dist/shared/host-stall-profiler-node.d.ts +36 -0
- package/dist/shared/host-stall-profiler-node.d.ts.map +1 -0
- package/dist/shared/host-stall-profiler-report.d.ts +68 -0
- package/dist/shared/host-stall-profiler-report.d.ts.map +1 -0
- package/dist/shared/host-stall-profiler.d.ts +5 -0
- package/dist/shared/host-stall-profiler.d.ts.map +1 -0
- package/dist/shared/model-resolution.d.ts +2 -3
- package/dist/shared/model-resolution.d.ts.map +1 -1
- package/dist/shared/sqlite.d.ts +46 -1
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/stable-json.d.ts.map +1 -1
- package/dist/shared/status-view.d.ts.map +1 -1
- package/dist/shared/user-facing-codes.d.ts +0 -5
- package/dist/shared/user-facing-codes.d.ts.map +1 -1
- package/dist/shared/window-geometry.d.ts +4 -0
- package/dist/shared/window-geometry.d.ts.map +1 -1
- package/dist/sqlite-context-pragmas-mv9jqwjh.js +6 -0
- package/dist/sqlite-mhjkerzq.js +46 -0
- package/dist/storage-memory-07fx57k6.js +71 -0
- package/dist/storage-tags-d3b0v2ss.js +131 -0
- package/dist/{task-config-v81k8jvk.js → task-config-cgpg0nvh.js} +2 -2
- package/dist/tools/ctx-expand/render.d.ts.map +1 -1
- package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
- package/dist/tools/ctx-note/constants.d.ts +4 -0
- package/dist/tools/ctx-note/constants.d.ts.map +1 -1
- package/dist/tools/ctx-note/tools.d.ts +1 -0
- package/dist/tools/ctx-note/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/dist/tools/parameter-descriptions.d.ts +2 -2
- package/dist/tools/parameter-descriptions.d.ts.map +1 -1
- package/dist/{transform-postprocess-phase-bqhz6vdr.js → transform-postprocess-phase-spcy2qre.js} +35 -17
- package/dist/transformers-node-wasm.js +18415 -18415
- package/dist/v2/auto-search-worker.js +112 -0
- package/dist/v2/{context-zecmzk5s.js → context-sf98kstd.js} +8378 -8530
- package/dist/v2/{derive-budgets-kdgp4s3r.js → derive-budgets-3vc0y84t.js} +4 -3
- package/dist/v2/embedding-activity-g1dhes6g.js +10 -0
- package/dist/v2/embedding-backfill-vn8g1nh1.js +21 -0
- package/dist/v2/embedding-cache-mgyr73ef.js +16 -0
- package/dist/v2/embedding-worker.js +12 -9
- package/dist/v2/fold/host-media.d.ts +4 -0
- package/dist/v2/fold/host-media.d.ts.map +1 -1
- package/dist/v2/fold/memory-cache.d.ts +70 -0
- package/dist/v2/fold/memory-cache.d.ts.map +1 -0
- package/dist/v2/fold/native-codec.d.ts +10 -0
- package/dist/v2/fold/native-codec.d.ts.map +1 -0
- package/dist/v2/fold/native-replay.d.ts +58 -0
- package/dist/v2/fold/native-replay.d.ts.map +1 -0
- package/dist/v2/fold/policy.d.ts +24 -0
- package/dist/v2/fold/policy.d.ts.map +1 -0
- package/dist/v2/harness-e4avh192.js +10 -0
- package/dist/v2/hidden-completion.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-manual.d.ts +1 -1
- package/dist/v2/hooks/dream-manual.d.ts.map +1 -1
- package/dist/v2/hooks/dream-timer.d.ts.map +1 -1
- package/dist/v2/hooks/dream-trigger.d.ts +1 -2
- package/dist/v2/hooks/dream-trigger.d.ts.map +1 -1
- package/dist/v2/hooks/payload.d.ts.map +1 -1
- package/dist/v2/hooks/tools.d.ts.map +1 -1
- package/dist/v2/hooks/types.d.ts +6 -0
- package/dist/v2/hooks/types.d.ts.map +1 -1
- package/dist/v2/logger-mxd0ym09.js +19 -0
- package/dist/v2/message-index-bounded-dy3s2k1e.js +123 -0
- package/dist/{message-time-backfill-68gj6t3e.js → v2/message-time-backfill-gtx1pwk1.js} +7 -7
- package/dist/v2/migration-worker.js +24 -15
- package/dist/v2/{model-resolution-xhfmyyvy.js → model-resolution-nx97z3x7.js} +1 -1
- package/dist/v2/project-embedding-registry-1tgba5dv.js +86 -0
- package/dist/v2/raw-ordinal-warmup-worker.js +60 -0
- package/dist/v2/{read-session-chunk-8vjp6581.js → read-session-chunk-rh4khdkv.js} +14 -8
- package/dist/v2/read-session-raw-bsbmf0vj.js +57 -0
- package/dist/v2/recomp-orchestrator-az61rfcg.js +57 -0
- package/dist/{safe-notification-target-43r8jsxw.js → v2/safe-notification-target-73ax1p5v.js} +3 -3
- package/dist/v2/search-nzkwrc6n.js +45 -0
- package/dist/{send-session-notification-dyw42dtr.js → v2/send-session-notification-6km439kn.js} +9 -7
- package/dist/{index-5eqk6z3s.js → v2/server-1r6pmz4f.js} +301 -27
- package/dist/v2/server-2bvekkfy.js +11 -0
- package/dist/v2/server-2fbhp79z.js +3205 -0
- package/dist/v2/{server-ajzeteqw.js → server-2ra6pwf3.js} +1 -18
- package/dist/v2/server-4dhv7mqz.js +131 -0
- package/dist/v2/{server-xa4xe3rv.js → server-4k748ke6.js} +11 -1053
- package/dist/v2/{server-vs5gpnd4.js → server-52awr2mw.js} +81 -145
- package/dist/v2/{server-1qbnzc80.js → server-5m3dt5r1.js} +136 -148
- package/dist/v2/server-686jg6eb.js +3508 -0
- package/dist/v2/server-7jzrrxap.js +931 -0
- package/dist/v2/server-7wwc2nga.js +3023 -0
- package/dist/v2/server-8ajstryz.js +18 -0
- package/dist/v2/{server-sfpzqvdz.js → server-8cd2j99z.js} +2 -3
- package/dist/v2/server-8pyt7hev.js +3408 -0
- package/dist/v2/server-8tqanswc.js +984 -0
- package/dist/v2/{server-14vq4kpm.js → server-aan4q8mx.js} +3 -3
- package/dist/v2/server-d9vfe7xq.js +16 -0
- package/dist/v2/server-e0vdaae2.js +3591 -0
- package/dist/v2/server-ezhze64f.js +1048 -0
- package/dist/v2/server-fb3zxn06.js +13 -0
- package/dist/v2/server-fr0wbhdn.js +43 -0
- package/dist/v2/server-g5r0n3b6.js +1137 -0
- package/dist/v2/server-gjcr4w98.js +154 -0
- package/dist/v2/server-k228xhjg.js +171 -0
- package/dist/v2/{server-jc7y02q0.js → server-k8x2rbxc.js} +5119 -10948
- package/dist/{index-fg34g6a5.js → v2/server-kb9nn0a0.js} +309 -52
- package/dist/v2/{server-g0fjrkta.js → server-mfkzp7qj.js} +28 -28
- package/dist/v2/server-pavzsdc6.js +17 -0
- package/dist/v2/server-psqc7e9n.js +2106 -0
- package/dist/v2/server-qt1qqasc.js +22 -0
- package/dist/v2/server-qye6ykf7.js +120 -0
- package/dist/v2/{server-qdjnb2z1.js → server-r5p3b1wy.js} +11 -9
- package/dist/v2/server-sggvvzhb.js +759 -0
- package/dist/v2/{server-y8maxeft.js → server-v06jcv0w.js} +16 -13
- package/dist/v2/server-vqpf3167.js +1048 -0
- package/dist/v2/{server-c5gz8wph.js → server-x2g40a71.js} +2048 -2059
- package/dist/v2/{server-wvjvfhck.js → server-xdfwt6kh.js} +20 -16
- package/dist/v2/{server-mcfawjgc.js → server-xxm59eww.js} +25 -44
- package/dist/v2/server-yczc713m.js +564 -0
- package/dist/v2/{server-3n4q9xme.js → server-z2037m6z.js} +4 -4
- package/dist/v2/{server-e29d7p7c.js → server-zm570tr2.js} +166 -160
- package/dist/v2/{server-6r257mf5.js → server-zxhgvsf9.js} +3 -3
- package/dist/v2/server.js +16 -14
- package/dist/v2/sqlite-context-pragmas-mv9jqwjh.js +6 -0
- package/dist/v2/sqlite-mhjkerzq.js +46 -0
- package/dist/v2/storage-memory-07fx57k6.js +71 -0
- package/dist/v2/storage-tags-w4tv77sn.js +130 -0
- package/dist/v2/store-reader.d.ts +16 -0
- package/dist/v2/store-reader.d.ts.map +1 -1
- package/dist/v2/{task-config-tbndqyny.js → task-config-x0hq3qfd.js} +2 -2
- package/dist/v2/transformers-node-wasm.js +18415 -18415
- package/dist/v2/tui/index.d.ts.map +1 -1
- package/dist/v2/tui/types.d.ts +1 -10
- package/dist/v2/tui/types.d.ts.map +1 -1
- package/dist/v2/{wrapup-orchestrator-0snhvwcv.js → wrapup-orchestrator-mz7efs7w.js} +51 -27
- package/dist/wrapup-orchestrator-amzta1t4.js +44 -0
- package/package.json +4 -3
- package/src/agents/magic-context-prompt.ts +27 -2
- package/src/agents/smart-notes-enabled.test.ts +294 -0
- package/src/agents/smart-notes-on.golden.json +70 -0
- package/src/config/index.test.ts +67 -1
- package/src/config/index.ts +8 -0
- package/src/config/latch-permanence-guard.test.ts +4 -0
- package/src/config/live-run-config.test.ts +37 -1
- package/src/config/live-run-config.ts +28 -0
- package/src/config/migrate-config-location.ts +10 -3
- package/src/config/mural-model-deprecation.ts +24 -0
- package/src/config/project-security.hostile-matrix.test.ts +4 -44
- package/src/config/project-security.test.ts +9 -14
- package/src/config/project-security.ts +13 -55
- package/src/config/schema/magic-context.test.ts +6 -5
- package/src/config/schema/magic-context.ts +34 -16
- package/src/features/magic-context/dreamer/task-registry.ts +6 -0
- package/src/shared/auto-search-lifecycle.ts +27 -0
- package/src/shared/conflict-detector.test.ts +56 -0
- package/src/shared/conflict-detector.ts +16 -5
- package/src/shared/historian-tool-defaults.json +6 -6
- package/src/shared/historian-tool-expansions.ts +65 -8
- package/src/shared/historian-tool-preview-defaults.json +13 -0
- package/src/shared/historian-tool-template.ts +48 -14
- package/src/shared/host-stall-profiler-bun.ts +358 -0
- package/src/shared/host-stall-profiler-common.ts +159 -0
- package/src/shared/host-stall-profiler-node.test.ts +234 -0
- package/src/shared/host-stall-profiler-node.ts +274 -0
- package/src/shared/host-stall-profiler-report.ts +248 -0
- package/src/shared/host-stall-profiler.ts +57 -0
- package/src/shared/model-resolution.test.ts +54 -17
- package/src/shared/model-resolution.ts +6 -17
- package/src/shared/sqlite-640-review-r2.test.ts +136 -0
- package/src/shared/sqlite-640-review.test.ts +183 -0
- package/src/shared/sqlite-acquisition-routing.test.ts +18 -0
- package/src/shared/sqlite.test.ts +52 -0
- package/src/shared/sqlite.ts +423 -37
- package/src/shared/stable-json-linear.test.ts +109 -0
- package/src/shared/stable-json.ts +37 -7
- package/src/shared/status-view.test.ts +6 -1
- package/src/shared/status-view.ts +15 -1
- package/src/shared/tag-transcript.ts +6 -0
- package/src/shared/user-facing-codes.ts +2 -6
- package/src/shared/window-geometry.ts +10 -0
- package/src/shared/write-transaction-attribution.test.ts +2 -2
- package/src/v2/tui/host-contract.test.ts +41 -67
- package/src/v2/tui/index.ts +6 -214
- package/src/v2/tui/types.ts +5 -17
- package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts +0 -45
- package/dist/features/magic-context/checkout-claim-fake-subc.test-support.d.ts.map +0 -1
- package/dist/features/magic-context/checkout-claim.d.ts +0 -155
- package/dist/features/magic-context/checkout-claim.d.ts.map +0 -1
- package/dist/index-3h0ybwah.js +0 -6540
- package/dist/index-r5hmbx3t.js +0 -7773
- package/dist/recomp-orchestrator-y15j0rb9.js +0 -39
- package/dist/v2/recomp-orchestrator-8ejxvrzz.js +0 -39
- package/dist/v2/server-frbm99q5.js +0 -7698
- package/dist/v2/server-qp5hx1ad.js +0 -6366
- package/dist/wrapup-orchestrator-39amhv9s.js +0 -26
- /package/dist/{index-en1k8h8m.js → index-2pp04pmg.js} +0 -0
- /package/dist/v2/{index-en1k8h8m.js → index-2pp04pmg.js} +0 -0
|
@@ -11,10 +11,7 @@ import {
|
|
|
11
11
|
withLiveDreamerOutputCap,
|
|
12
12
|
renderUserFacingFailure,
|
|
13
13
|
userFacingFailureCode,
|
|
14
|
-
advanceSessionActivity,
|
|
15
|
-
backfillSessionActivity,
|
|
16
14
|
invalidateAutoEmbedSession,
|
|
17
|
-
deleteChunkEmbedBackoffForSession,
|
|
18
15
|
acquireCompartmentLease,
|
|
19
16
|
releaseCompartmentLeaseBestEffort,
|
|
20
17
|
isCompartmentLeaseHeld,
|
|
@@ -60,6 +57,7 @@ import {
|
|
|
60
57
|
HiddenChildHook,
|
|
61
58
|
parseProviderModel,
|
|
62
59
|
toModelEntry,
|
|
60
|
+
CTX_NOTE_LIGHT_DESCRIPTION,
|
|
63
61
|
ACTIVE_TOOL_IDS,
|
|
64
62
|
createPromptSurfaceRuntime,
|
|
65
63
|
runTokenLog,
|
|
@@ -69,17 +67,19 @@ import {
|
|
|
69
67
|
resetPrefixTrimFallbackState,
|
|
70
68
|
resolveOpenCodeProtectedTailBoundary,
|
|
71
69
|
getActiveCompartmentRun
|
|
72
|
-
} from "./index-
|
|
70
|
+
} from "./index-692jzy0p.js";
|
|
73
71
|
import"./index-sd3egckz.js";
|
|
74
72
|
import {
|
|
75
|
-
|
|
73
|
+
getHarness2
|
|
74
|
+
} from "./index-pavzsdc6.js";
|
|
75
|
+
import {
|
|
76
76
|
getDataDir,
|
|
77
77
|
getMagicContextStorageDir
|
|
78
|
-
} from "./index-
|
|
78
|
+
} from "./index-0sahzkwr.js";
|
|
79
79
|
import {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
} from "./index-
|
|
80
|
+
log2,
|
|
81
|
+
sessionLog2
|
|
82
|
+
} from "./index-8j7gqj40.js";
|
|
83
83
|
import {
|
|
84
84
|
isSaneLimit,
|
|
85
85
|
setOutputReserveConfig,
|
|
@@ -89,9 +89,9 @@ import {
|
|
|
89
89
|
deriveHistorianChunkTokens2,
|
|
90
90
|
resolveKnownHistorianContextLimit2,
|
|
91
91
|
resolveHistorianContextLimit2
|
|
92
|
-
} from "./index-
|
|
93
|
-
import"./index-
|
|
94
|
-
import"./index-
|
|
92
|
+
} from "./index-pdecwzrd.js";
|
|
93
|
+
import"./index-bhvjj0ys.js";
|
|
94
|
+
import"./index-mfkzp7qj.js";
|
|
95
95
|
import {
|
|
96
96
|
CANONICAL_DREAM_TASKS,
|
|
97
97
|
DREAM_TASK_CAPABILITIES,
|
|
@@ -99,66 +99,86 @@ import {
|
|
|
99
99
|
isCanonicalDreamTask,
|
|
100
100
|
buildDreamTaskRuntimeConfigs2,
|
|
101
101
|
userMemoryCollectionEnabled2
|
|
102
|
-
} from "./index-
|
|
102
|
+
} from "./index-ghc9f0n7.js";
|
|
103
103
|
import {
|
|
104
104
|
resolveHistorianModel2
|
|
105
|
-
} from "./index-
|
|
105
|
+
} from "./index-8cd2j99z.js";
|
|
106
106
|
import {
|
|
107
107
|
formatFailClosedBlockingMessage,
|
|
108
108
|
formatFailClosedBlockingSummary,
|
|
109
109
|
createFailClosedBlockingError,
|
|
110
110
|
isFailClosedBlockingError,
|
|
111
|
-
setHomeProjectPermission,
|
|
112
|
-
resolveProjectIdentityForSession,
|
|
113
|
-
scheduleAfterBootQuiet,
|
|
114
111
|
startBootDeadline,
|
|
115
112
|
BOOT_SQLITE_BUSY_TIMEOUT_MS,
|
|
116
113
|
openCurrentDatabase,
|
|
117
114
|
openDatabaseAsync,
|
|
118
115
|
isDatabasePersisted,
|
|
119
116
|
getDatabasePersistenceError
|
|
120
|
-
} from "./index-
|
|
117
|
+
} from "./index-q7h7csab.js";
|
|
118
|
+
import {
|
|
119
|
+
setHomeProjectPermission,
|
|
120
|
+
resolveProjectIdentityForSession,
|
|
121
|
+
scheduleAfterBootQuiet
|
|
122
|
+
} from "./index-knh4y330.js";
|
|
121
123
|
import {
|
|
122
|
-
isDroppedToolOutput,
|
|
123
|
-
evaluateChannel2,
|
|
124
|
-
reclaimableToolOutputCount,
|
|
125
|
-
buildChannel2Reminder,
|
|
126
124
|
markHostUnservedRow,
|
|
127
125
|
DreamTokenBudgetExceeded,
|
|
128
126
|
createDreamTokenBudget,
|
|
129
127
|
recordToolParameters,
|
|
130
128
|
createDroppedInputGuard,
|
|
131
129
|
assertExecutableToolInput,
|
|
130
|
+
estimateFinalWireInputTokens,
|
|
132
131
|
detectOverflow,
|
|
132
|
+
modelAcceptsEmptyContent,
|
|
133
133
|
hasRawMessageProvider2,
|
|
134
134
|
setBoundedRawMessageProvider2,
|
|
135
135
|
cleanUserText2,
|
|
136
136
|
readRawSessionMessageIdOrdinalsForRange2,
|
|
137
137
|
readRawSessionMessageOrdinalById2
|
|
138
|
-
} from "./index-
|
|
138
|
+
} from "./index-bdckkfw6.js";
|
|
139
139
|
import {
|
|
140
140
|
hasMeaningfulUserText2,
|
|
141
141
|
preloadTokenizer,
|
|
142
142
|
estimateTokens
|
|
143
|
-
} from "./index-
|
|
143
|
+
} from "./index-1qb0z7wa.js";
|
|
144
|
+
import"./index-72nhv486.js";
|
|
145
|
+
import {
|
|
146
|
+
withSqliteTransformPass2,
|
|
147
|
+
withoutSqliteTransformPass2,
|
|
148
|
+
isTransientSqliteError2,
|
|
149
|
+
withAsyncPrivilegedWriter2
|
|
150
|
+
} from "./index-kb9nn0a0.js";
|
|
151
|
+
import"./index-fbv1dn35.js";
|
|
152
|
+
import {
|
|
153
|
+
deleteChunkEmbedBackoffForSession
|
|
154
|
+
} from "./index-pg50n748.js";
|
|
144
155
|
import {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
156
|
+
advanceSessionActivity,
|
|
157
|
+
backfillSessionActivity
|
|
158
|
+
} from "./index-14cjxt20.js";
|
|
159
|
+
import"./index-db7zwa3v.js";
|
|
160
|
+
import"./index-1zx87sdm.js";
|
|
161
|
+
import {
|
|
162
|
+
isDroppedToolOutput,
|
|
163
|
+
evaluateChannel2,
|
|
164
|
+
reclaimableToolOutputCount,
|
|
165
|
+
buildChannel2Reminder
|
|
166
|
+
} from "./index-k9nnwad5.js";
|
|
167
|
+
import"./index-dhwzyd54.js";
|
|
168
|
+
import"./index-58e3y90n.js";
|
|
169
|
+
import"./index-ty64eqcn.js";
|
|
150
170
|
import {
|
|
151
171
|
getCurrentToolSetHash,
|
|
152
172
|
recordToolDefinition
|
|
153
|
-
} from "./index-
|
|
154
|
-
import"./index-
|
|
155
|
-
import"./index-
|
|
156
|
-
import"./index-
|
|
173
|
+
} from "./index-0qhce371.js";
|
|
174
|
+
import"./index-fr0wbhdn.js";
|
|
175
|
+
import"./index-7mx8v0pw.js";
|
|
176
|
+
import"./index-fb3zxn06.js";
|
|
177
|
+
import"./index-c43kr12f.js";
|
|
178
|
+
import"./index-cb7emxgj.js";
|
|
157
179
|
import {
|
|
158
|
-
createSubcCheckoutClaimGate,
|
|
159
180
|
runSessionProjectBackfill,
|
|
160
181
|
createLiveSessionState,
|
|
161
|
-
getDefaultSubcConnectionFile,
|
|
162
182
|
RETROSPECTIVE_MAX_USER_MESSAGE_CHARS,
|
|
163
183
|
RETROSPECTIVE_MESSAGE_TRUNCATION_MARKER,
|
|
164
184
|
createDreamTaskExecutor,
|
|
@@ -176,6 +196,9 @@ import {
|
|
|
176
196
|
createToolExecuteAfterHook,
|
|
177
197
|
recordHiddenVariantWarning,
|
|
178
198
|
registerRpcHandlers,
|
|
199
|
+
armLatestThinkingRecoveryFromError,
|
|
200
|
+
lkgReplayLimit,
|
|
201
|
+
measureLkgReplay,
|
|
179
202
|
STORAGE_BUSY_MESSAGE,
|
|
180
203
|
StorageBusyRefusalError,
|
|
181
204
|
UnmanagedOverWindowError,
|
|
@@ -185,42 +208,50 @@ import {
|
|
|
185
208
|
createModuleToolBackends,
|
|
186
209
|
createRustRefusalRecovery,
|
|
187
210
|
createSystemPromptHashHandler,
|
|
211
|
+
tryMessagesTransformLkgReplay,
|
|
188
212
|
createMessagesTransformHandler,
|
|
189
213
|
subagentToolRefusal,
|
|
190
214
|
hideSubagentTools,
|
|
191
215
|
createCtxExpandTools,
|
|
192
216
|
createCtxMemoryTools,
|
|
193
217
|
createCtxMemoryListTools,
|
|
218
|
+
CTX_NOTE_DESCRIPTION,
|
|
219
|
+
withoutSmartNoteText,
|
|
194
220
|
createCtxNoteTools,
|
|
195
221
|
createCtxReduceTools,
|
|
196
222
|
createCtxSearchTools,
|
|
197
223
|
applyJsonSchemaParameterDescriptions,
|
|
198
224
|
MagicContextRpcServer
|
|
199
|
-
} from "./index-
|
|
225
|
+
} from "./index-0an7742d.js";
|
|
200
226
|
import {
|
|
201
227
|
createTagger,
|
|
202
228
|
setCtxReduceRegisteredGlobally,
|
|
203
229
|
resolveCtxReduceAvailabilityFromMessages,
|
|
204
|
-
DegradedPassRefusalError,
|
|
205
230
|
EmergencyFailClosedError,
|
|
231
|
+
DegradedPassRefusalError,
|
|
206
232
|
isSyntheticHeadMessage2
|
|
207
|
-
} from "./index-
|
|
208
|
-
import"./index-
|
|
233
|
+
} from "./index-f0h6xwqm.js";
|
|
234
|
+
import"./index-qt1qqasc.js";
|
|
235
|
+
import"./index-4dhv7mqz.js";
|
|
236
|
+
import"./index-5zygecde.js";
|
|
237
|
+
import"./index-7jzrrxap.js";
|
|
209
238
|
import {
|
|
210
239
|
pushNotification2,
|
|
211
240
|
isTuiConnected2
|
|
212
241
|
} from "./index-x8a7jmkh.js";
|
|
213
|
-
import"./index-
|
|
214
|
-
import"./index-
|
|
215
|
-
import"./index-
|
|
242
|
+
import"./index-jcbd3znx.js";
|
|
243
|
+
import"./index-yq8n9qcb.js";
|
|
244
|
+
import"./index-mmyh6d8f.js";
|
|
245
|
+
import"./index-0w56v4fm.js";
|
|
216
246
|
import {
|
|
217
247
|
detectConflicts
|
|
218
|
-
} from "./index-
|
|
248
|
+
} from "./index-eeezr5zz.js";
|
|
249
|
+
import"./index-q2tqeqde.js";
|
|
219
250
|
import"./index-fmsr7v69.js";
|
|
220
251
|
import {
|
|
221
252
|
getErrorMessage
|
|
222
253
|
} from "./index-y8v6yerm.js";
|
|
223
|
-
import"./index-
|
|
254
|
+
import"./index-2y0s2hep.js";
|
|
224
255
|
|
|
225
256
|
// src/hooks/magic-context/history-boundary-repair.ts
|
|
226
257
|
function readBaseline(db, sessionId) {
|
|
@@ -296,7 +327,7 @@ function repairMissingHistoryBoundary(args) {
|
|
|
296
327
|
if (anchor === "unknown")
|
|
297
328
|
return { kind: "unknown" };
|
|
298
329
|
if (anchor === null) {
|
|
299
|
-
|
|
330
|
+
sessionLog2(sessionId, `history boundary unresolved: ${problem} and no older compartment end is in the host store; nothing to re-anchor on`);
|
|
300
331
|
return { kind: "unresolved", reason: "no-anchor", missingEndMessageId: missing };
|
|
301
332
|
}
|
|
302
333
|
if (getActiveCompartmentRun(sessionId) !== undefined) {
|
|
@@ -318,7 +349,7 @@ function repairMissingHistoryBoundary(args) {
|
|
|
318
349
|
invalidateAutoEmbedSession(sessionId);
|
|
319
350
|
forgetCachedPrefix(sessionId);
|
|
320
351
|
const range = dropped.length === 0 ? "none" : `${dropped[0]}-${dropped[dropped.length - 1]} (${dropped.length})`;
|
|
321
|
-
|
|
352
|
+
sessionLog2(sessionId, `history boundary repair: ${problem}; re-anchored on compartment ${anchor.sequence} ending at ${anchor.endMessageId} (ordinal ${anchor.endMessage}); removed compartments ${range} for the historian to rebuild; the cached prefix is rebuilt on this pass`);
|
|
322
353
|
return {
|
|
323
354
|
kind: "repaired",
|
|
324
355
|
missingEndMessageId: missing,
|
|
@@ -334,7 +365,7 @@ function repairMissingHistoryBoundary(args) {
|
|
|
334
365
|
return { kind: "intact" };
|
|
335
366
|
clearCachedM0M1(db, sessionId);
|
|
336
367
|
forgetCachedPrefix(sessionId);
|
|
337
|
-
|
|
368
|
+
sessionLog2(sessionId, `history boundary repair: the cached prefix boundary ${baseline.boundary} is not in the host store; rebuilding it on this pass against compartment end ${latestEnd}`);
|
|
338
369
|
return {
|
|
339
370
|
kind: "baseline-reset",
|
|
340
371
|
anchorEndMessageId: latestEnd,
|
|
@@ -407,7 +438,7 @@ function createV2RustCompactionMarkerStrategy(findBoundaryUserMessage) {
|
|
|
407
438
|
targetEndMessageId: pending.endMessageId
|
|
408
439
|
};
|
|
409
440
|
setPersistedCompactionMarkerState(db, sessionId, state);
|
|
410
|
-
|
|
441
|
+
sessionLog2(sessionId, `v2 boundary recorded at ordinal ${pending.ordinal}, boundary message ${boundary.id}`);
|
|
411
442
|
return { kind: "applied", markerOrdinal: pending.ordinal };
|
|
412
443
|
}
|
|
413
444
|
};
|
|
@@ -539,7 +570,8 @@ function hostUsesMediaAssets() {
|
|
|
539
570
|
schemaUsesAssets.set(messageClass, uses);
|
|
540
571
|
return uses;
|
|
541
572
|
}
|
|
542
|
-
function decodeThroughHostSchema(
|
|
573
|
+
function decodeThroughHostSchema(encoded) {
|
|
574
|
+
const source = encoded.source;
|
|
543
575
|
const messageClass = rememberedMessageClass;
|
|
544
576
|
if (!messageClass)
|
|
545
577
|
return "no host message class seen";
|
|
@@ -559,7 +591,7 @@ function decodeThroughHostSchema(source) {
|
|
|
559
591
|
const run = decode?.run;
|
|
560
592
|
if (typeof run !== "function")
|
|
561
593
|
return "host media schema has no decode transformation";
|
|
562
|
-
const result = run.call(decode, { _tag: "Some", value:
|
|
594
|
+
const result = run.call(decode, { _tag: "Some", value: encoded }, {});
|
|
563
595
|
const option = result?._tag === "Success" ? result.value : undefined;
|
|
564
596
|
const asset = option?._tag === "Some" ? option.value : undefined;
|
|
565
597
|
if (!isClassInstance(asset) || asset.source !== source)
|
|
@@ -567,18 +599,30 @@ function decodeThroughHostSchema(source) {
|
|
|
567
599
|
return asset;
|
|
568
600
|
}
|
|
569
601
|
function hostMediaAsset(data, mediaType) {
|
|
570
|
-
|
|
602
|
+
return hostMediaFromEncoded({ source: { type: "base64", data, mediaType } });
|
|
603
|
+
}
|
|
604
|
+
function hostMediaFromEncoded(value) {
|
|
605
|
+
if (!isRecord(value.source))
|
|
606
|
+
return "missing Media.Asset source";
|
|
607
|
+
const encoded = structuredClone(value);
|
|
608
|
+
if (encoded.source.type === "bytes") {
|
|
609
|
+
if (typeof encoded.source.data !== "string")
|
|
610
|
+
return "bytes media has no JSON codec payload";
|
|
611
|
+
encoded.source.data = new Uint8Array(Buffer.from(encoded.source.data, "base64"));
|
|
612
|
+
}
|
|
613
|
+
if (!["bytes", "base64", "url", "ref"].includes(String(encoded.source.type)))
|
|
614
|
+
return "unknown Media.Asset source";
|
|
571
615
|
let first = "no Media.Asset seen in this process";
|
|
572
616
|
if (rememberedAssetClass) {
|
|
573
617
|
try {
|
|
574
|
-
return new rememberedAssetClass(
|
|
618
|
+
return new rememberedAssetClass(encoded);
|
|
575
619
|
} catch (error) {
|
|
576
620
|
first = `remembered asset class threw: ${String(error)}`;
|
|
577
621
|
}
|
|
578
622
|
}
|
|
579
623
|
let second;
|
|
580
624
|
try {
|
|
581
|
-
const decoded = decodeThroughHostSchema(
|
|
625
|
+
const decoded = decodeThroughHostSchema(encoded);
|
|
582
626
|
if (typeof decoded !== "string")
|
|
583
627
|
return decoded;
|
|
584
628
|
second = decoded;
|
|
@@ -588,6 +632,9 @@ function hostMediaAsset(data, mediaType) {
|
|
|
588
632
|
return `${first}; ${second}`;
|
|
589
633
|
}
|
|
590
634
|
|
|
635
|
+
// src/v2/fold/memory-cache.ts
|
|
636
|
+
import { randomUUID } from "node:crypto";
|
|
637
|
+
|
|
591
638
|
// src/v2/fold/owner.ts
|
|
592
639
|
import { createHash } from "node:crypto";
|
|
593
640
|
var foldDigest = (value) => createHash("sha256").update(value).digest("hex");
|
|
@@ -670,122 +717,777 @@ class FoldOwner {
|
|
|
670
717
|
}
|
|
671
718
|
}
|
|
672
719
|
|
|
673
|
-
// src/v2/fold/
|
|
674
|
-
|
|
720
|
+
// src/v2/fold/memory-cache.ts
|
|
721
|
+
var DEFAULT_NATIVE_FOLD_BUDGET = Object.freeze({
|
|
722
|
+
maxSessionBytes: 16 * 1024 * 1024,
|
|
723
|
+
maxSessions: 16
|
|
724
|
+
});
|
|
725
|
+
var stringBytes = (value) => (value?.length ?? 0) * 2;
|
|
675
726
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
727
|
+
class NativeFoldCache {
|
|
728
|
+
resolveDB;
|
|
729
|
+
sourceID = randomUUID();
|
|
730
|
+
tails = new Map;
|
|
731
|
+
nativeRows = new Map;
|
|
732
|
+
admissions = new Map;
|
|
733
|
+
retention = new Map;
|
|
734
|
+
evictionListeners = new Set;
|
|
735
|
+
budget;
|
|
736
|
+
constructor(resolveDB, budget = DEFAULT_NATIVE_FOLD_BUDGET) {
|
|
737
|
+
this.resolveDB = resolveDB;
|
|
738
|
+
if (!Number.isSafeInteger(budget.maxSessionBytes) || budget.maxSessionBytes < 1 || !Number.isSafeInteger(budget.maxSessions) || budget.maxSessions < 1)
|
|
739
|
+
throw new Error("Native fold cache budgets must be positive integers");
|
|
740
|
+
this.budget = { ...budget };
|
|
741
|
+
}
|
|
742
|
+
onEvict(listener) {
|
|
743
|
+
this.evictionListeners.add(listener);
|
|
744
|
+
}
|
|
745
|
+
touch(sessionID) {
|
|
746
|
+
const state = this.retention.get(sessionID) ?? { active: 0, bytes: 0, replayBytes: 0 };
|
|
747
|
+
this.retention.delete(sessionID);
|
|
748
|
+
this.retention.set(sessionID, state);
|
|
749
|
+
return state;
|
|
750
|
+
}
|
|
751
|
+
hold(sessionID) {
|
|
752
|
+
const state = this.touch(sessionID);
|
|
753
|
+
state.active++;
|
|
754
|
+
return () => {
|
|
755
|
+
state.active--;
|
|
756
|
+
this.enforceBudget();
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
accountReplay(sessionID, bytes) {
|
|
760
|
+
this.touch(sessionID).replayBytes = bytes;
|
|
761
|
+
this.recount(sessionID);
|
|
762
|
+
}
|
|
763
|
+
usage() {
|
|
764
|
+
return {
|
|
765
|
+
sessions: this.retention.size,
|
|
766
|
+
bytes: [...this.retention.values()].reduce((total, state) => total + state.bytes, 0)
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
recount(sessionID) {
|
|
770
|
+
const state = this.touch(sessionID);
|
|
771
|
+
let bytes = 256 + stringBytes(sessionID) + state.replayBytes;
|
|
772
|
+
const tail = this.tails.get(sessionID);
|
|
773
|
+
if (tail)
|
|
774
|
+
bytes += 128 + stringBytes(tail.data) + stringBytes(tail.digest);
|
|
775
|
+
for (const row of this.nativeRows.get(sessionID)?.values() ?? [])
|
|
776
|
+
bytes += 256 + stringBytes(row.id) + stringBytes(row.type) + stringBytes(row.data) + stringBytes(row.digest);
|
|
777
|
+
for (const row of this.admissions.get(sessionID)?.values() ?? [])
|
|
778
|
+
bytes += 256 + stringBytes(row.admission_id) + stringBytes(row.status) + stringBytes(row.owner_token) + stringBytes(row.data) + stringBytes(row.payload) + stringBytes(row.digest);
|
|
779
|
+
state.bytes = bytes;
|
|
780
|
+
this.enforceBudget();
|
|
781
|
+
}
|
|
782
|
+
enforceBudget() {
|
|
783
|
+
for (const [id, state] of this.retention)
|
|
784
|
+
if (!state.active && state.bytes > this.budget.maxSessionBytes)
|
|
785
|
+
this.evict(id);
|
|
786
|
+
for (const [id, state] of this.retention) {
|
|
787
|
+
if (this.retention.size <= this.budget.maxSessions)
|
|
788
|
+
break;
|
|
789
|
+
if (!state.active)
|
|
790
|
+
this.evict(id);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
evict(sessionID) {
|
|
794
|
+
this.tails.delete(sessionID);
|
|
795
|
+
this.nativeRows.delete(sessionID);
|
|
796
|
+
this.admissions.delete(sessionID);
|
|
797
|
+
this.retention.delete(sessionID);
|
|
798
|
+
for (const listener of this.evictionListeners)
|
|
799
|
+
listener(sessionID);
|
|
800
|
+
}
|
|
801
|
+
get db() {
|
|
802
|
+
return this.resolveDB();
|
|
803
|
+
}
|
|
804
|
+
tail(sessionID) {
|
|
805
|
+
const record = this.tails.get(sessionID);
|
|
806
|
+
if (record)
|
|
807
|
+
this.touch(sessionID);
|
|
808
|
+
return record && { ...record };
|
|
809
|
+
}
|
|
810
|
+
async saveTail(sessionID, data, rows) {
|
|
811
|
+
this.tails.set(sessionID, {
|
|
812
|
+
revision: (this.tails.get(sessionID)?.revision ?? -1) + 1,
|
|
813
|
+
data,
|
|
814
|
+
digest: foldDigest(data)
|
|
815
|
+
});
|
|
816
|
+
const kept = this.nativeRows.get(sessionID) ?? new Map;
|
|
817
|
+
for (const row of rows)
|
|
818
|
+
kept.set(row.id, Object.freeze({ ...row, revision: (kept.get(row.id)?.revision ?? -1) + 1 }));
|
|
819
|
+
this.nativeRows.set(sessionID, kept);
|
|
820
|
+
this.recount(sessionID);
|
|
821
|
+
}
|
|
822
|
+
replaceRows(sessionID, through, rows, after = -1) {
|
|
823
|
+
const kept = this.nativeRows.get(sessionID) ?? new Map;
|
|
824
|
+
for (const [id, row] of kept)
|
|
825
|
+
if (row.seq > after && row.seq <= through)
|
|
826
|
+
kept.delete(id);
|
|
827
|
+
for (const row of rows)
|
|
828
|
+
kept.set(row.id, Object.freeze({ ...row, revision: 0 }));
|
|
829
|
+
this.nativeRows.set(sessionID, kept);
|
|
830
|
+
this.recount(sessionID);
|
|
831
|
+
}
|
|
832
|
+
truncate(sessionID, through) {
|
|
833
|
+
const kept = this.nativeRows.get(sessionID);
|
|
834
|
+
if (kept) {
|
|
835
|
+
for (const [id, row] of kept)
|
|
836
|
+
if (row.seq > through)
|
|
837
|
+
kept.delete(id);
|
|
838
|
+
}
|
|
839
|
+
if (kept)
|
|
840
|
+
this.recount(sessionID);
|
|
841
|
+
}
|
|
842
|
+
sequenceForID(sessionID, id) {
|
|
843
|
+
return this.nativeRows.get(sessionID)?.get(id)?.seq;
|
|
844
|
+
}
|
|
845
|
+
containsRows(sessionID, ids) {
|
|
846
|
+
const kept = this.nativeRows.get(sessionID);
|
|
847
|
+
return ids.every((id) => kept?.has(id));
|
|
848
|
+
}
|
|
849
|
+
rows(sessionID, after, through) {
|
|
850
|
+
if (this.retention.has(sessionID))
|
|
851
|
+
this.touch(sessionID);
|
|
852
|
+
return [...this.nativeRows.get(sessionID)?.values() ?? []].filter((row) => row.seq > after && row.seq <= through).sort((a, b) => a.seq - b.seq).map((row) => ({ ...row }));
|
|
853
|
+
}
|
|
854
|
+
admission(sessionID, id) {
|
|
855
|
+
const record = this.admissions.get(sessionID)?.get(id);
|
|
856
|
+
if (record)
|
|
857
|
+
this.touch(sessionID);
|
|
858
|
+
return record && { ...record };
|
|
859
|
+
}
|
|
860
|
+
put(sessionID, id, record) {
|
|
861
|
+
const kept = this.admissions.get(sessionID) ?? new Map;
|
|
862
|
+
kept.set(id, Object.freeze({ ...record }));
|
|
863
|
+
this.admissions.set(sessionID, kept);
|
|
864
|
+
this.recount(sessionID);
|
|
865
|
+
}
|
|
866
|
+
async supply(sessionID, id, payload) {
|
|
867
|
+
const old = this.admission(sessionID, id);
|
|
868
|
+
this.put(sessionID, id, {
|
|
869
|
+
admission_id: id,
|
|
870
|
+
revision: (old?.revision ?? -1) + 1,
|
|
871
|
+
status: old?.status ?? "supplied",
|
|
872
|
+
owner_token: old?.owner_token ?? null,
|
|
873
|
+
data: old?.data ?? "{}",
|
|
874
|
+
payload,
|
|
875
|
+
digest: foldDigest(payload),
|
|
876
|
+
logged_at: old?.logged_at ?? null
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
latestAttempt(sessionID) {
|
|
880
|
+
const record = [...this.admissions.get(sessionID)?.values() ?? []].reverse().find((row) => JSON.parse(row.data).after !== undefined);
|
|
881
|
+
return record && { ...record };
|
|
882
|
+
}
|
|
883
|
+
async claim(sessionID, id, data, owner, _now) {
|
|
884
|
+
const old = this.admission(sessionID, id);
|
|
885
|
+
if (old?.owner_token || old?.logged_at !== null && old?.logged_at !== undefined)
|
|
886
|
+
return false;
|
|
887
|
+
this.put(sessionID, id, {
|
|
888
|
+
admission_id: id,
|
|
889
|
+
revision: 0,
|
|
890
|
+
status: "pending",
|
|
891
|
+
owner_token: owner,
|
|
892
|
+
data,
|
|
893
|
+
payload: old?.payload ?? null,
|
|
894
|
+
digest: old?.digest ?? null,
|
|
895
|
+
logged_at: null
|
|
896
|
+
});
|
|
897
|
+
return true;
|
|
898
|
+
}
|
|
899
|
+
async finish(sessionID, id, owner, status, now) {
|
|
900
|
+
const old = this.admission(sessionID, id);
|
|
901
|
+
if (!old || old.owner_token !== owner || old.status !== "pending")
|
|
902
|
+
return false;
|
|
903
|
+
this.put(sessionID, id, { ...old, status, owner_token: null, logged_at: now });
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
async forget(sessionID) {
|
|
907
|
+
this.evict(sessionID);
|
|
908
|
+
}
|
|
680
909
|
}
|
|
681
|
-
function
|
|
682
|
-
return
|
|
910
|
+
function nativeFoldCache(db, budget) {
|
|
911
|
+
return new NativeFoldCache(typeof db === "function" ? db : () => db, budget);
|
|
683
912
|
}
|
|
684
|
-
|
|
685
|
-
|
|
913
|
+
|
|
914
|
+
// src/v2/tool-result.ts
|
|
915
|
+
function isToolError(part) {
|
|
916
|
+
const result = part.result;
|
|
917
|
+
return part.resultType === "error" || result?.type === "error" || result !== null && typeof result === "object" && "error" in result;
|
|
686
918
|
}
|
|
687
|
-
function
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (message.content.length !== 1)
|
|
691
|
-
return false;
|
|
692
|
-
const part = message.content[0];
|
|
693
|
-
return part?.type === "text" && isStorageNoticeText(part.text);
|
|
919
|
+
function toolErrorText(result) {
|
|
920
|
+
const value = result.value === undefined ? result : result.value;
|
|
921
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
694
922
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
923
|
+
function toolStateContent(state) {
|
|
924
|
+
if (typeof state.output === "string")
|
|
925
|
+
return state.output;
|
|
926
|
+
if (state.status === "error")
|
|
927
|
+
return toolErrorText({ error: state.error, content: state.content ?? [] });
|
|
928
|
+
if (typeof state.content === "string")
|
|
929
|
+
return state.content;
|
|
930
|
+
if (!Array.isArray(state.content))
|
|
931
|
+
return "";
|
|
932
|
+
return state.content.map((part) => {
|
|
933
|
+
if (typeof part === "string")
|
|
934
|
+
return part;
|
|
935
|
+
if (!part || typeof part !== "object")
|
|
936
|
+
return "";
|
|
937
|
+
const value = part;
|
|
938
|
+
if (typeof value.text === "string")
|
|
939
|
+
return value.text;
|
|
940
|
+
if (typeof value.value === "string")
|
|
941
|
+
return value.value;
|
|
942
|
+
return "";
|
|
943
|
+
}).filter(Boolean).join(`
|
|
944
|
+
`);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// src/v2/hooks/payload.ts
|
|
948
|
+
var HEAD_IDS = ["__magic_context_v2_m0__", "__magic_context_v2_m1__"];
|
|
949
|
+
var HOST_CONTENT_TYPES = new Set([
|
|
950
|
+
"text",
|
|
951
|
+
"media",
|
|
952
|
+
"tool-call",
|
|
953
|
+
"tool-result",
|
|
954
|
+
"reasoning",
|
|
955
|
+
"compaction",
|
|
956
|
+
"effort"
|
|
957
|
+
]);
|
|
958
|
+
function rejectContentPart(message, part, reason) {
|
|
959
|
+
const type = typeof part.type === "string" ? part.type : "<missing>";
|
|
960
|
+
const text = `v2 context omitted a content part the host would reject: type=${type} message=${String(message.info.id ?? "<head>")} reason=${reason}`;
|
|
961
|
+
if (typeof message.info.sessionID === "string")
|
|
962
|
+
sessionLog2(message.info.sessionID, text);
|
|
963
|
+
else
|
|
964
|
+
log2(text);
|
|
965
|
+
}
|
|
966
|
+
var emptyTextOmissionLogged = new Set;
|
|
967
|
+
function placeCacheHint(content, hint) {
|
|
968
|
+
if (content.length === 0 || content.some((part) => part.cache !== undefined))
|
|
969
|
+
return;
|
|
970
|
+
let lastText = content.length - 1;
|
|
971
|
+
while (lastText >= 0 && content[lastText]?.type !== "text")
|
|
972
|
+
lastText -= 1;
|
|
973
|
+
const index = lastText >= 0 ? lastText : content.length - 1;
|
|
974
|
+
content[index] = { ...content[index], cache: hint };
|
|
975
|
+
}
|
|
976
|
+
function removeEmptyTextParts(content, rendered) {
|
|
977
|
+
let hint;
|
|
978
|
+
let removed = 0;
|
|
979
|
+
for (let index = content.length - 1;index >= 0; index -= 1) {
|
|
980
|
+
const part = content[index];
|
|
981
|
+
if (part?.type !== "text" || part.text !== "")
|
|
699
982
|
continue;
|
|
700
|
-
if (
|
|
701
|
-
|
|
983
|
+
if (hint === undefined)
|
|
984
|
+
hint = part.cache;
|
|
985
|
+
content.splice(index, 1);
|
|
986
|
+
removed += 1;
|
|
702
987
|
}
|
|
703
|
-
if (
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
988
|
+
if (hint !== undefined) {
|
|
989
|
+
const target = content.length > 0 ? content : rendered.at(-1)?.content;
|
|
990
|
+
if (target)
|
|
991
|
+
placeCacheHint(target, hint);
|
|
992
|
+
}
|
|
993
|
+
return removed;
|
|
708
994
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
995
|
+
function isHostInstance(value) {
|
|
996
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
997
|
+
return false;
|
|
998
|
+
const prototype = Object.getPrototypeOf(value);
|
|
999
|
+
return prototype !== Object.prototype && prototype !== null;
|
|
713
1000
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
file.description === undefined ? undefined : `Description: ${file.description}`,
|
|
743
|
-
directory && !file.data.length ? undefined : "",
|
|
744
|
-
directory && !file.data.length ? undefined : Buffer.from(file.data, "base64").toString("utf8")
|
|
745
|
-
].filter((line) => line !== undefined).join(`
|
|
746
|
-
`)}`,
|
|
747
|
-
metadata: {
|
|
748
|
-
attachment: {
|
|
749
|
-
source: file.source,
|
|
750
|
-
name: file.name,
|
|
751
|
-
description: file.description
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
];
|
|
1001
|
+
function containsHostInstance(value) {
|
|
1002
|
+
if (isHostInstance(value))
|
|
1003
|
+
return true;
|
|
1004
|
+
if (Array.isArray(value))
|
|
1005
|
+
return value.some(containsHostInstance);
|
|
1006
|
+
if (value && typeof value === "object")
|
|
1007
|
+
return Object.values(value).some(containsHostInstance);
|
|
1008
|
+
return false;
|
|
1009
|
+
}
|
|
1010
|
+
function clonePreservingInstances(value) {
|
|
1011
|
+
if (isHostInstance(value))
|
|
1012
|
+
return value;
|
|
1013
|
+
if (Array.isArray(value))
|
|
1014
|
+
return value.map(clonePreservingInstances);
|
|
1015
|
+
if (value && typeof value === "object") {
|
|
1016
|
+
const copy = {};
|
|
1017
|
+
for (const [key, entry] of Object.entries(value))
|
|
1018
|
+
copy[key] = clonePreservingInstances(entry);
|
|
1019
|
+
return copy;
|
|
1020
|
+
}
|
|
1021
|
+
return value;
|
|
1022
|
+
}
|
|
1023
|
+
function contentKey(value) {
|
|
1024
|
+
const plain = (entry) => {
|
|
1025
|
+
if (ArrayBuffer.isView(entry)) {
|
|
1026
|
+
return {
|
|
1027
|
+
bytes: Buffer.from(entry.buffer, entry.byteOffset, entry.byteLength).toString("base64")
|
|
1028
|
+
};
|
|
756
1029
|
}
|
|
757
|
-
if (
|
|
758
|
-
return
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
if (!media)
|
|
762
|
-
return [
|
|
763
|
-
...located,
|
|
764
|
-
{
|
|
765
|
-
type: "media",
|
|
766
|
-
mediaType: file.mime,
|
|
767
|
-
data: file.data,
|
|
768
|
-
filename: file.name,
|
|
769
|
-
metadata
|
|
770
|
-
}
|
|
771
|
-
];
|
|
772
|
-
const asset = media.asset(file.data, file.mime);
|
|
773
|
-
if (typeof asset === "string") {
|
|
774
|
-
media.unavailable({ rowID, name: file.name, mediaType: file.mime, reason: asset });
|
|
775
|
-
return [
|
|
776
|
-
...located,
|
|
777
|
-
{ type: "text", text: unavailableAttachmentNote(file.name, file.mime) }
|
|
778
|
-
];
|
|
1030
|
+
if (Array.isArray(entry))
|
|
1031
|
+
return entry.map(plain);
|
|
1032
|
+
if (entry && typeof entry === "object") {
|
|
1033
|
+
return Object.fromEntries(Object.entries(entry).map(([key, nested]) => [key, plain(nested)]));
|
|
779
1034
|
}
|
|
780
|
-
return
|
|
781
|
-
}
|
|
1035
|
+
return entry;
|
|
1036
|
+
};
|
|
1037
|
+
return JSON.stringify(plain(value));
|
|
782
1038
|
}
|
|
783
|
-
function
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
1039
|
+
function fileContentValue(result) {
|
|
1040
|
+
const value = result?.value;
|
|
1041
|
+
if (result?.type !== "content" || !Array.isArray(value))
|
|
1042
|
+
return;
|
|
1043
|
+
return value.some((item) => item?.type === "file") ? value : undefined;
|
|
1044
|
+
}
|
|
1045
|
+
function contentValueText(value) {
|
|
1046
|
+
return value.filter((item) => item?.type === "text" && typeof item.text === "string").map((item) => item.text).join(`
|
|
1047
|
+
`);
|
|
1048
|
+
}
|
|
1049
|
+
function contentValueAttachments(value) {
|
|
1050
|
+
return value.filter((item) => item?.type === "file").map((item) => ({
|
|
1051
|
+
type: "file",
|
|
1052
|
+
mime: item.mime,
|
|
1053
|
+
url: item.uri,
|
|
1054
|
+
...typeof item.name === "string" ? { filename: item.name } : {}
|
|
1055
|
+
}));
|
|
1056
|
+
}
|
|
1057
|
+
function rebuildFileContent(value, output) {
|
|
1058
|
+
if (isDroppedToolOutput(output))
|
|
1059
|
+
return { type: "text", value: output };
|
|
1060
|
+
const rebuilt = [];
|
|
1061
|
+
let placed = false;
|
|
1062
|
+
for (const item of value) {
|
|
1063
|
+
if (item?.type !== "text") {
|
|
1064
|
+
rebuilt.push(item);
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
if (placed)
|
|
1068
|
+
continue;
|
|
1069
|
+
placed = true;
|
|
1070
|
+
rebuilt.push({ ...item, text: output });
|
|
1071
|
+
}
|
|
1072
|
+
if (!placed && output)
|
|
1073
|
+
rebuilt.unshift({ type: "text", text: output });
|
|
1074
|
+
return { type: "content", value: rebuilt };
|
|
1075
|
+
}
|
|
1076
|
+
function adaptPayload(draft, admittedIDs = new Set) {
|
|
1077
|
+
const existingHeads = new Map(draft.messages.filter((m) => HEAD_IDS.some((id) => m.id === id)).map((m) => [m.id, m]));
|
|
1078
|
+
const source = draft.messages.filter((m) => !existingHeads.has(m.id));
|
|
1079
|
+
const results = new Map;
|
|
1080
|
+
const hostParts = new Map;
|
|
1081
|
+
const hostPartTypes = new Set;
|
|
1082
|
+
for (const message of source) {
|
|
1083
|
+
for (const part of message.content) {
|
|
1084
|
+
if (part.type !== "tool-call" && part.type !== "tool-result" && part.type !== "tool" && containsHostInstance(part)) {
|
|
1085
|
+
const key = contentKey(part);
|
|
1086
|
+
if (!hostParts.has(key))
|
|
1087
|
+
hostParts.set(key, part);
|
|
1088
|
+
hostPartTypes.add(part.type);
|
|
1089
|
+
}
|
|
1090
|
+
if (part.type === "tool-result" && typeof part.id === "string") {
|
|
1091
|
+
const queue = results.get(part.id) ?? [];
|
|
1092
|
+
queue.push({ part, message });
|
|
1093
|
+
results.set(part.id, queue);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
const paired = new Set;
|
|
1098
|
+
const bridges = new Map;
|
|
1099
|
+
const originals = new Map;
|
|
1100
|
+
const bridgesByMessageID = new Map;
|
|
1101
|
+
const bridgesByCarrier = new Map;
|
|
1102
|
+
const originalsByID = new Map;
|
|
1103
|
+
const bridgesFor = (message) => typeof message.info.id === "string" ? bridgesByMessageID.get(message.info.id) : bridgesByCarrier.get(message);
|
|
1104
|
+
const nativeTool = (call, result) => {
|
|
1105
|
+
const hostResult = result?.part.result;
|
|
1106
|
+
const failed = result !== undefined && isToolError(result.part);
|
|
1107
|
+
const value = hostResult?.value;
|
|
1108
|
+
const files = fileContentValue(hostResult);
|
|
1109
|
+
const output = failed && hostResult ? toolErrorText(hostResult) : files ? contentValueText(files) : typeof value === "string" ? value : value === undefined ? "" : JSON.stringify(value);
|
|
1110
|
+
const part = {
|
|
1111
|
+
type: "tool",
|
|
1112
|
+
callID: call?.id ?? result?.part.id,
|
|
1113
|
+
tool: call?.name ?? result?.part.name,
|
|
1114
|
+
state: {
|
|
1115
|
+
input: structuredClone(call?.input ?? {}),
|
|
1116
|
+
status: failed ? "error" : result ? "completed" : "running",
|
|
1117
|
+
...result ? { output } : {},
|
|
1118
|
+
...result && (call?.name ?? result.part.name) === "question" ? { metadata: { userAnswer: true } } : {},
|
|
1119
|
+
...files ? { attachments: contentValueAttachments(files) } : {}
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
bridges.set(part, {
|
|
1123
|
+
call,
|
|
1124
|
+
result: result?.part,
|
|
1125
|
+
resultMessage: result?.message,
|
|
1126
|
+
output,
|
|
1127
|
+
files
|
|
1128
|
+
});
|
|
1129
|
+
if (result)
|
|
1130
|
+
paired.add(result.part);
|
|
1131
|
+
return part;
|
|
1132
|
+
};
|
|
1133
|
+
const convertedTool = (source) => {
|
|
1134
|
+
const sourceState = source.state && typeof source.state === "object" ? source.state : {};
|
|
1135
|
+
const output = toolStateContent(sourceState);
|
|
1136
|
+
const { content: _content, ...projectedState } = structuredClone(sourceState);
|
|
1137
|
+
const part = {
|
|
1138
|
+
type: "tool",
|
|
1139
|
+
callID: source.callID ?? source.id,
|
|
1140
|
+
tool: source.tool ?? source.name,
|
|
1141
|
+
state: { ...projectedState, output }
|
|
1142
|
+
};
|
|
1143
|
+
bridges.set(part, { native: source, output });
|
|
1144
|
+
return part;
|
|
1145
|
+
};
|
|
1146
|
+
const callParts = new Map;
|
|
1147
|
+
for (const message of source) {
|
|
1148
|
+
for (const part of message.content) {
|
|
1149
|
+
if (part.type === "tool-call") {
|
|
1150
|
+
callParts.set(part, nativeTool(part, results.get(String(part.id))?.shift()));
|
|
1151
|
+
} else if (part.type === "tool") {
|
|
1152
|
+
callParts.set(part, convertedTool(part));
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
const messages = [];
|
|
1157
|
+
for (const message of source) {
|
|
1158
|
+
const parts = [];
|
|
1159
|
+
for (const content of message.content) {
|
|
1160
|
+
if (paired.has(content))
|
|
1161
|
+
continue;
|
|
1162
|
+
const part = callParts.get(content) ?? (content.type === "tool-result" ? nativeTool(undefined, { part: content, message }) : clonePreservingInstances(content));
|
|
1163
|
+
if (message.id && admittedIDs.has(message.id))
|
|
1164
|
+
part.synthetic = true;
|
|
1165
|
+
parts.push(part);
|
|
1166
|
+
}
|
|
1167
|
+
if (!parts.length && message.content.length)
|
|
1168
|
+
continue;
|
|
1169
|
+
const byCallID = new Map;
|
|
1170
|
+
for (const part of parts) {
|
|
1171
|
+
const bridge = bridges.get(part);
|
|
1172
|
+
if (bridge && typeof part.callID === "string")
|
|
1173
|
+
byCallID.set(part.callID, bridge);
|
|
1174
|
+
}
|
|
1175
|
+
if (typeof message.id === "string") {
|
|
1176
|
+
bridgesByMessageID.set(message.id, byCallID);
|
|
1177
|
+
originalsByID.set(message.id, message);
|
|
1178
|
+
}
|
|
1179
|
+
const mapped = {
|
|
1180
|
+
info: {
|
|
1181
|
+
id: message.id,
|
|
1182
|
+
sessionID: draft.sessionID,
|
|
1183
|
+
role: message.role,
|
|
1184
|
+
...{
|
|
1185
|
+
providerID: draft.model.providerID,
|
|
1186
|
+
modelID: draft.model.id,
|
|
1187
|
+
agent: draft.agent
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
parts
|
|
1191
|
+
};
|
|
1192
|
+
originals.set(mapped, message);
|
|
1193
|
+
if (typeof message.id !== "string")
|
|
1194
|
+
bridgesByCarrier.set(mapped, byCallID);
|
|
1195
|
+
messages.push(mapped);
|
|
1196
|
+
}
|
|
1197
|
+
return {
|
|
1198
|
+
messages,
|
|
1199
|
+
commit() {
|
|
1200
|
+
let head = 0;
|
|
1201
|
+
const rendered = [];
|
|
1202
|
+
const emptyTextGuard = modelAcceptsEmptyContent(draft.model.providerID);
|
|
1203
|
+
let omitted = 0;
|
|
1204
|
+
let emptyTextParts = 0;
|
|
1205
|
+
for (const message of messages) {
|
|
1206
|
+
const original = originals.get(message) ?? (typeof message.info.id === "string" ? originalsByID.get(message.info.id) : undefined);
|
|
1207
|
+
const id = message.info.syntheticHead ? HEAD_IDS[head++] : original?.id;
|
|
1208
|
+
const content = [];
|
|
1209
|
+
const following = [];
|
|
1210
|
+
for (const candidate of message.parts) {
|
|
1211
|
+
const part = candidate;
|
|
1212
|
+
const bridge = bridges.get(part) ?? (part.type === "tool" && typeof part.callID === "string" ? bridgesFor(message)?.get(part.callID) : undefined);
|
|
1213
|
+
if (!bridge) {
|
|
1214
|
+
if (part.type === "tool") {
|
|
1215
|
+
const state = part.state;
|
|
1216
|
+
if (typeof part.callID !== "string" || typeof part.tool !== "string" || !state || typeof state !== "object") {
|
|
1217
|
+
rejectContentPart(message, part, "tool has no call ID, name, or state");
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
content.push({
|
|
1221
|
+
type: "tool-call",
|
|
1222
|
+
id: part.callID,
|
|
1223
|
+
name: part.tool,
|
|
1224
|
+
input: state.input
|
|
1225
|
+
});
|
|
1226
|
+
if (state.status === "completed" || state.status === "error") {
|
|
1227
|
+
following.push({
|
|
1228
|
+
role: "tool",
|
|
1229
|
+
content: [
|
|
1230
|
+
{
|
|
1231
|
+
type: "tool-result",
|
|
1232
|
+
id: part.callID,
|
|
1233
|
+
name: part.tool,
|
|
1234
|
+
result: {
|
|
1235
|
+
type: state.status === "error" ? "error" : "text",
|
|
1236
|
+
value: toolStateContent(state)
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
]
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
if (part.type === "file") {
|
|
1245
|
+
const mime = part.mime;
|
|
1246
|
+
const url = part.url;
|
|
1247
|
+
const prefix = `data:${mime};base64,`;
|
|
1248
|
+
if (typeof mime !== "string" || !/^image\/(png|jpeg|gif|webp)$/.test(mime) || typeof url !== "string" || !url.startsWith(prefix)) {
|
|
1249
|
+
rejectContentPart(message, part, "file is not an inline supported image");
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
const asset = hostMediaAsset(url.slice(prefix.length), mime);
|
|
1253
|
+
if (typeof asset === "string") {
|
|
1254
|
+
rejectContentPart(message, part, `host media unavailable: ${asset}`);
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
content.push({ type: "media", media: asset });
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
if (!HOST_CONTENT_TYPES.has(String(part.type))) {
|
|
1261
|
+
rejectContentPart(message, part, "unknown host content type");
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
const { synthetic: _synthetic, ...clean } = part;
|
|
1265
|
+
const original = hostPartTypes.has(clean.type) && !containsHostInstance(clean) ? hostParts.get(contentKey(clean)) : undefined;
|
|
1266
|
+
content.push(original ?? clean);
|
|
1267
|
+
continue;
|
|
1268
|
+
}
|
|
1269
|
+
const state = part.state;
|
|
1270
|
+
if (bridge.native) {
|
|
1271
|
+
const native = bridge.native;
|
|
1272
|
+
const callID = String(native.id ?? native.callID ?? part.callID);
|
|
1273
|
+
const name = String(native.name ?? native.tool ?? part.tool);
|
|
1274
|
+
content.push({ type: "tool-call", id: callID, name, input: state.input });
|
|
1275
|
+
if (state.status === "completed" || state.status === "error") {
|
|
1276
|
+
following.push({
|
|
1277
|
+
role: "tool",
|
|
1278
|
+
content: [
|
|
1279
|
+
{
|
|
1280
|
+
type: "tool-result",
|
|
1281
|
+
id: callID,
|
|
1282
|
+
name,
|
|
1283
|
+
result: {
|
|
1284
|
+
type: state.status === "error" ? "error" : "text",
|
|
1285
|
+
value: state.output
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
]
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
if (bridge.call)
|
|
1294
|
+
content.push({ ...bridge.call, input: state.input });
|
|
1295
|
+
if (bridge.result && bridge.resultMessage) {
|
|
1296
|
+
const result = {
|
|
1297
|
+
...bridge.result,
|
|
1298
|
+
result: state.output === bridge.output ? bridge.result.result : bridge.files && typeof state.output === "string" ? rebuildFileContent(bridge.files, state.output) : {
|
|
1299
|
+
type: state.status === "error" ? "error" : "text",
|
|
1300
|
+
value: state.output
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
if (bridge.resultMessage === original)
|
|
1304
|
+
content.push(result);
|
|
1305
|
+
else
|
|
1306
|
+
following.push({ ...bridge.resultMessage, content: [result] });
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
const guarded = emptyTextGuard ? removeEmptyTextParts(content, rendered) : 0;
|
|
1310
|
+
if (content.length || !original?.content.length && guarded === 0) {
|
|
1311
|
+
const value = {
|
|
1312
|
+
...original,
|
|
1313
|
+
id,
|
|
1314
|
+
role: message.info.role ?? "user",
|
|
1315
|
+
content
|
|
1316
|
+
};
|
|
1317
|
+
const existing = id ? existingHeads.get(id) : undefined;
|
|
1318
|
+
rendered.push(existing ? Object.assign(existing, { content }) : value);
|
|
1319
|
+
} else if (emptyTextGuard) {
|
|
1320
|
+
omitted += 1;
|
|
1321
|
+
}
|
|
1322
|
+
emptyTextParts += guarded;
|
|
1323
|
+
rendered.push(...following);
|
|
1324
|
+
}
|
|
1325
|
+
if (omitted + emptyTextParts > 0 && !emptyTextOmissionLogged.has(draft.sessionID)) {
|
|
1326
|
+
emptyTextOmissionLogged.add(draft.sessionID);
|
|
1327
|
+
sessionLog2(draft.sessionID, `v2 payload: left out ${omitted} emptied messages and ${emptyTextParts} empty text parts, because Anthropic rejects an empty text block (logged once per session)`);
|
|
1328
|
+
}
|
|
1329
|
+
draft.messages.splice(0, draft.messages.length, ...rendered);
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// src/v2/fold/native-codec.ts
|
|
1335
|
+
class NativeReplayUnavailable extends Error {
|
|
1336
|
+
}
|
|
1337
|
+
function encodeNative(messages) {
|
|
1338
|
+
for (const message of messages)
|
|
1339
|
+
for (const part of message.content) {
|
|
1340
|
+
if (part.type !== "media" || !part.media)
|
|
1341
|
+
continue;
|
|
1342
|
+
const asset = part.media;
|
|
1343
|
+
if (typeof asset.toJSON !== "function" || asset.headers !== undefined)
|
|
1344
|
+
throw new NativeReplayUnavailable("Native media has no lossless replay codec");
|
|
1345
|
+
const encoded = asset.toJSON();
|
|
1346
|
+
const revived = hostMediaFromEncoded(encoded);
|
|
1347
|
+
if (typeof revived === "string" || JSON.stringify(revived) !== JSON.stringify(asset))
|
|
1348
|
+
throw new NativeReplayUnavailable("Native media cannot be decoded losslessly before admission");
|
|
1349
|
+
}
|
|
1350
|
+
const encoded = JSON.parse(JSON.stringify(messages));
|
|
1351
|
+
const freeze = (value) => {
|
|
1352
|
+
if (!value || typeof value !== "object")
|
|
1353
|
+
return;
|
|
1354
|
+
for (const item of Object.values(value))
|
|
1355
|
+
freeze(item);
|
|
1356
|
+
Object.freeze(value);
|
|
1357
|
+
};
|
|
1358
|
+
freeze(encoded);
|
|
1359
|
+
return encoded;
|
|
1360
|
+
}
|
|
1361
|
+
function reviveNative(messages) {
|
|
1362
|
+
const result = structuredClone([...messages]);
|
|
1363
|
+
for (const message of result)
|
|
1364
|
+
for (const part of message.content) {
|
|
1365
|
+
if (part.type !== "media" || !part.media)
|
|
1366
|
+
continue;
|
|
1367
|
+
const asset = hostMediaFromEncoded(part.media);
|
|
1368
|
+
if (typeof asset === "string")
|
|
1369
|
+
throw new NativeReplayUnavailable(asset);
|
|
1370
|
+
part.media = asset;
|
|
1371
|
+
}
|
|
1372
|
+
return result;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// src/v2/fold/restore.ts
|
|
1376
|
+
import { fileURLToPath } from "node:url";
|
|
1377
|
+
|
|
1378
|
+
// src/v2/hooks/storage-notice.ts
|
|
1379
|
+
var STORAGE_NOTICE_PREFIX = "[Magic Context storage notice] ";
|
|
1380
|
+
function formatStorageRefusalNotice(reason) {
|
|
1381
|
+
return `${STORAGE_NOTICE_PREFIX}This turn was not sent to the model. ${formatFailClosedBlockingSummary(reason)}`;
|
|
1382
|
+
}
|
|
1383
|
+
function formatStorageRecoveryNotice(hiddenWorkRecovered) {
|
|
1384
|
+
return hiddenWorkRecovered ? `${STORAGE_NOTICE_PREFIX}Magic Context storage is available again and turns are sent to the model. The historian and dreamer are running again. Restart OpenCode to enable Magic Context's tools.` : `${STORAGE_NOTICE_PREFIX}Magic Context storage is available again and turns are sent to the model. Restart OpenCode to enable Magic Context's tools, historian and dreamer.`;
|
|
1385
|
+
}
|
|
1386
|
+
function isStorageNoticeText(text) {
|
|
1387
|
+
return typeof text === "string" && text.startsWith(STORAGE_NOTICE_PREFIX);
|
|
1388
|
+
}
|
|
1389
|
+
function hasStorageNoticeShape(message) {
|
|
1390
|
+
if (message.role !== "user" || typeof message.id !== "string")
|
|
1391
|
+
return false;
|
|
1392
|
+
if (message.content.length !== 1)
|
|
1393
|
+
return false;
|
|
1394
|
+
const part = message.content[0];
|
|
1395
|
+
return part?.type === "text" && isStorageNoticeText(part.text);
|
|
1396
|
+
}
|
|
1397
|
+
async function dropStorageNotices(messages, isAdmittedSynthetic) {
|
|
1398
|
+
const drop = new Set;
|
|
1399
|
+
for (const message of messages) {
|
|
1400
|
+
if (!hasStorageNoticeShape(message))
|
|
1401
|
+
continue;
|
|
1402
|
+
if (await isAdmittedSynthetic(message.id))
|
|
1403
|
+
drop.add(message);
|
|
1404
|
+
}
|
|
1405
|
+
if (drop.size === 0)
|
|
1406
|
+
return 0;
|
|
1407
|
+
const kept = messages.filter((message) => !drop.has(message));
|
|
1408
|
+
messages.splice(0, messages.length, ...kept);
|
|
1409
|
+
return drop.size;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// src/v2/fold/restore.ts
|
|
1413
|
+
function unavailableAttachmentNote(name, mediaType) {
|
|
1414
|
+
return `[Attachment ${name ? `"${name}" ` : ""}(${mediaType}) is not available in this restored history]`;
|
|
1415
|
+
}
|
|
1416
|
+
var imageMimes = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
1417
|
+
function attachmentParts(files, rowID, media) {
|
|
1418
|
+
const seen = new Map;
|
|
1419
|
+
return files.flatMap((file) => {
|
|
1420
|
+
if (imageMimes.has(file.mime) && file.source.type === "inline" && file.mention?.text) {
|
|
1421
|
+
const key = JSON.stringify([
|
|
1422
|
+
file.mime,
|
|
1423
|
+
file.name ?? null,
|
|
1424
|
+
file.description ?? null,
|
|
1425
|
+
file.mention.text
|
|
1426
|
+
]);
|
|
1427
|
+
const payloads = seen.get(key) ?? new Set;
|
|
1428
|
+
if (payloads.has(file.data))
|
|
1429
|
+
return [];
|
|
1430
|
+
payloads.add(file.data);
|
|
1431
|
+
seen.set(key, payloads);
|
|
1432
|
+
}
|
|
1433
|
+
const uri = file.source.type === "uri" ? file.source.uri : undefined;
|
|
1434
|
+
const location = uri?.startsWith("file:") ? fileURLToPath(uri) : undefined;
|
|
1435
|
+
if (file.mime === "text/plain" || file.mime === "application/x-directory") {
|
|
1436
|
+
const directory = file.mime === "application/x-directory";
|
|
1437
|
+
return [
|
|
1438
|
+
{
|
|
1439
|
+
type: "text",
|
|
1440
|
+
text: `
|
|
1441
|
+
|
|
1442
|
+
${[
|
|
1443
|
+
directory ? `Attached directory: ${location ?? file.name ?? uri ?? "directory"}` : `Attached file: ${file.name ?? uri ?? "inline attachment"}`,
|
|
1444
|
+
file.description === undefined ? undefined : `Description: ${file.description}`,
|
|
1445
|
+
directory && !file.data.length ? undefined : "",
|
|
1446
|
+
directory && !file.data.length ? undefined : Buffer.from(file.data, "base64").toString("utf8")
|
|
1447
|
+
].filter((line) => line !== undefined).join(`
|
|
1448
|
+
`)}`,
|
|
1449
|
+
metadata: {
|
|
1450
|
+
attachment: {
|
|
1451
|
+
source: file.source,
|
|
1452
|
+
name: file.name,
|
|
1453
|
+
description: file.description
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
];
|
|
1458
|
+
}
|
|
1459
|
+
if (!imageMimes.has(file.mime) && file.mime !== "application/pdf")
|
|
1460
|
+
return [];
|
|
1461
|
+
const metadata = file.description === undefined ? undefined : { description: file.description };
|
|
1462
|
+
const located = location ? [{ type: "text", text: `Attached file: ${location}` }] : [];
|
|
1463
|
+
if (!media)
|
|
1464
|
+
return [
|
|
1465
|
+
...located,
|
|
1466
|
+
{
|
|
1467
|
+
type: "media",
|
|
1468
|
+
mediaType: file.mime,
|
|
1469
|
+
data: file.data,
|
|
1470
|
+
filename: file.name,
|
|
1471
|
+
metadata
|
|
1472
|
+
}
|
|
1473
|
+
];
|
|
1474
|
+
const asset = media.asset(file.data, file.mime);
|
|
1475
|
+
if (typeof asset === "string") {
|
|
1476
|
+
media.unavailable({ rowID, name: file.name, mediaType: file.mime, reason: asset });
|
|
1477
|
+
return [
|
|
1478
|
+
...located,
|
|
1479
|
+
{ type: "text", text: unavailableAttachmentNote(file.name, file.mime) }
|
|
1480
|
+
];
|
|
1481
|
+
}
|
|
1482
|
+
return [...located, { type: "media", media: asset, filename: file.name, metadata }];
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
function restoreRow(row, model, media) {
|
|
1486
|
+
const data = row.data;
|
|
1487
|
+
const make = (role, content, metadata = data.metadata) => ({
|
|
1488
|
+
id: row.id,
|
|
1489
|
+
role,
|
|
1490
|
+
content,
|
|
789
1491
|
...metadata === undefined ? {} : { metadata }
|
|
790
1492
|
});
|
|
791
1493
|
if (row.type === "user") {
|
|
@@ -866,36 +1568,425 @@ ${data.output?.output ?? ""}`
|
|
|
866
1568
|
let input = state.input;
|
|
867
1569
|
if (state.status === "streaming" && typeof input === "string") {
|
|
868
1570
|
try {
|
|
869
|
-
input = JSON.parse(input);
|
|
870
|
-
} catch {}
|
|
1571
|
+
input = JSON.parse(input);
|
|
1572
|
+
} catch {}
|
|
1573
|
+
}
|
|
1574
|
+
content.push({
|
|
1575
|
+
type: "tool-call",
|
|
1576
|
+
id: part.id,
|
|
1577
|
+
name: part.name,
|
|
1578
|
+
input,
|
|
1579
|
+
providerExecuted: part.executed,
|
|
1580
|
+
providerMetadata: toolReuse ? metadata(part.providerState) : undefined
|
|
1581
|
+
});
|
|
1582
|
+
if (!completed)
|
|
1583
|
+
continue;
|
|
1584
|
+
const toolContent = state.content ?? [];
|
|
1585
|
+
const single = toolContent.length === 1 ? toolContent[0] : undefined;
|
|
1586
|
+
const result = {
|
|
1587
|
+
type: "tool-result",
|
|
1588
|
+
id: part.id,
|
|
1589
|
+
name: part.name,
|
|
1590
|
+
providerExecuted: part.executed,
|
|
1591
|
+
providerMetadata: toolReuse ? metadata(part.providerResultState ?? part.providerState) : sameProvider ? metadata(part.providerResultState) : undefined,
|
|
1592
|
+
result: state.status === "error" ? { error: state.error, content: toolContent } : single?.type === "text" ? { type: "text", value: single.text } : { type: "content", value: toolContent },
|
|
1593
|
+
...state.status === "error" ? { resultType: "error" } : {}
|
|
1594
|
+
};
|
|
1595
|
+
if (part.executed === true)
|
|
1596
|
+
content.push(result);
|
|
1597
|
+
else
|
|
1598
|
+
results.push({ role: "tool", content: [result] });
|
|
1599
|
+
}
|
|
1600
|
+
return [...content.length ? [make("assistant", content)] : [], ...results];
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// src/v2/fold/native-replay.ts
|
|
1604
|
+
var coordinates = new WeakMap;
|
|
1605
|
+
function nativeRowID(message) {
|
|
1606
|
+
return coordinates.get(message) ?? message.id;
|
|
1607
|
+
}
|
|
1608
|
+
function copyNativeInput(messages) {
|
|
1609
|
+
const copies = reviveNative(encodeNative(messages));
|
|
1610
|
+
copies.forEach((copy, i) => {
|
|
1611
|
+
const original = messages[i];
|
|
1612
|
+
const id = original && nativeRowID(original);
|
|
1613
|
+
if (id !== undefined)
|
|
1614
|
+
coordinates.set(copy, id);
|
|
1615
|
+
});
|
|
1616
|
+
return copies;
|
|
1617
|
+
}
|
|
1618
|
+
function isLocalCheckpoint(row) {
|
|
1619
|
+
return row.type === "compaction" && row.data.status === "completed" && row.data.providerContext === undefined;
|
|
1620
|
+
}
|
|
1621
|
+
var conversational = (row) => RAW_MESSAGE_TYPES.includes(row.type) || row.type === "location-switched";
|
|
1622
|
+
|
|
1623
|
+
class NativeFoldReplay {
|
|
1624
|
+
storage;
|
|
1625
|
+
openReader;
|
|
1626
|
+
changed = new Set;
|
|
1627
|
+
dirty = new Set;
|
|
1628
|
+
unavailable = new Set;
|
|
1629
|
+
metadata = new Map;
|
|
1630
|
+
metadataThrough = new Map;
|
|
1631
|
+
loaded = new Map;
|
|
1632
|
+
sources = new Map;
|
|
1633
|
+
constructor(storage, openReader) {
|
|
1634
|
+
this.storage = storage;
|
|
1635
|
+
this.openReader = openReader;
|
|
1636
|
+
storage.onEvict((sessionID) => this.clear(sessionID));
|
|
1637
|
+
}
|
|
1638
|
+
account(sessionID) {
|
|
1639
|
+
const source = this.sources.get(sessionID);
|
|
1640
|
+
let bytes = 256 + (source?.length ?? 0) * 2;
|
|
1641
|
+
for (const row of this.metadata.get(sessionID) ?? [])
|
|
1642
|
+
bytes += 128 + (row.id.length + row.type.length) * 2;
|
|
1643
|
+
this.storage.accountReplay(sessionID, bytes);
|
|
1644
|
+
}
|
|
1645
|
+
async observeSource(sessionID, reader) {
|
|
1646
|
+
const source = reader.hostIdentity();
|
|
1647
|
+
const prior = this.sources.get(sessionID);
|
|
1648
|
+
if (prior && prior !== source)
|
|
1649
|
+
await this.forget(sessionID);
|
|
1650
|
+
this.sources.set(sessionID, source);
|
|
1651
|
+
this.account(sessionID);
|
|
1652
|
+
}
|
|
1653
|
+
onEvent(event) {
|
|
1654
|
+
const sessionID = event.data?.sessionID;
|
|
1655
|
+
if (sessionID && (event.type?.startsWith("session.revert.") || event.type === "session.message.content.updated"))
|
|
1656
|
+
this.invalidate(sessionID);
|
|
1657
|
+
}
|
|
1658
|
+
invalidate(sessionID, suppressNomination = false) {
|
|
1659
|
+
if (suppressNomination)
|
|
1660
|
+
this.unavailable.add(sessionID);
|
|
1661
|
+
this.dirty.add(sessionID);
|
|
1662
|
+
this.changed.add(sessionID);
|
|
1663
|
+
this.metadata.delete(sessionID);
|
|
1664
|
+
this.metadataThrough.delete(sessionID);
|
|
1665
|
+
this.account(sessionID);
|
|
1666
|
+
}
|
|
1667
|
+
tail(sessionID) {
|
|
1668
|
+
const record = this.storage.tail(sessionID);
|
|
1669
|
+
return record ? JSON.parse(record.data) : undefined;
|
|
1670
|
+
}
|
|
1671
|
+
async baseline(sessionID) {
|
|
1672
|
+
return this.tail(sessionID)?.baseline;
|
|
1673
|
+
}
|
|
1674
|
+
rendered(row, model) {
|
|
1675
|
+
return restoreRow(row, model, hostUsesMediaAssets() ? {
|
|
1676
|
+
asset: hostMediaAsset,
|
|
1677
|
+
unavailable: () => {}
|
|
1678
|
+
} : undefined);
|
|
1679
|
+
}
|
|
1680
|
+
record(row, messages) {
|
|
1681
|
+
const data = JSON.stringify(encodeNative(messages));
|
|
1682
|
+
return { id: row.id, type: row.type, seq: row.seq, data, digest: foldDigest(data) };
|
|
1683
|
+
}
|
|
1684
|
+
records(sessionID, native, reader) {
|
|
1685
|
+
let metadata = this.metadata.get(sessionID);
|
|
1686
|
+
const through = reader.latestSequence(sessionID);
|
|
1687
|
+
const known = new Set(metadata?.map((row) => row.id));
|
|
1688
|
+
if (!metadata || (this.metadataThrough.get(sessionID) ?? -1) < through || native.some((message) => message.id && !known.has(message.id))) {
|
|
1689
|
+
metadata = reader.rowStampsThrough(sessionID, through).filter(conversational);
|
|
1690
|
+
this.metadata.set(sessionID, metadata);
|
|
1691
|
+
this.metadataThrough.set(sessionID, through);
|
|
1692
|
+
}
|
|
1693
|
+
const byID = new Map(metadata.map((row) => [row.id, row]));
|
|
1694
|
+
const grouped = new Map;
|
|
1695
|
+
const anchorIndex = native.findIndex((message) => {
|
|
1696
|
+
const id = nativeRowID(message);
|
|
1697
|
+
return id !== undefined && byID.has(id);
|
|
1698
|
+
});
|
|
1699
|
+
const leading = native.slice(0, anchorIndex < 0 ? native.length : anchorIndex).filter((message) => !nativeRowID(message) && message.role === "system");
|
|
1700
|
+
const anchorMessage = native[anchorIndex];
|
|
1701
|
+
const anchorID = anchorMessage && nativeRowID(anchorMessage);
|
|
1702
|
+
const anchor = anchorID ? byID.get(anchorID) : undefined;
|
|
1703
|
+
const leadingRows = anchor ? metadata.filter((row) => row.type === "system" && row.seq < anchor.seq).slice(-leading.length) : [];
|
|
1704
|
+
const leadingIDs = new Map(leading.map((message, i) => [message, leadingRows[i]?.id]));
|
|
1705
|
+
let cursor = -1;
|
|
1706
|
+
let owner;
|
|
1707
|
+
for (const message of native) {
|
|
1708
|
+
let id = nativeRowID(message);
|
|
1709
|
+
if (!id && message.role === "system") {
|
|
1710
|
+
if (leadingIDs.has(message)) {
|
|
1711
|
+
id = leadingIDs.get(message);
|
|
1712
|
+
if (!id)
|
|
1713
|
+
continue;
|
|
1714
|
+
} else
|
|
1715
|
+
id = metadata.find((row) => row.type === "system" && row.seq > cursor)?.id;
|
|
1716
|
+
}
|
|
1717
|
+
if (!id && message.role === "tool")
|
|
1718
|
+
id = owner;
|
|
1719
|
+
const row = id ? byID.get(id) : undefined;
|
|
1720
|
+
if (!row)
|
|
1721
|
+
throw new NativeReplayUnavailable("Native carrier has no source row coordinate");
|
|
1722
|
+
cursor = row.seq;
|
|
1723
|
+
coordinates.set(message, row.id);
|
|
1724
|
+
if (message.role !== "tool")
|
|
1725
|
+
owner = row.id;
|
|
1726
|
+
const messages = grouped.get(row) ?? [];
|
|
1727
|
+
messages.push(message);
|
|
1728
|
+
grouped.set(row, messages);
|
|
1729
|
+
}
|
|
1730
|
+
return [...grouped].map(([row, messages]) => this.record(row, messages));
|
|
1731
|
+
}
|
|
1732
|
+
async capture(draft, native) {
|
|
1733
|
+
const release = this.storage.hold(draft.sessionID);
|
|
1734
|
+
try {
|
|
1735
|
+
rememberHostMedia(native);
|
|
1736
|
+
const reader = this.openReader(draft.sessionID);
|
|
1737
|
+
try {
|
|
1738
|
+
const rows = this.records(draft.sessionID, native, reader);
|
|
1739
|
+
const baseline = draft.messages.find((message) => message.id === HEAD_IDS[0])?.content.find((part) => part.type === "text")?.text;
|
|
1740
|
+
const through = Math.max(-1, ...rows.map((row) => row.seq));
|
|
1741
|
+
const tail = {
|
|
1742
|
+
model: `${draft.model.providerID}/${draft.model.id}`,
|
|
1743
|
+
sourceSeq: through,
|
|
1744
|
+
baseline: typeof baseline === "string" ? baseline : this.tail(draft.sessionID)?.baseline
|
|
1745
|
+
};
|
|
1746
|
+
await this.storage.saveTail(draft.sessionID, JSON.stringify(tail), rows);
|
|
1747
|
+
this.storage.truncate(draft.sessionID, through);
|
|
1748
|
+
this.loaded.set(draft.sessionID, through);
|
|
1749
|
+
this.unavailable.delete(draft.sessionID);
|
|
1750
|
+
this.changed.delete(draft.sessionID);
|
|
1751
|
+
} finally {
|
|
1752
|
+
reader.close();
|
|
1753
|
+
}
|
|
1754
|
+
} catch {
|
|
1755
|
+
this.unavailable.add(draft.sessionID);
|
|
1756
|
+
this.invalidate(draft.sessionID);
|
|
1757
|
+
} finally {
|
|
1758
|
+
this.account(draft.sessionID);
|
|
1759
|
+
release();
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
async canReplay(sessionID, _reader) {
|
|
1763
|
+
return !this.unavailable.has(sessionID) && typeof await this.baseline(sessionID) === "string";
|
|
1764
|
+
}
|
|
1765
|
+
async supply(args) {
|
|
1766
|
+
const release = this.storage.hold(args.draft.sessionID);
|
|
1767
|
+
try {
|
|
1768
|
+
return await this.supplyHeld(args);
|
|
1769
|
+
} finally {
|
|
1770
|
+
this.account(args.draft.sessionID);
|
|
1771
|
+
release();
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
async supplyHeld(args) {
|
|
1775
|
+
const { draft, reader, summary } = args;
|
|
1776
|
+
const tail = this.tail(draft.sessionID);
|
|
1777
|
+
const id = reader.latestRunningCompaction(draft.sessionID)?.id;
|
|
1778
|
+
if (!id || !tail || tail.model !== `${draft.model.providerID}/${draft.model.id}`)
|
|
1779
|
+
return false;
|
|
1780
|
+
const through = reader.latestSequence(draft.sessionID);
|
|
1781
|
+
const after = this.dirty.has(draft.sessionID) ? -1 : tail.sourceSeq;
|
|
1782
|
+
const fresh = reader.range(draft.sessionID, after, through).filter(conversational);
|
|
1783
|
+
const records = fresh.map((row) => this.record(row, this.rendered(row, draft.model)));
|
|
1784
|
+
if (this.dirty.has(draft.sessionID))
|
|
1785
|
+
this.storage.replaceRows(draft.sessionID, through, records);
|
|
1786
|
+
await this.storage.saveTail(draft.sessionID, JSON.stringify({
|
|
1787
|
+
...tail,
|
|
1788
|
+
sourceSeq: Math.max(tail.sourceSeq, ...fresh.map((row) => row.seq))
|
|
1789
|
+
}), records);
|
|
1790
|
+
this.dirty.delete(draft.sessionID);
|
|
1791
|
+
this.metadata.delete(draft.sessionID);
|
|
1792
|
+
this.metadataThrough.delete(draft.sessionID);
|
|
1793
|
+
this.loaded.set(draft.sessionID, through);
|
|
1794
|
+
const snapshot = {
|
|
1795
|
+
source: this.storage.sourceID,
|
|
1796
|
+
sessionID: draft.sessionID,
|
|
1797
|
+
admissionID: id,
|
|
1798
|
+
summary
|
|
1799
|
+
};
|
|
1800
|
+
await this.storage.supply(draft.sessionID, id, JSON.stringify(snapshot));
|
|
1801
|
+
return true;
|
|
1802
|
+
}
|
|
1803
|
+
async restore(sessionID, cut, model, options = {}) {
|
|
1804
|
+
const release = this.storage.hold(sessionID);
|
|
1805
|
+
try {
|
|
1806
|
+
return await this.restoreHeld(sessionID, cut, model, options);
|
|
1807
|
+
} finally {
|
|
1808
|
+
this.account(sessionID);
|
|
1809
|
+
release();
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
async restoreHeld(sessionID, cut, model, options) {
|
|
1813
|
+
if (!isLocalCheckpoint(cut))
|
|
1814
|
+
return;
|
|
1815
|
+
const resolvedModel = {
|
|
1816
|
+
providerID: model.slice(0, model.indexOf("/")),
|
|
1817
|
+
id: model.slice(model.indexOf("/") + 1)
|
|
1818
|
+
};
|
|
1819
|
+
const tail = this.tail(sessionID);
|
|
1820
|
+
if (tail && tail.model !== model)
|
|
1821
|
+
this.invalidate(sessionID);
|
|
1822
|
+
let after = options.after ?? -1;
|
|
1823
|
+
if (options.bounded && options.after === undefined && !this.changed.has(sessionID)) {
|
|
1824
|
+
const state = getOrCreateSessionMeta(this.storage.db, sessionID);
|
|
1825
|
+
const max = this.storage.db.prepare("SELECT COALESCE(MAX(id),0) AS id FROM m0_mutation_log WHERE session_id=?").get(sessionID);
|
|
1826
|
+
const boundary = this.storage.db.prepare("SELECT cached_m0_last_baseline_end_message_id AS id FROM session_meta WHERE session_id=?").get(sessionID)?.id;
|
|
1827
|
+
if (max.id <= (state.cachedM0MaxMutationId ?? 0) && boundary)
|
|
1828
|
+
after = this.storage.sequenceForID(sessionID, boundary) ?? -1;
|
|
1829
|
+
}
|
|
1830
|
+
const through = cut.seq - 1;
|
|
1831
|
+
let reader;
|
|
1832
|
+
try {
|
|
1833
|
+
let metadata = this.metadata.get(sessionID);
|
|
1834
|
+
if (!metadata || (this.metadataThrough.get(sessionID) ?? -1) < through) {
|
|
1835
|
+
reader = this.openReader(sessionID);
|
|
1836
|
+
metadata = reader.rowStampsThrough(sessionID, through).filter(conversational);
|
|
1837
|
+
this.metadata.set(sessionID, metadata);
|
|
1838
|
+
this.metadataThrough.set(sessionID, through);
|
|
1839
|
+
}
|
|
1840
|
+
const required = metadata.filter((row) => row.seq > after && row.seq <= through);
|
|
1841
|
+
if (this.dirty.has(sessionID) || !tail || (this.loaded.get(sessionID) ?? -1) < through && !this.storage.admission(sessionID, cut.id) || !this.storage.containsRows(sessionID, required.map((row) => row.id))) {
|
|
1842
|
+
reader ??= this.openReader(sessionID);
|
|
1843
|
+
const fresh = reader.range(sessionID, after, through).filter(conversational);
|
|
1844
|
+
const records = fresh.map((row) => this.record(row, this.rendered(row, resolvedModel)));
|
|
1845
|
+
const restored = new Set(records.map((row) => row.id));
|
|
1846
|
+
if (required.some((row) => !restored.has(row.id)))
|
|
1847
|
+
throw new NativeReplayUnavailable("Host restore omitted a required source row");
|
|
1848
|
+
this.storage.replaceRows(sessionID, through, records, after);
|
|
1849
|
+
await this.storage.saveTail(sessionID, JSON.stringify({
|
|
1850
|
+
model,
|
|
1851
|
+
sourceSeq: through,
|
|
1852
|
+
baseline: tail?.baseline ?? cut.data.summary
|
|
1853
|
+
}), []);
|
|
1854
|
+
this.loaded.set(sessionID, through);
|
|
1855
|
+
this.dirty.delete(sessionID);
|
|
1856
|
+
}
|
|
1857
|
+
} finally {
|
|
1858
|
+
reader?.close();
|
|
1859
|
+
}
|
|
1860
|
+
const messages = [];
|
|
1861
|
+
for (const row of this.storage.rows(sessionID, after, cut.seq - 1)) {
|
|
1862
|
+
const revived = reviveNative(JSON.parse(row.data));
|
|
1863
|
+
for (const message of revived)
|
|
1864
|
+
coordinates.set(message, row.id);
|
|
1865
|
+
messages.push(...revived);
|
|
1866
|
+
}
|
|
1867
|
+
return messages;
|
|
1868
|
+
}
|
|
1869
|
+
async restoreRead(sessionID, cut, model) {
|
|
1870
|
+
const reader = this.openReader(sessionID);
|
|
1871
|
+
try {
|
|
1872
|
+
return reader.range(sessionID, -1, cut.seq - 1).filter(conversational).flatMap((row) => {
|
|
1873
|
+
const messages = this.rendered(row, model);
|
|
1874
|
+
for (const message of messages)
|
|
1875
|
+
coordinates.set(message, row.id);
|
|
1876
|
+
return messages;
|
|
1877
|
+
});
|
|
1878
|
+
} finally {
|
|
1879
|
+
reader.close();
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
sourceChanged(sessionID) {
|
|
1883
|
+
return this.changed.has(sessionID);
|
|
1884
|
+
}
|
|
1885
|
+
clear(sessionID) {
|
|
1886
|
+
this.sources.delete(sessionID);
|
|
1887
|
+
this.metadata.delete(sessionID);
|
|
1888
|
+
this.metadataThrough.delete(sessionID);
|
|
1889
|
+
this.loaded.delete(sessionID);
|
|
1890
|
+
this.unavailable.delete(sessionID);
|
|
1891
|
+
this.dirty.delete(sessionID);
|
|
1892
|
+
this.changed.delete(sessionID);
|
|
1893
|
+
}
|
|
1894
|
+
async forget(sessionID) {
|
|
1895
|
+
await this.storage.forget(sessionID);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
// src/v2/fold/policy.ts
|
|
1900
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1901
|
+
class HostFoldPolicy {
|
|
1902
|
+
deps;
|
|
1903
|
+
active = new Set;
|
|
1904
|
+
owner = randomUUID2();
|
|
1905
|
+
constructor(deps) {
|
|
1906
|
+
this.deps = deps;
|
|
1907
|
+
}
|
|
1908
|
+
owned(sessionID, cut, id) {
|
|
1909
|
+
const row = this.deps.storage.admission(sessionID, id);
|
|
1910
|
+
if (!isLocalCheckpoint(cut) || cut.id !== id || !row?.payload || row.digest !== foldDigest(row.payload))
|
|
1911
|
+
return false;
|
|
1912
|
+
const snapshot = JSON.parse(row.payload);
|
|
1913
|
+
return snapshot.summary === cut.data.summary && snapshot.source === this.deps.storage.sourceID && snapshot.sessionID === sessionID && snapshot.admissionID === id;
|
|
1914
|
+
}
|
|
1915
|
+
async idle(sessionID) {
|
|
1916
|
+
const { storage, session, threshold } = this.deps;
|
|
1917
|
+
if (!Number.isSafeInteger(threshold) || threshold <= 0 || typeof session.compact !== "function" || this.active.has(sessionID))
|
|
1918
|
+
return;
|
|
1919
|
+
this.active.add(sessionID);
|
|
1920
|
+
const now = this.deps.now ?? Date.now;
|
|
1921
|
+
let attempt;
|
|
1922
|
+
let claimed = false;
|
|
1923
|
+
try {
|
|
1924
|
+
const reader = this.deps.openReader();
|
|
1925
|
+
try {
|
|
1926
|
+
const cut = reader.latestCompaction(sessionID);
|
|
1927
|
+
const previous = storage.latestAttempt(sessionID);
|
|
1928
|
+
const old = previous ? JSON.parse(previous.data) : undefined;
|
|
1929
|
+
if (cut && previous?.status === "pending" && old && this.owned(sessionID, cut, old.id)) {
|
|
1930
|
+
if (await storage.claim(sessionID, old.id, previous.data, this.owner, now()) && await storage.finish(sessionID, old.id, this.owner, "completed", now()))
|
|
1931
|
+
this.deps.log(sessionID, `v2 host fold: rows_before=${old.before} rows_after=${reader.storedRowsAfter(sessionID, cut.seq)} duration_ms=${now() - old.started} reason=row_threshold id=${cut.id} status=completed`);
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
const after = cut?.seq ?? -1;
|
|
1935
|
+
const before = reader.storedRowsAfter(sessionID, after);
|
|
1936
|
+
if (before < threshold || !await this.deps.canReplay(sessionID, reader))
|
|
1937
|
+
return;
|
|
1938
|
+
const retry = old?.after === after ? old.attempt + (previous?.status === "failed" || previous?.status === "coalesced" ? 1 : 0) : 0;
|
|
1939
|
+
const id = old?.after === after && previous?.status === "pending" ? old.id : `msg_mc_fold_${foldDigest(`${storage.sourceID}/${sessionID}/${after}/${retry}`).slice(0, 24)}`;
|
|
1940
|
+
attempt = {
|
|
1941
|
+
after,
|
|
1942
|
+
before,
|
|
1943
|
+
watermark: reader.latestSequence(sessionID),
|
|
1944
|
+
attempt: retry,
|
|
1945
|
+
id,
|
|
1946
|
+
started: now()
|
|
1947
|
+
};
|
|
1948
|
+
claimed = await storage.claim(sessionID, id, JSON.stringify(attempt), this.owner, now());
|
|
1949
|
+
if (!claimed)
|
|
1950
|
+
return;
|
|
1951
|
+
} finally {
|
|
1952
|
+
reader.close();
|
|
1953
|
+
}
|
|
1954
|
+
const admitted = await session.compact({
|
|
1955
|
+
sessionID,
|
|
1956
|
+
id: attempt.id,
|
|
1957
|
+
delivery: "queue"
|
|
1958
|
+
});
|
|
1959
|
+
await session.wait({ sessionID });
|
|
1960
|
+
const settled = this.deps.openReader();
|
|
1961
|
+
try {
|
|
1962
|
+
const cut = settled.latestCompaction(sessionID);
|
|
1963
|
+
if (admitted?.id !== attempt.id || !cut || cut.seq <= attempt.after || !this.owned(sessionID, cut, attempt.id)) {
|
|
1964
|
+
if (await storage.finish(sessionID, attempt.id, this.owner, "coalesced", now()))
|
|
1965
|
+
this.deps.log(sessionID, `v2 host fold: rows_before=${attempt.before} rows_after=unknown duration_ms=${now() - attempt.started} reason=row_threshold id=${attempt.id} actual_id=${cut?.id ?? admitted?.id ?? "unknown"} status=coalesced`);
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
const after = settled.storedRowsAfter(sessionID, cut.seq);
|
|
1969
|
+
if (await storage.finish(sessionID, attempt.id, this.owner, "completed", now()))
|
|
1970
|
+
this.deps.log(sessionID, `v2 host fold: rows_before=${attempt.before} rows_after=${after} duration_ms=${now() - attempt.started} reason=row_threshold id=${cut.id} status=completed`);
|
|
1971
|
+
} finally {
|
|
1972
|
+
settled.close();
|
|
1973
|
+
}
|
|
1974
|
+
} catch (error) {
|
|
1975
|
+
if (claimed && attempt) {
|
|
1976
|
+
let finished = false;
|
|
1977
|
+
try {
|
|
1978
|
+
finished = await storage.finish(sessionID, attempt.id, this.owner, "failed", now());
|
|
1979
|
+
} catch {
|
|
1980
|
+
finished = true;
|
|
1981
|
+
}
|
|
1982
|
+
if (finished)
|
|
1983
|
+
this.deps.log(sessionID, `v2 host fold: rows_before=${attempt.before} rows_after=unknown duration_ms=${now() - attempt.started} reason=row_threshold id=${attempt.id} status=failed error=${JSON.stringify(String(error))}`);
|
|
1984
|
+
} else
|
|
1985
|
+
this.deps.log(sessionID, `v2 host fold: rows_before=unknown rows_after=unknown duration_ms=0 reason=row_threshold status=failed error=${JSON.stringify(String(error))} cache_error=${JSON.stringify(String(error))}`);
|
|
1986
|
+
} finally {
|
|
1987
|
+
this.active.delete(sessionID);
|
|
871
1988
|
}
|
|
872
|
-
content.push({
|
|
873
|
-
type: "tool-call",
|
|
874
|
-
id: part.id,
|
|
875
|
-
name: part.name,
|
|
876
|
-
input,
|
|
877
|
-
providerExecuted: part.executed,
|
|
878
|
-
providerMetadata: toolReuse ? metadata(part.providerState) : undefined
|
|
879
|
-
});
|
|
880
|
-
if (!completed)
|
|
881
|
-
continue;
|
|
882
|
-
const toolContent = state.content ?? [];
|
|
883
|
-
const single = toolContent.length === 1 ? toolContent[0] : undefined;
|
|
884
|
-
const result = {
|
|
885
|
-
type: "tool-result",
|
|
886
|
-
id: part.id,
|
|
887
|
-
name: part.name,
|
|
888
|
-
providerExecuted: part.executed,
|
|
889
|
-
providerMetadata: toolReuse ? metadata(part.providerResultState ?? part.providerState) : sameProvider ? metadata(part.providerResultState) : undefined,
|
|
890
|
-
result: state.status === "error" ? { error: state.error, content: toolContent } : single?.type === "text" ? { type: "text", value: single.text } : { type: "content", value: toolContent },
|
|
891
|
-
...state.status === "error" ? { resultType: "error" } : {}
|
|
892
|
-
};
|
|
893
|
-
if (part.executed === true)
|
|
894
|
-
content.push(result);
|
|
895
|
-
else
|
|
896
|
-
results.push({ role: "tool", content: [result] });
|
|
897
1989
|
}
|
|
898
|
-
return [...content.length ? [make("assistant", content)] : [], ...results];
|
|
899
1990
|
}
|
|
900
1991
|
|
|
901
1992
|
// src/v2/fork-inheritance.ts
|
|
@@ -1459,7 +2550,7 @@ async function createV2HiddenCompletionExecutor(host, options) {
|
|
|
1459
2550
|
const runs = new WeakMap;
|
|
1460
2551
|
const generation = options.generation ?? "opencode2";
|
|
1461
2552
|
const roleTails = new Map;
|
|
1462
|
-
const note = options.log ??
|
|
2553
|
+
const note = options.log ?? log2;
|
|
1463
2554
|
const removeSession = host.removeSession;
|
|
1464
2555
|
if (!removeSession)
|
|
1465
2556
|
throw new Error("Magic Context requires OpenCode 2.0.22 session.remove");
|
|
@@ -1694,7 +2785,8 @@ async function createV2HiddenCompletionExecutor(host, options) {
|
|
|
1694
2785
|
...budget ? [budgetStopped] : []
|
|
1695
2786
|
]);
|
|
1696
2787
|
if (!attempt.shaped) {
|
|
1697
|
-
|
|
2788
|
+
const task = run.identity.title.replace(/^magic-context-dream-/, "");
|
|
2789
|
+
throw new HiddenCompletionRefusal("hidden_prompt_unrecognized", `Host did not dispatch the hidden child context hook (task=${task}, agent=${run.identity.agent}, child=${run.child.id}, directory=${run.identity.directory}, stage=context/HiddenChildHook.apply). Check that the child's location loads the owning Magic Context instance; changing models cannot repair missing context shaping.`, true);
|
|
1698
2790
|
}
|
|
1699
2791
|
const text = assistantText(row);
|
|
1700
2792
|
const system = typeof request.body.system === "string" ? request.body.system : run.identity.system;
|
|
@@ -1829,7 +2921,7 @@ async function deliverPendingChannel2(context, db, sessionID, baseline) {
|
|
|
1829
2921
|
async function registerV2Commands(args) {
|
|
1830
2922
|
const domain = args.command;
|
|
1831
2923
|
if (!domain || typeof domain.transform !== "function") {
|
|
1832
|
-
|
|
2924
|
+
log2("[magic-context] v2 host exposes no command domain; /ctx-* commands are TUI-only");
|
|
1833
2925
|
return false;
|
|
1834
2926
|
}
|
|
1835
2927
|
const descriptions = getMagicContextBuiltinCommands(args.compactionEnabled);
|
|
@@ -1845,10 +2937,10 @@ async function registerV2Commands(args) {
|
|
|
1845
2937
|
}
|
|
1846
2938
|
});
|
|
1847
2939
|
} catch (error) {
|
|
1848
|
-
|
|
2940
|
+
log2("[magic-context] v2 command registration failed", error);
|
|
1849
2941
|
return false;
|
|
1850
2942
|
}
|
|
1851
|
-
|
|
2943
|
+
log2(`[magic-context] registered server-side commands: ${commands.map((command) => command.name).join(" ")}`);
|
|
1852
2944
|
return true;
|
|
1853
2945
|
}
|
|
1854
2946
|
function commandArgument(input, name) {
|
|
@@ -1918,6 +3010,10 @@ function buildV2Commands(rpc, directory) {
|
|
|
1918
3010
|
const error = typeof reply.error === "string" ? reply.error : "dream request failed";
|
|
1919
3011
|
throw new Error(`Magic Context could not start the dream run: ${error}`);
|
|
1920
3012
|
}
|
|
3013
|
+
pushNotification2("toast", {
|
|
3014
|
+
message: "Dream run started; the summary appears when it finishes",
|
|
3015
|
+
variant: "info"
|
|
3016
|
+
}, input.sessionID);
|
|
1921
3017
|
}
|
|
1922
3018
|
},
|
|
1923
3019
|
{
|
|
@@ -1925,702 +3021,328 @@ function buildV2Commands(rpc, directory) {
|
|
|
1925
3021
|
execute: async (input) => {
|
|
1926
3022
|
const reply = await rpc.dispatch("flush", { sessionId: input.sessionID });
|
|
1927
3023
|
reportRpcReply(input.sessionID, "Flush", reply, "Flush started");
|
|
3024
|
+
if (reply.ok === true) {
|
|
3025
|
+
pushNotification2("action", { action: "refresh-sidebar" }, input.sessionID);
|
|
3026
|
+
}
|
|
1928
3027
|
}
|
|
1929
3028
|
},
|
|
1930
3029
|
{
|
|
1931
3030
|
name: "ctx-embed",
|
|
1932
3031
|
execute: async (input) => {
|
|
1933
3032
|
const argument = commandArgument(input, "ctx-embed").toLowerCase();
|
|
1934
|
-
if (argument !== "" && argument !== "start" && argument !== "pause") {
|
|
1935
|
-
reportUsage(input.sessionID, "Usage: /ctx-embed (status), /ctx-embed start, or /ctx-embed pause");
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
const reply = await rpc.dispatch("embed", {
|
|
1939
|
-
sessionId: input.sessionID,
|
|
1940
|
-
action: argument === "" ? "status" : argument,
|
|
1941
|
-
directory
|
|
1942
|
-
});
|
|
1943
|
-
reportRpcReply(input.sessionID, "Embed", reply, "Embedding started; the summary appears when it finishes");
|
|
1944
|
-
}
|
|
1945
|
-
},
|
|
1946
|
-
{
|
|
1947
|
-
name: "ctx-wrapup",
|
|
1948
|
-
execute: async (input) => {
|
|
1949
|
-
const argument = commandArgument(input, "ctx-wrapup");
|
|
1950
|
-
if (argument !== "" && !/^\d+$/.test(argument)) {
|
|
1951
|
-
const usage = "Usage: /ctx-wrapup [messages_to_keep] where messages_to_keep is a positive integer";
|
|
1952
|
-
reportUsage(input.sessionID, usage);
|
|
1953
|
-
throw new Error(usage);
|
|
1954
|
-
}
|
|
1955
|
-
const messagesToKeep = argument === "" ? 20 : Number.parseInt(argument, 10);
|
|
1956
|
-
if (messagesToKeep <= 0) {
|
|
1957
|
-
const usage = "messages_to_keep must be a positive integer";
|
|
1958
|
-
reportUsage(input.sessionID, usage);
|
|
1959
|
-
throw new Error(usage);
|
|
1960
|
-
}
|
|
1961
|
-
const reply = await rpc.dispatch("wrapup", {
|
|
1962
|
-
sessionId: input.sessionID,
|
|
1963
|
-
messagesToKeep
|
|
1964
|
-
});
|
|
1965
|
-
reportRpcReply(input.sessionID, "Wrapup", reply, "Wrapup started; the summary appears when it finishes");
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
];
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
// src/v2/hooks/deleted-session-tombstones.ts
|
|
1972
|
-
class DeletedSessionTombstones {
|
|
1973
|
-
limit;
|
|
1974
|
-
#ids = new Set;
|
|
1975
|
-
constructor(limit = 1000) {
|
|
1976
|
-
this.limit = limit;
|
|
1977
|
-
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
1978
|
-
throw new Error("Deleted-session tombstone limit must be a positive integer");
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
add(sessionID) {
|
|
1982
|
-
this.#ids.delete(sessionID);
|
|
1983
|
-
this.#ids.add(sessionID);
|
|
1984
|
-
while (this.#ids.size > this.limit) {
|
|
1985
|
-
const oldest = this.#ids.values().next().value;
|
|
1986
|
-
if (oldest === undefined)
|
|
1987
|
-
break;
|
|
1988
|
-
this.#ids.delete(oldest);
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
has(sessionID) {
|
|
1992
|
-
return this.#ids.has(sessionID);
|
|
1993
|
-
}
|
|
1994
|
-
clear() {
|
|
1995
|
-
this.#ids.clear();
|
|
1996
|
-
}
|
|
1997
|
-
get size() {
|
|
1998
|
-
return this.#ids.size;
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
|
|
2002
|
-
// src/v2/retrospective-raw-provider.ts
|
|
2003
|
-
class V2RetrospectiveRawProvider {
|
|
2004
|
-
contextDb;
|
|
2005
|
-
openReader;
|
|
2006
|
-
reader;
|
|
2007
|
-
constructor(contextDb, openReader = () => new V2StoreReader(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"))) {
|
|
2008
|
-
this.contextDb = contextDb;
|
|
2009
|
-
this.openReader = openReader;
|
|
2010
|
-
}
|
|
2011
|
-
store() {
|
|
2012
|
-
return this.reader ??= this.openReader();
|
|
2013
|
-
}
|
|
2014
|
-
listProjectSessions(projectIdentity) {
|
|
2015
|
-
const sessions = this.contextDb.prepare(`
|
|
2016
|
-
SELECT sp.session_id FROM session_projects sp
|
|
2017
|
-
LEFT JOIN session_meta m ON m.session_id = sp.session_id
|
|
2018
|
-
WHERE sp.project_path = ? AND sp.harness = 'opencode2'
|
|
2019
|
-
AND COALESCE(m.is_subagent, 0) = 0
|
|
2020
|
-
ORDER BY sp.session_id`).all(projectIdentity);
|
|
2021
|
-
const roots = this.store().rootSessionActivity(sessions.map((row) => row.session_id));
|
|
2022
|
-
return sessions.filter((row) => roots.has(row.session_id)).map((row) => ({ sessionId: row.session_id, updatedAt: roots.get(row.session_id) }));
|
|
2023
|
-
}
|
|
2024
|
-
readOldestMessageTimesSince(sessionIds, sinceMs) {
|
|
2025
|
-
return this.store().oldestUserMessageTimesSince(sessionIds, sinceMs);
|
|
2026
|
-
}
|
|
2027
|
-
read(sessionId, boundaryMs, limit, before = false) {
|
|
2028
|
-
return this.store().retrospectiveUserPage(sessionId, {
|
|
2029
|
-
boundaryMs,
|
|
2030
|
-
before,
|
|
2031
|
-
limit,
|
|
2032
|
-
maxChars: RETROSPECTIVE_MAX_USER_MESSAGE_CHARS,
|
|
2033
|
-
truncationMarker: RETROSPECTIVE_MESSAGE_TRUNCATION_MARKER
|
|
2034
|
-
}).map((row) => ({
|
|
2035
|
-
sessionId,
|
|
2036
|
-
ordinal: row.seq,
|
|
2037
|
-
role: "user",
|
|
2038
|
-
text: cleanUserText2(row.text),
|
|
2039
|
-
ts: row.time_created
|
|
2040
|
-
}));
|
|
2041
|
-
}
|
|
2042
|
-
readUserMessagesSince(sessionId, sinceMs, capPerSession) {
|
|
2043
|
-
const cap = Math.max(1, Math.min(9999, Math.floor(capPerSession)));
|
|
2044
|
-
const rows = this.read(sessionId, sinceMs, cap + 1);
|
|
2045
|
-
return { messages: rows.slice(0, cap), truncated: rows.length > cap };
|
|
2046
|
-
}
|
|
2047
|
-
readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
2048
|
-
return this.read(sessionId, beforeMs, Math.max(1, Math.min(1e4, Math.floor(count))), true).reverse().filter((row) => hasMeaningfulUserText2([{ type: "text", text: row.text }]));
|
|
2049
|
-
}
|
|
2050
|
-
dispose() {
|
|
2051
|
-
this.reader?.close();
|
|
2052
|
-
this.reader = undefined;
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
// src/v2/hooks/dream-manual.ts
|
|
2057
|
-
function resolveManualDreamTask(raw) {
|
|
2058
|
-
const requested = typeof raw === "string" ? raw.trim() : "";
|
|
2059
|
-
if (!requested)
|
|
2060
|
-
return {};
|
|
2061
|
-
if (!isCanonicalDreamTask(requested)) {
|
|
2062
|
-
return {
|
|
2063
|
-
error: `Unknown task "${requested}". Valid tasks: ${CANONICAL_DREAM_TASKS.join(", ")}.`
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
return { task: requested };
|
|
2067
|
-
}
|
|
2068
|
-
function selectRunnableDreamTasks(args) {
|
|
2069
|
-
const requiresTools = (task) => DREAM_TASK_CAPABILITIES[task].requiresTools;
|
|
2070
|
-
if (args.toolsSupported)
|
|
2071
|
-
return { runnable: [...args.tasks], unsupported: [] };
|
|
2072
|
-
if (args.requestedTask !== undefined) {
|
|
2073
|
-
if (requiresTools(args.requestedTask)) {
|
|
2074
|
-
return { runnable: [], unsupported: [args.requestedTask] };
|
|
2075
|
-
}
|
|
2076
|
-
return {
|
|
2077
|
-
runnable: args.tasks.filter((config) => config.task === args.requestedTask),
|
|
2078
|
-
unsupported: []
|
|
2079
|
-
};
|
|
2080
|
-
}
|
|
2081
|
-
const unsupported = args.tasks.filter((config) => config.schedule.trim() !== "" && requiresTools(config.task)).map((config) => config.task);
|
|
2082
|
-
return {
|
|
2083
|
-
runnable: args.tasks.filter((config) => !requiresTools(config.task)),
|
|
2084
|
-
unsupported
|
|
2085
|
-
};
|
|
2086
|
-
}
|
|
2087
|
-
async function runManualDreamNow(args) {
|
|
2088
|
-
const tasks = buildDreamTaskRuntimeConfigs2(args.dreamer, "opencode", args.language, args.mural?.model);
|
|
2089
|
-
const executor = createDreamTaskExecutor({
|
|
2090
|
-
hiddenCompletionExecutor: args.executor,
|
|
2091
|
-
parentSessionId: args.sessionId,
|
|
2092
|
-
sessionDirectory: args.directory,
|
|
2093
|
-
openOpenCodeDb: () => null,
|
|
2094
|
-
retrospectiveRawProvider: (db) => new V2RetrospectiveRawProvider(db),
|
|
2095
|
-
language: args.language,
|
|
2096
|
-
mural: args.mural
|
|
2097
|
-
});
|
|
2098
|
-
const selection = selectRunnableDreamTasks({
|
|
2099
|
-
tasks,
|
|
2100
|
-
toolsSupported: args.executor.capabilities.tools === true,
|
|
2101
|
-
...args.task !== undefined ? { requestedTask: args.task } : {}
|
|
2102
|
-
});
|
|
2103
|
-
const summary = await runManualDream({
|
|
2104
|
-
db: args.db,
|
|
2105
|
-
projectIdentity: args.projectIdentity,
|
|
2106
|
-
tasks: selection.runnable,
|
|
2107
|
-
executor,
|
|
2108
|
-
...args.task !== undefined ? { task: args.task } : {}
|
|
2109
|
-
});
|
|
2110
|
-
return { summary, unsupportedTasks: selection.unsupported };
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
// src/v2/hooks/dream-timer.ts
|
|
2114
|
-
function findV2DreamParentSession(openReader, directory) {
|
|
2115
|
-
let reader;
|
|
2116
|
-
try {
|
|
2117
|
-
reader = openReader();
|
|
2118
|
-
const parent = reader.latestRootSessionInDirectory(directory);
|
|
2119
|
-
if (!parent) {
|
|
2120
|
-
log(`[dreamer] no OpenCode 2 session in ${directory} to parent a timer-started dream run; tasks that need a child session are skipped until one exists`);
|
|
2121
|
-
}
|
|
2122
|
-
return parent;
|
|
2123
|
-
} catch (error) {
|
|
2124
|
-
log(`[dreamer] OpenCode 2 parent session lookup failed for ${directory}:`, error);
|
|
2125
|
-
return;
|
|
2126
|
-
} finally {
|
|
2127
|
-
reader?.close();
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
function registerV2DreamScheduleTimer(args) {
|
|
2131
|
-
const { config, directory, dreamer, executor } = args;
|
|
2132
|
-
const gitCommitIndexing = config.memory.git_commit_indexing;
|
|
2133
|
-
let disposed = false;
|
|
2134
|
-
const registration = startDreamScheduleTimer({
|
|
2135
|
-
directory,
|
|
2136
|
-
projectIdentity: args.projectIdentity,
|
|
2137
|
-
harness: "opencode",
|
|
2138
|
-
hiddenCompletionExecutor: executor,
|
|
2139
|
-
findParentSessionId: () => findV2DreamParentSession(args.openReader, directory),
|
|
2140
|
-
openOpenCodeDb: () => null,
|
|
2141
|
-
retrospectiveRawProvider: (db) => new V2RetrospectiveRawProvider(db),
|
|
2142
|
-
dreamerConfig: dreamer,
|
|
2143
|
-
sampleDreamRun: () => {
|
|
2144
|
-
const current = dreamerRunConfig(config, args.liveConfig());
|
|
2145
|
-
return { dreamerConfig: current.dreamer ?? dreamer, mural: current.mural };
|
|
3033
|
+
if (argument !== "" && argument !== "start" && argument !== "pause") {
|
|
3034
|
+
reportUsage(input.sessionID, "Usage: /ctx-embed (status), /ctx-embed start, or /ctx-embed pause");
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
const reply = await rpc.dispatch("embed", {
|
|
3038
|
+
sessionId: input.sessionID,
|
|
3039
|
+
action: argument === "" ? "status" : argument,
|
|
3040
|
+
directory
|
|
3041
|
+
});
|
|
3042
|
+
reportRpcReply(input.sessionID, "Embed", reply, "Embedding started; the summary appears when it finishes");
|
|
3043
|
+
}
|
|
2146
3044
|
},
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
log(`[magic-context] v2 dream timer registration failed (continuing without it): ${error}`);
|
|
2162
|
-
return;
|
|
2163
|
-
});
|
|
2164
|
-
return {
|
|
2165
|
-
async dispose() {
|
|
2166
|
-
if (disposed)
|
|
2167
|
-
return;
|
|
2168
|
-
disposed = true;
|
|
2169
|
-
const stop = await registration;
|
|
2170
|
-
stop?.();
|
|
2171
|
-
}
|
|
2172
|
-
};
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
|
-
// src/v2/hooks/dream-trigger.ts
|
|
2176
|
-
async function sessionInThisLocation(context, sessionID) {
|
|
2177
|
-
let location;
|
|
2178
|
-
try {
|
|
2179
|
-
location = (await context.session.get({ sessionID })).location;
|
|
2180
|
-
} catch {
|
|
2181
|
-
return false;
|
|
2182
|
-
}
|
|
2183
|
-
return location?.directory === context.location.directory && (location.workspaceID ?? "") === (context.location.workspaceID ?? "");
|
|
2184
|
-
}
|
|
2185
|
-
function startDreamTrigger(context, args) {
|
|
2186
|
-
const controller = new AbortController;
|
|
2187
|
-
const done = (async () => {
|
|
2188
|
-
try {
|
|
2189
|
-
for await (const value of context.event.subscribe({ signal: controller.signal })) {
|
|
2190
|
-
if (controller.signal.aborted)
|
|
2191
|
-
break;
|
|
2192
|
-
const event = value;
|
|
2193
|
-
if (event.type !== "session.execution.succeeded" || !event.data?.sessionID)
|
|
2194
|
-
continue;
|
|
2195
|
-
if (!await sessionInThisLocation(context, event.data.sessionID))
|
|
2196
|
-
continue;
|
|
2197
|
-
const db = openCurrentDatabase();
|
|
2198
|
-
if (!db)
|
|
2199
|
-
continue;
|
|
2200
|
-
try {
|
|
2201
|
-
const reader = args.openReader?.() ?? new V2StoreReader(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"));
|
|
2202
|
-
try {
|
|
2203
|
-
const activity = reader.rootSessionActivity([event.data.sessionID]).get(event.data.sessionID);
|
|
2204
|
-
if (activity !== undefined)
|
|
2205
|
-
advanceSessionActivity(db, event.data.sessionID, activity);
|
|
2206
|
-
} finally {
|
|
2207
|
-
reader.close();
|
|
2208
|
-
}
|
|
2209
|
-
const sampled = args.sample?.();
|
|
2210
|
-
if ((sampled?.config ?? args.config).disable === true)
|
|
2211
|
-
continue;
|
|
2212
|
-
const { runnable } = selectRunnableDreamTasks({
|
|
2213
|
-
tasks: buildDreamTaskRuntimeConfigs2(sampled?.config ?? args.config, "opencode", args.language, (sampled?.mural ?? args.mural)?.model),
|
|
2214
|
-
toolsSupported: args.executor.capabilities.tools === true
|
|
2215
|
-
});
|
|
2216
|
-
await runDueTasksForProject({
|
|
2217
|
-
db,
|
|
2218
|
-
projectIdentity: args.projectIdentity(),
|
|
2219
|
-
projectMemoryEnabled: args.projectMemoryEnabled,
|
|
2220
|
-
tasks: runnable,
|
|
2221
|
-
executor: createDreamTaskExecutor({
|
|
2222
|
-
hiddenCompletionExecutor: args.executor,
|
|
2223
|
-
parentSessionId: event.data.sessionID,
|
|
2224
|
-
sessionDirectory: context.location.directory,
|
|
2225
|
-
openOpenCodeDb: () => null,
|
|
2226
|
-
retrospectiveRawProvider: (db) => new V2RetrospectiveRawProvider(db),
|
|
2227
|
-
language: args.language,
|
|
2228
|
-
mural: sampled?.mural ?? args.mural
|
|
2229
|
-
})
|
|
2230
|
-
});
|
|
2231
|
-
} catch (error) {
|
|
2232
|
-
log("[magic-context] v2 dream scheduling failed", error);
|
|
3045
|
+
{
|
|
3046
|
+
name: "ctx-wrapup",
|
|
3047
|
+
execute: async (input) => {
|
|
3048
|
+
const argument = commandArgument(input, "ctx-wrapup");
|
|
3049
|
+
if (argument !== "" && !/^\d+$/.test(argument)) {
|
|
3050
|
+
const usage = "Usage: /ctx-wrapup [messages_to_keep] where messages_to_keep is a positive integer";
|
|
3051
|
+
reportUsage(input.sessionID, usage);
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
const messagesToKeep = argument === "" ? 20 : Number.parseInt(argument, 10);
|
|
3055
|
+
if (messagesToKeep <= 0) {
|
|
3056
|
+
const usage = "messages_to_keep must be a positive integer";
|
|
3057
|
+
reportUsage(input.sessionID, usage);
|
|
3058
|
+
return;
|
|
2233
3059
|
}
|
|
3060
|
+
const reply = await rpc.dispatch("wrapup", {
|
|
3061
|
+
sessionId: input.sessionID,
|
|
3062
|
+
messagesToKeep
|
|
3063
|
+
});
|
|
3064
|
+
reportRpcReply(input.sessionID, "Wrapup", reply, "Wrapup started; the summary appears when it finishes");
|
|
2234
3065
|
}
|
|
2235
|
-
} catch (error) {
|
|
2236
|
-
if (!controller.signal.aborted)
|
|
2237
|
-
log("[magic-context] v2 dream event subscription failed", error);
|
|
2238
|
-
}
|
|
2239
|
-
})();
|
|
2240
|
-
return {
|
|
2241
|
-
async dispose() {
|
|
2242
|
-
controller.abort();
|
|
2243
|
-
await done;
|
|
2244
3066
|
}
|
|
2245
|
-
|
|
3067
|
+
];
|
|
2246
3068
|
}
|
|
2247
3069
|
|
|
2248
|
-
// src/v2/
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
}
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
}).filter(Boolean).join(`
|
|
2278
|
-
`);
|
|
3070
|
+
// src/v2/hooks/deleted-session-tombstones.ts
|
|
3071
|
+
class DeletedSessionTombstones {
|
|
3072
|
+
limit;
|
|
3073
|
+
#ids = new Set;
|
|
3074
|
+
constructor(limit = 1000) {
|
|
3075
|
+
this.limit = limit;
|
|
3076
|
+
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
3077
|
+
throw new Error("Deleted-session tombstone limit must be a positive integer");
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
add(sessionID) {
|
|
3081
|
+
this.#ids.delete(sessionID);
|
|
3082
|
+
this.#ids.add(sessionID);
|
|
3083
|
+
while (this.#ids.size > this.limit) {
|
|
3084
|
+
const oldest = this.#ids.values().next().value;
|
|
3085
|
+
if (oldest === undefined)
|
|
3086
|
+
break;
|
|
3087
|
+
this.#ids.delete(oldest);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
has(sessionID) {
|
|
3091
|
+
return this.#ids.has(sessionID);
|
|
3092
|
+
}
|
|
3093
|
+
clear() {
|
|
3094
|
+
this.#ids.clear();
|
|
3095
|
+
}
|
|
3096
|
+
get size() {
|
|
3097
|
+
return this.#ids.size;
|
|
3098
|
+
}
|
|
2279
3099
|
}
|
|
2280
3100
|
|
|
2281
|
-
// src/v2/
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
"
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
3101
|
+
// src/v2/retrospective-raw-provider.ts
|
|
3102
|
+
class V2RetrospectiveRawProvider {
|
|
3103
|
+
contextDb;
|
|
3104
|
+
openReader;
|
|
3105
|
+
reader;
|
|
3106
|
+
constructor(contextDb, openReader = () => new V2StoreReader(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"))) {
|
|
3107
|
+
this.contextDb = contextDb;
|
|
3108
|
+
this.openReader = openReader;
|
|
3109
|
+
}
|
|
3110
|
+
store() {
|
|
3111
|
+
return this.reader ??= this.openReader();
|
|
3112
|
+
}
|
|
3113
|
+
listProjectSessions(projectIdentity) {
|
|
3114
|
+
const sessions = this.contextDb.prepare(`
|
|
3115
|
+
SELECT sp.session_id FROM session_projects sp
|
|
3116
|
+
LEFT JOIN session_meta m ON m.session_id = sp.session_id
|
|
3117
|
+
WHERE sp.project_path = ? AND sp.harness = 'opencode2'
|
|
3118
|
+
AND COALESCE(m.is_subagent, 0) = 0
|
|
3119
|
+
ORDER BY sp.session_id`).all(projectIdentity);
|
|
3120
|
+
const roots = this.store().rootSessionActivity(sessions.map((row) => row.session_id));
|
|
3121
|
+
return sessions.filter((row) => roots.has(row.session_id)).map((row) => ({ sessionId: row.session_id, updatedAt: roots.get(row.session_id) }));
|
|
3122
|
+
}
|
|
3123
|
+
readOldestMessageTimesSince(sessionIds, sinceMs) {
|
|
3124
|
+
return this.store().oldestUserMessageTimesSince(sessionIds, sinceMs);
|
|
3125
|
+
}
|
|
3126
|
+
read(sessionId, boundaryMs, limit, before = false) {
|
|
3127
|
+
return this.store().retrospectiveUserPage(sessionId, {
|
|
3128
|
+
boundaryMs,
|
|
3129
|
+
before,
|
|
3130
|
+
limit,
|
|
3131
|
+
maxChars: RETROSPECTIVE_MAX_USER_MESSAGE_CHARS,
|
|
3132
|
+
truncationMarker: RETROSPECTIVE_MESSAGE_TRUNCATION_MARKER
|
|
3133
|
+
}).map((row) => ({
|
|
3134
|
+
sessionId,
|
|
3135
|
+
ordinal: row.seq,
|
|
3136
|
+
role: "user",
|
|
3137
|
+
text: cleanUserText2(row.text),
|
|
3138
|
+
ts: row.time_created
|
|
3139
|
+
}));
|
|
3140
|
+
}
|
|
3141
|
+
readUserMessagesSince(sessionId, sinceMs, capPerSession) {
|
|
3142
|
+
const cap = Math.max(1, Math.min(9999, Math.floor(capPerSession)));
|
|
3143
|
+
const rows = this.read(sessionId, sinceMs, cap + 1);
|
|
3144
|
+
return { messages: rows.slice(0, cap), truncated: rows.length > cap };
|
|
3145
|
+
}
|
|
3146
|
+
readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
3147
|
+
return this.read(sessionId, beforeMs, Math.max(1, Math.min(1e4, Math.floor(count))), true).reverse().filter((row) => hasMeaningfulUserText2([{ type: "text", text: row.text }]));
|
|
3148
|
+
}
|
|
3149
|
+
dispose() {
|
|
3150
|
+
this.reader?.close();
|
|
3151
|
+
this.reader = undefined;
|
|
3152
|
+
}
|
|
2314
3153
|
}
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
3154
|
+
|
|
3155
|
+
// src/v2/hooks/dream-manual.ts
|
|
3156
|
+
function resolveManualDreamTask(raw) {
|
|
3157
|
+
const requested = typeof raw === "string" ? raw.trim() : "";
|
|
3158
|
+
if (!requested)
|
|
3159
|
+
return {};
|
|
3160
|
+
if (!isCanonicalDreamTask(requested)) {
|
|
3161
|
+
return {
|
|
3162
|
+
error: `Unknown task "${requested}". Valid tasks: ${CANONICAL_DREAM_TASKS.join(", ")}.`
|
|
3163
|
+
};
|
|
2325
3164
|
}
|
|
2326
|
-
return
|
|
3165
|
+
return { task: requested };
|
|
2327
3166
|
}
|
|
2328
|
-
function
|
|
2329
|
-
const
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
if (Array.isArray(entry))
|
|
2336
|
-
return entry.map(plain);
|
|
2337
|
-
if (entry && typeof entry === "object") {
|
|
2338
|
-
return Object.fromEntries(Object.entries(entry).map(([key, nested]) => [key, plain(nested)]));
|
|
3167
|
+
function selectRunnableDreamTasks(args) {
|
|
3168
|
+
const requiresTools = (task) => DREAM_TASK_CAPABILITIES[task].requiresTools;
|
|
3169
|
+
if (args.toolsSupported)
|
|
3170
|
+
return { runnable: [...args.tasks], unsupported: [] };
|
|
3171
|
+
if (args.requestedTask !== undefined) {
|
|
3172
|
+
if (requiresTools(args.requestedTask)) {
|
|
3173
|
+
return { runnable: [], unsupported: [args.requestedTask] };
|
|
2339
3174
|
}
|
|
2340
|
-
return
|
|
3175
|
+
return {
|
|
3176
|
+
runnable: args.tasks.filter((config) => config.task === args.requestedTask),
|
|
3177
|
+
unsupported: []
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3180
|
+
const unsupported = args.tasks.filter((config) => config.schedule.trim() !== "" && requiresTools(config.task)).map((config) => config.task);
|
|
3181
|
+
return {
|
|
3182
|
+
runnable: args.tasks.filter((config) => !requiresTools(config.task)),
|
|
3183
|
+
unsupported
|
|
2341
3184
|
};
|
|
2342
|
-
return JSON.stringify(plain(value));
|
|
2343
|
-
}
|
|
2344
|
-
function fileContentValue(result) {
|
|
2345
|
-
const value = result?.value;
|
|
2346
|
-
if (result?.type !== "content" || !Array.isArray(value))
|
|
2347
|
-
return;
|
|
2348
|
-
return value.some((item) => item?.type === "file") ? value : undefined;
|
|
2349
|
-
}
|
|
2350
|
-
function contentValueText(value) {
|
|
2351
|
-
return value.filter((item) => item?.type === "text" && typeof item.text === "string").map((item) => item.text).join(`
|
|
2352
|
-
`);
|
|
2353
3185
|
}
|
|
2354
|
-
function
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
3186
|
+
async function runManualDreamNow(args) {
|
|
3187
|
+
const tasks = buildDreamTaskRuntimeConfigs2(args.dreamer, "opencode", args.language, args.smartNotesEnabled);
|
|
3188
|
+
const executor = createDreamTaskExecutor({
|
|
3189
|
+
hiddenCompletionExecutor: args.executor,
|
|
3190
|
+
parentSessionId: args.sessionId,
|
|
3191
|
+
sessionDirectory: args.directory,
|
|
3192
|
+
openOpenCodeDb: () => null,
|
|
3193
|
+
retrospectiveRawProvider: (db) => new V2RetrospectiveRawProvider(db),
|
|
3194
|
+
language: args.language,
|
|
3195
|
+
mural: args.mural,
|
|
3196
|
+
smartNotesEnabled: args.smartNotesEnabled
|
|
3197
|
+
});
|
|
3198
|
+
const selection = selectRunnableDreamTasks({
|
|
3199
|
+
tasks,
|
|
3200
|
+
toolsSupported: args.executor.capabilities.tools === true,
|
|
3201
|
+
...args.task !== undefined ? { requestedTask: args.task } : {}
|
|
3202
|
+
});
|
|
3203
|
+
const summary = await runManualDream({
|
|
3204
|
+
db: args.db,
|
|
3205
|
+
projectIdentity: args.projectIdentity,
|
|
3206
|
+
tasks: selection.runnable,
|
|
3207
|
+
executor,
|
|
3208
|
+
...args.task !== undefined ? { task: args.task } : {}
|
|
3209
|
+
});
|
|
3210
|
+
return { summary, unsupportedTasks: selection.unsupported };
|
|
2361
3211
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
3212
|
+
|
|
3213
|
+
// src/v2/hooks/dream-timer.ts
|
|
3214
|
+
function findV2DreamParentSession(openReader, directory) {
|
|
3215
|
+
let reader;
|
|
3216
|
+
try {
|
|
3217
|
+
reader = openReader();
|
|
3218
|
+
const parent = reader.latestRootSessionInDirectory(directory);
|
|
3219
|
+
if (!parent) {
|
|
3220
|
+
log2(`[dreamer] no OpenCode 2 session in ${directory} to parent a timer-started dream run; tasks that need a child session are skipped until one exists`);
|
|
2371
3221
|
}
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
3222
|
+
return parent;
|
|
3223
|
+
} catch (error) {
|
|
3224
|
+
log2(`[dreamer] OpenCode 2 parent session lookup failed for ${directory}:`, error);
|
|
3225
|
+
return;
|
|
3226
|
+
} finally {
|
|
3227
|
+
reader?.close();
|
|
2376
3228
|
}
|
|
2377
|
-
if (!placed && output)
|
|
2378
|
-
rebuilt.unshift({ type: "text", text: output });
|
|
2379
|
-
return { type: "content", value: rebuilt };
|
|
2380
3229
|
}
|
|
2381
|
-
function
|
|
2382
|
-
const
|
|
2383
|
-
const
|
|
2384
|
-
|
|
2385
|
-
const
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
...result && (call?.name ?? result.part.name) === "question" ? { metadata: { userAnswer: true } } : {},
|
|
2424
|
-
...files ? { attachments: contentValueAttachments(files) } : {}
|
|
2425
|
-
}
|
|
2426
|
-
};
|
|
2427
|
-
bridges.set(part, {
|
|
2428
|
-
call,
|
|
2429
|
-
result: result?.part,
|
|
2430
|
-
resultMessage: result?.message,
|
|
2431
|
-
output,
|
|
2432
|
-
files
|
|
2433
|
-
});
|
|
2434
|
-
if (result)
|
|
2435
|
-
paired.add(result.part);
|
|
2436
|
-
return part;
|
|
2437
|
-
};
|
|
2438
|
-
const convertedTool = (source) => {
|
|
2439
|
-
const sourceState = source.state && typeof source.state === "object" ? source.state : {};
|
|
2440
|
-
const output = toolStateContent(sourceState);
|
|
2441
|
-
const { content: _content, ...projectedState } = structuredClone(sourceState);
|
|
2442
|
-
const part = {
|
|
2443
|
-
type: "tool",
|
|
2444
|
-
callID: source.callID ?? source.id,
|
|
2445
|
-
tool: source.tool ?? source.name,
|
|
2446
|
-
state: { ...projectedState, output }
|
|
2447
|
-
};
|
|
2448
|
-
bridges.set(part, { native: source, output });
|
|
2449
|
-
return part;
|
|
2450
|
-
};
|
|
2451
|
-
const callParts = new Map;
|
|
2452
|
-
for (const message of source) {
|
|
2453
|
-
for (const part of message.content) {
|
|
2454
|
-
if (part.type === "tool-call") {
|
|
2455
|
-
callParts.set(part, nativeTool(part, results.get(String(part.id))?.shift()));
|
|
2456
|
-
} else if (part.type === "tool") {
|
|
2457
|
-
callParts.set(part, convertedTool(part));
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
const messages = [];
|
|
2462
|
-
for (const message of source) {
|
|
2463
|
-
const parts = [];
|
|
2464
|
-
for (const content of message.content) {
|
|
2465
|
-
if (paired.has(content))
|
|
2466
|
-
continue;
|
|
2467
|
-
const part = callParts.get(content) ?? (content.type === "tool-result" ? nativeTool(undefined, { part: content, message }) : clonePreservingInstances(content));
|
|
2468
|
-
if (message.id && admittedIDs.has(message.id))
|
|
2469
|
-
part.synthetic = true;
|
|
2470
|
-
parts.push(part);
|
|
2471
|
-
}
|
|
2472
|
-
if (!parts.length && message.content.length)
|
|
2473
|
-
continue;
|
|
2474
|
-
const byCallID = new Map;
|
|
2475
|
-
for (const part of parts) {
|
|
2476
|
-
const bridge = bridges.get(part);
|
|
2477
|
-
if (bridge && typeof part.callID === "string")
|
|
2478
|
-
byCallID.set(part.callID, bridge);
|
|
2479
|
-
}
|
|
2480
|
-
if (typeof message.id === "string") {
|
|
2481
|
-
bridgesByMessageID.set(message.id, byCallID);
|
|
2482
|
-
originalsByID.set(message.id, message);
|
|
3230
|
+
function registerV2DreamScheduleTimer(args) {
|
|
3231
|
+
const { config, directory, dreamer, executor } = args;
|
|
3232
|
+
const gitCommitIndexing = config.memory.git_commit_indexing;
|
|
3233
|
+
let disposed = false;
|
|
3234
|
+
const registration = startDreamScheduleTimer({
|
|
3235
|
+
directory,
|
|
3236
|
+
projectIdentity: args.projectIdentity,
|
|
3237
|
+
harness: "opencode",
|
|
3238
|
+
hiddenCompletionExecutor: executor,
|
|
3239
|
+
findParentSessionId: () => findV2DreamParentSession(args.openReader, directory),
|
|
3240
|
+
openOpenCodeDb: () => null,
|
|
3241
|
+
retrospectiveRawProvider: (db) => new V2RetrospectiveRawProvider(db),
|
|
3242
|
+
dreamerConfig: dreamer,
|
|
3243
|
+
sampleDreamRun: () => {
|
|
3244
|
+
const current = dreamerRunConfig(config, args.liveConfig());
|
|
3245
|
+
return { dreamerConfig: current.dreamer ?? dreamer, mural: current.mural };
|
|
3246
|
+
},
|
|
3247
|
+
validateTaskModels: (tasks) => selectRunnableDreamTasks({ tasks, toolsSupported: executor.capabilities.tools }).runnable,
|
|
3248
|
+
language: config.language,
|
|
3249
|
+
mural: config.mural,
|
|
3250
|
+
memoryEnabled: config.memory.enabled === true,
|
|
3251
|
+
memoryInjectionBudgetTokens: config.memory.injection_budget_tokens,
|
|
3252
|
+
retinaHandoff: config.smart_notes.retina_handoff,
|
|
3253
|
+
smartNotesEnabled: config.smart_notes.enabled,
|
|
3254
|
+
embeddingConfig: config.embedding,
|
|
3255
|
+
gitCommitIndexing: gitCommitIndexing?.enabled ? {
|
|
3256
|
+
enabled: true,
|
|
3257
|
+
since_days: gitCommitIndexing.since_days,
|
|
3258
|
+
max_commits: gitCommitIndexing.max_commits
|
|
3259
|
+
} : undefined,
|
|
3260
|
+
ensureRegistered: ensureProjectRegisteredFromOpenCodeDirectory
|
|
3261
|
+
}).catch((error) => {
|
|
3262
|
+
log2(`[magic-context] v2 dream timer registration failed (continuing without it): ${error}`);
|
|
3263
|
+
return;
|
|
3264
|
+
});
|
|
3265
|
+
return {
|
|
3266
|
+
async dispose() {
|
|
3267
|
+
if (disposed)
|
|
3268
|
+
return;
|
|
3269
|
+
disposed = true;
|
|
3270
|
+
const stop = await registration;
|
|
3271
|
+
stop?.();
|
|
2483
3272
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
},
|
|
2495
|
-
parts
|
|
2496
|
-
};
|
|
2497
|
-
originals.set(mapped, message);
|
|
2498
|
-
if (typeof message.id !== "string")
|
|
2499
|
-
bridgesByCarrier.set(mapped, byCallID);
|
|
2500
|
-
messages.push(mapped);
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
// src/v2/hooks/dream-trigger.ts
|
|
3277
|
+
async function sessionInThisLocation(context, sessionID) {
|
|
3278
|
+
let location;
|
|
3279
|
+
try {
|
|
3280
|
+
location = (await context.session.get({ sessionID })).location;
|
|
3281
|
+
} catch {
|
|
3282
|
+
return false;
|
|
2501
3283
|
}
|
|
2502
|
-
return
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
const
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
});
|
|
2528
|
-
if (state.status === "completed" || state.status === "error") {
|
|
2529
|
-
following.push({
|
|
2530
|
-
role: "tool",
|
|
2531
|
-
content: [
|
|
2532
|
-
{
|
|
2533
|
-
type: "tool-result",
|
|
2534
|
-
id: part.callID,
|
|
2535
|
-
name: part.tool,
|
|
2536
|
-
result: {
|
|
2537
|
-
type: state.status === "error" ? "error" : "text",
|
|
2538
|
-
value: toolStateContent(state)
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
]
|
|
2542
|
-
});
|
|
2543
|
-
}
|
|
2544
|
-
continue;
|
|
2545
|
-
}
|
|
2546
|
-
if (part.type === "file") {
|
|
2547
|
-
const mime = part.mime;
|
|
2548
|
-
const url = part.url;
|
|
2549
|
-
const prefix = `data:${mime};base64,`;
|
|
2550
|
-
if (typeof mime !== "string" || !/^image\/(png|jpeg|gif|webp)$/.test(mime) || typeof url !== "string" || !url.startsWith(prefix)) {
|
|
2551
|
-
rejectContentPart(message, part, "file is not an inline supported image");
|
|
2552
|
-
continue;
|
|
2553
|
-
}
|
|
2554
|
-
const asset = hostMediaAsset(url.slice(prefix.length), mime);
|
|
2555
|
-
if (typeof asset === "string") {
|
|
2556
|
-
rejectContentPart(message, part, `host media unavailable: ${asset}`);
|
|
2557
|
-
continue;
|
|
2558
|
-
}
|
|
2559
|
-
content.push({ type: "media", media: asset });
|
|
2560
|
-
continue;
|
|
2561
|
-
}
|
|
2562
|
-
if (!HOST_CONTENT_TYPES.has(String(part.type))) {
|
|
2563
|
-
rejectContentPart(message, part, "unknown host content type");
|
|
2564
|
-
continue;
|
|
2565
|
-
}
|
|
2566
|
-
const { synthetic: _synthetic, ...clean } = part;
|
|
2567
|
-
const original = hostPartTypes.has(clean.type) && !containsHostInstance(clean) ? hostParts.get(contentKey(clean)) : undefined;
|
|
2568
|
-
content.push(original ?? clean);
|
|
2569
|
-
continue;
|
|
3284
|
+
return location?.directory === context.location.directory && (location.workspaceID ?? "") === (context.location.workspaceID ?? "");
|
|
3285
|
+
}
|
|
3286
|
+
function startDreamTrigger(context, args) {
|
|
3287
|
+
const controller = new AbortController;
|
|
3288
|
+
const done = (async () => {
|
|
3289
|
+
try {
|
|
3290
|
+
for await (const value of context.event.subscribe({ signal: controller.signal })) {
|
|
3291
|
+
if (controller.signal.aborted)
|
|
3292
|
+
break;
|
|
3293
|
+
const event = value;
|
|
3294
|
+
if (event.type !== "session.execution.succeeded" || !event.data?.sessionID)
|
|
3295
|
+
continue;
|
|
3296
|
+
if (!await sessionInThisLocation(context, event.data.sessionID))
|
|
3297
|
+
continue;
|
|
3298
|
+
const db = openCurrentDatabase();
|
|
3299
|
+
if (!db)
|
|
3300
|
+
continue;
|
|
3301
|
+
try {
|
|
3302
|
+
const reader = args.openReader?.() ?? new V2StoreReader(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"));
|
|
3303
|
+
try {
|
|
3304
|
+
const activity = reader.rootSessionActivity([event.data.sessionID]).get(event.data.sessionID);
|
|
3305
|
+
if (activity !== undefined)
|
|
3306
|
+
advanceSessionActivity(db, event.data.sessionID, activity);
|
|
3307
|
+
} finally {
|
|
3308
|
+
reader.close();
|
|
2570
3309
|
}
|
|
2571
|
-
const
|
|
2572
|
-
if (
|
|
2573
|
-
const native = bridge.native;
|
|
2574
|
-
const callID = String(native.id ?? native.callID ?? part.callID);
|
|
2575
|
-
const name = String(native.name ?? native.tool ?? part.tool);
|
|
2576
|
-
content.push({ type: "tool-call", id: callID, name, input: state.input });
|
|
2577
|
-
if (state.status === "completed" || state.status === "error") {
|
|
2578
|
-
following.push({
|
|
2579
|
-
role: "tool",
|
|
2580
|
-
content: [
|
|
2581
|
-
{
|
|
2582
|
-
type: "tool-result",
|
|
2583
|
-
id: callID,
|
|
2584
|
-
name,
|
|
2585
|
-
result: {
|
|
2586
|
-
type: state.status === "error" ? "error" : "text",
|
|
2587
|
-
value: state.output
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
]
|
|
2591
|
-
});
|
|
2592
|
-
}
|
|
3310
|
+
const sampled = args.sample?.();
|
|
3311
|
+
if ((sampled?.config ?? args.config).disable === true)
|
|
2593
3312
|
continue;
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
content
|
|
2617
|
-
};
|
|
2618
|
-
const existing = id ? existingHeads.get(id) : undefined;
|
|
2619
|
-
rendered.push(existing ? Object.assign(existing, { content }) : value);
|
|
3313
|
+
const { runnable } = selectRunnableDreamTasks({
|
|
3314
|
+
tasks: buildDreamTaskRuntimeConfigs2(sampled?.config ?? args.config, "opencode", args.language, args.smartNotesEnabled),
|
|
3315
|
+
toolsSupported: args.executor.capabilities.tools === true
|
|
3316
|
+
});
|
|
3317
|
+
await runDueTasksForProject({
|
|
3318
|
+
db,
|
|
3319
|
+
projectIdentity: args.projectIdentity(),
|
|
3320
|
+
projectMemoryEnabled: args.projectMemoryEnabled,
|
|
3321
|
+
tasks: runnable,
|
|
3322
|
+
executor: createDreamTaskExecutor({
|
|
3323
|
+
hiddenCompletionExecutor: args.executor,
|
|
3324
|
+
parentSessionId: event.data.sessionID,
|
|
3325
|
+
sessionDirectory: context.location.directory,
|
|
3326
|
+
openOpenCodeDb: () => null,
|
|
3327
|
+
retrospectiveRawProvider: (db) => new V2RetrospectiveRawProvider(db),
|
|
3328
|
+
language: args.language,
|
|
3329
|
+
mural: sampled?.mural ?? args.mural,
|
|
3330
|
+
smartNotesEnabled: args.smartNotesEnabled
|
|
3331
|
+
})
|
|
3332
|
+
});
|
|
3333
|
+
} catch (error) {
|
|
3334
|
+
log2("[magic-context] v2 dream scheduling failed", error);
|
|
2620
3335
|
}
|
|
2621
|
-
rendered.push(...following);
|
|
2622
3336
|
}
|
|
2623
|
-
|
|
3337
|
+
} catch (error) {
|
|
3338
|
+
if (!controller.signal.aborted)
|
|
3339
|
+
log2("[magic-context] v2 dream event subscription failed", error);
|
|
3340
|
+
}
|
|
3341
|
+
})();
|
|
3342
|
+
return {
|
|
3343
|
+
async dispose() {
|
|
3344
|
+
controller.abort();
|
|
3345
|
+
await done;
|
|
2624
3346
|
}
|
|
2625
3347
|
};
|
|
2626
3348
|
}
|
|
@@ -2738,7 +3460,7 @@ async function warmModelLimitCacheFromCatalog(context, options = {}) {
|
|
|
2738
3460
|
retryDelayMs: options.retryDelayMs ?? 1000
|
|
2739
3461
|
});
|
|
2740
3462
|
} catch (error) {
|
|
2741
|
-
|
|
3463
|
+
sessionLog2("global", `v2 model-limit cache warm failed: ${getErrorMessage(error)}`);
|
|
2742
3464
|
} finally {
|
|
2743
3465
|
if (!modelLimitCacheWarm())
|
|
2744
3466
|
warmStarted = false;
|
|
@@ -2899,7 +3621,7 @@ function runV2SessionProjectBackfill(db, openStoreReader, allowHomeProject = fal
|
|
|
2899
3621
|
} finally {
|
|
2900
3622
|
reader.close();
|
|
2901
3623
|
}
|
|
2902
|
-
}, { leaseKey: `${
|
|
3624
|
+
}, { leaseKey: `${getHarness2()}:session-projects-creation-v2`, allowHomeProject });
|
|
2903
3625
|
}
|
|
2904
3626
|
|
|
2905
3627
|
// src/v2/hooks/storage-gate.ts
|
|
@@ -3223,6 +3945,7 @@ async function registerTools(context, db, config, rustToolBackends, isInternalCh
|
|
|
3223
3945
|
...createCtxNoteTools({
|
|
3224
3946
|
...project,
|
|
3225
3947
|
dreamerEnabled: isDreamerRunnable(config),
|
|
3948
|
+
smartNotesEnabled: config.smart_notes.enabled,
|
|
3226
3949
|
...rustToolBackends ? { rustToolBackends } : {}
|
|
3227
3950
|
}),
|
|
3228
3951
|
...createCtxSearchTools(project),
|
|
@@ -3250,7 +3973,7 @@ async function registerTools(context, db, config, rustToolBackends, isInternalCh
|
|
|
3250
3973
|
if (refusal)
|
|
3251
3974
|
return { content: refusal };
|
|
3252
3975
|
if (name === "ctx_memory" || name === "ctx_note" || name === "ctx_reduce")
|
|
3253
|
-
await
|
|
3976
|
+
await withAsyncPrivilegedWriter2(db, () => {
|
|
3254
3977
|
return;
|
|
3255
3978
|
});
|
|
3256
3979
|
const parsedInput = tool.schema.object(definition.args).passthrough().safeParse(input);
|
|
@@ -3316,7 +4039,7 @@ function persistV2UsageReading(args) {
|
|
|
3316
4039
|
const { db, sessionID, draftModel, reading } = args;
|
|
3317
4040
|
const currentMeta = getOrCreateSessionMeta(db, sessionID);
|
|
3318
4041
|
if (reading.completed !== undefined && currentMeta.lastResponseTime > 0 && reading.completed <= currentMeta.lastResponseTime) {
|
|
3319
|
-
|
|
4042
|
+
sessionLog2(sessionID, `v2 usage: skipped stale reading completed=${reading.completed} <= lastResponseTime=${currentMeta.lastResponseTime}`);
|
|
3320
4043
|
return;
|
|
3321
4044
|
}
|
|
3322
4045
|
const draftModelKey = `${draftModel.providerID}/${draftModel.id}`;
|
|
@@ -3343,7 +4066,7 @@ function persistV2UsageReading(args) {
|
|
|
3343
4066
|
if (responseTime)
|
|
3344
4067
|
updates.lastResponseTime = responseTime;
|
|
3345
4068
|
updateSessionMeta(db, sessionID, updates);
|
|
3346
|
-
|
|
4069
|
+
sessionLog2(sessionID, `v2 usage: inputTokens=${reading.inputTokens} contextLimit=${reading.limit} percentage=${percentage} responseModel=${reading.modelKey ?? "legacy"} draftContextLimit=${reading.admissionLimit} pressure=${readingMatchesDraft ? "current" : "stale-model-ignored"}`);
|
|
3347
4070
|
if (readingMatchesDraft) {
|
|
3348
4071
|
args.contextUsageMap.set(sessionID, {
|
|
3349
4072
|
usage: { inputTokens: reading.inputTokens, percentage },
|
|
@@ -3356,6 +4079,8 @@ function persistV2UsageReading(args) {
|
|
|
3356
4079
|
}
|
|
3357
4080
|
|
|
3358
4081
|
// src/v2/hooks/context.ts
|
|
4082
|
+
class V2LkgAdmissionReplay extends Error {
|
|
4083
|
+
}
|
|
3359
4084
|
var HIDDEN_SESSION_ERROR_GRACE_MS = 50;
|
|
3360
4085
|
function isBlockingV2TransformError(error) {
|
|
3361
4086
|
return error instanceof EmergencyFailClosedError || error instanceof UnresolvedHistoryBoundaryError || error instanceof UnmanagedOverWindowError || error instanceof DegradedPassRefusalError || isFailClosedBlockingError(error);
|
|
@@ -3369,7 +4094,7 @@ function checkHistoryBoundary(db, reader, sessionID) {
|
|
|
3369
4094
|
isInHostStore: (messageId) => storeHasSession ? reader.sequenceForId(sessionID, messageId) !== undefined : null
|
|
3370
4095
|
});
|
|
3371
4096
|
} catch (error) {
|
|
3372
|
-
|
|
4097
|
+
sessionLog2(sessionID, "history boundary check failed; continuing without repair", error);
|
|
3373
4098
|
return;
|
|
3374
4099
|
}
|
|
3375
4100
|
}
|
|
@@ -3382,18 +4107,19 @@ async function cacheV2SessionDirectory(session, sessionID, directories) {
|
|
|
3382
4107
|
directories.set(sessionID, directory);
|
|
3383
4108
|
}
|
|
3384
4109
|
} catch (error) {
|
|
3385
|
-
|
|
4110
|
+
sessionLog2(sessionID, "v2 session directory lookup failed; using the launch directory:", error);
|
|
3386
4111
|
}
|
|
3387
4112
|
}
|
|
3388
4113
|
function refuseBeforeProvider(session, sessionID, arm, cause) {
|
|
3389
4114
|
const detail = cause instanceof Error ? `${cause.name}: ${cause.message}` : cause === undefined ? "" : String(cause);
|
|
3390
|
-
|
|
4115
|
+
sessionLog2(sessionID, `v2 refusal: interrupting the turn before the provider request arm=${arm}${detail ? ` cause=${JSON.stringify(detail.slice(0, 500))}` : ""}`);
|
|
3391
4116
|
return interruptBeforeProvider(session, sessionID);
|
|
3392
4117
|
}
|
|
3393
4118
|
function createHostSeams(context, readAllForConversion, reconciliationSource, liveModels) {
|
|
3394
4119
|
return {
|
|
3395
4120
|
hostRawMessages: readAllForConversion,
|
|
3396
4121
|
hostMessageReconciliationSource: reconciliationSource,
|
|
4122
|
+
hostPrepareRawOrdinals: async () => true,
|
|
3397
4123
|
hostProtectedTailBoundary: (args) => resolveOpenCodeProtectedTailBoundary({
|
|
3398
4124
|
...args,
|
|
3399
4125
|
cacheNamespace: `opencode2:${args.sessionId}`
|
|
@@ -3449,7 +4175,7 @@ function toolResultText2(result) {
|
|
|
3449
4175
|
`);
|
|
3450
4176
|
}
|
|
3451
4177
|
function reportPreProviderRefusal(sessionID, error) {
|
|
3452
|
-
|
|
4178
|
+
sessionLog2(sessionID, `v2 refusing this turn before the model call: the context could not be read: ${getErrorMessage(error)}`);
|
|
3453
4179
|
}
|
|
3454
4180
|
function catalogModels(listed, draftModel) {
|
|
3455
4181
|
const rows = Array.isArray(listed) ? listed : listed && typeof listed === "object" && Array.isArray(listed.data) ? listed.data : [];
|
|
@@ -3487,7 +4213,7 @@ function removeDreamerOnlyTools(draft) {
|
|
|
3487
4213
|
if (draft.tools)
|
|
3488
4214
|
delete draft.tools.ctx_memory_list;
|
|
3489
4215
|
}
|
|
3490
|
-
function applyV2PromptSurfaceTools(draft, runtime, config) {
|
|
4216
|
+
function applyV2PromptSurfaceTools(draft, runtime, config, smartNotesEnabled = true) {
|
|
3491
4217
|
if (!draft.tools)
|
|
3492
4218
|
return;
|
|
3493
4219
|
const modelKey = `${draft.model.providerID}/${draft.model.id}`;
|
|
@@ -3496,8 +4222,8 @@ function applyV2PromptSurfaceTools(draft, runtime, config) {
|
|
|
3496
4222
|
const tool = draft.tools[id];
|
|
3497
4223
|
if (!tool)
|
|
3498
4224
|
continue;
|
|
3499
|
-
tool.description = registration.descriptionFor(id, tool.description);
|
|
3500
|
-
applyJsonSchemaParameterDescriptions(id, tool.input, registration.preset);
|
|
4225
|
+
tool.description = id === "ctx_note" && !smartNotesEnabled ? withoutSmartNoteText(registration.preset === "light" ? CTX_NOTE_LIGHT_DESCRIPTION : CTX_NOTE_DESCRIPTION) : registration.descriptionFor(id, tool.description);
|
|
4226
|
+
applyJsonSchemaParameterDescriptions(id, tool.input, registration.preset, smartNotesEnabled);
|
|
3501
4227
|
}
|
|
3502
4228
|
}
|
|
3503
4229
|
function recordV2ToolDefinitions(draft) {
|
|
@@ -3539,13 +4265,13 @@ async function registerContext(context) {
|
|
|
3539
4265
|
if (!hasRequiredSessionAPI(context.session) || !nativeRemove) {
|
|
3540
4266
|
const message = OPENCODE2_SESSION_API_NOTICE;
|
|
3541
4267
|
console.warn(`[magic-context] v2 host API unavailable: ${message}`);
|
|
3542
|
-
|
|
4268
|
+
log2(`[magic-context] ${message}`);
|
|
3543
4269
|
let noticed = false;
|
|
3544
4270
|
const refuseUnsupportedHost = async (draft) => {
|
|
3545
4271
|
if (!noticed) {
|
|
3546
4272
|
noticed = true;
|
|
3547
4273
|
pushNotification2("toast", { message, variant: "error" }, draft.sessionID);
|
|
3548
|
-
context.session.wait({ sessionID: draft.sessionID }).then(() => deliverSynthetic(context, draft.sessionID, message)).catch((error) =>
|
|
4274
|
+
context.session.wait({ sessionID: draft.sessionID }).then(() => deliverSynthetic(context, draft.sessionID, message)).catch((error) => sessionLog2(draft.sessionID, "host API notice could not be delivered:", error));
|
|
3549
4275
|
}
|
|
3550
4276
|
await refuseBeforeProvider(context.session, draft.sessionID, "unsupported-session-api");
|
|
3551
4277
|
};
|
|
@@ -3557,7 +4283,6 @@ async function registerContext(context) {
|
|
|
3557
4283
|
return;
|
|
3558
4284
|
}
|
|
3559
4285
|
const liveConfigReader = pluginConfigReader(directory, config);
|
|
3560
|
-
const checkoutClaim = createSubcCheckoutClaimGate("opencode", () => config.subc?.connection_file ?? getDefaultSubcConnectionFile());
|
|
3561
4286
|
const compactionOff = !isCompactionEnabled(config);
|
|
3562
4287
|
const conflicts = detectConflicts(directory, {
|
|
3563
4288
|
compactionEnabled: !compactionOff,
|
|
@@ -3568,6 +4293,8 @@ async function registerContext(context) {
|
|
|
3568
4293
|
return;
|
|
3569
4294
|
}
|
|
3570
4295
|
const folds = new FoldOwner(context.storage);
|
|
4296
|
+
const nativeFoldMemory = !compactionOff && process.env.MC_OC2_INVISIBLE_FOLD === "1" ? nativeFoldCache(() => storage.require()) : undefined;
|
|
4297
|
+
const nativeFoldReplay = nativeFoldMemory ? new NativeFoldReplay(nativeFoldMemory, () => openStoreReader()) : undefined;
|
|
3571
4298
|
setOutputReserveConfig(config.output_reserve);
|
|
3572
4299
|
const queriedModels = new Set;
|
|
3573
4300
|
const rawLimits = new Map;
|
|
@@ -3576,13 +4303,13 @@ async function registerContext(context) {
|
|
|
3576
4303
|
const promptSurfaceRuntime = createPromptSurfaceRuntime({
|
|
3577
4304
|
harness: "opencode2",
|
|
3578
4305
|
directory,
|
|
3579
|
-
warn: (message) =>
|
|
4306
|
+
warn: (message) => log2(`[magic-context] config warning: ${message}`)
|
|
3580
4307
|
});
|
|
3581
4308
|
const storage = createV2StorageGate({
|
|
3582
4309
|
onUnavailable: (reason) => {
|
|
3583
4310
|
const message = formatFailClosedBlockingMessage(reason);
|
|
3584
4311
|
console.warn(`[magic-context] v2 storage unavailable: ${message}`);
|
|
3585
|
-
|
|
4312
|
+
log2(`[magic-context] v2 storage unavailable: ${message}`);
|
|
3586
4313
|
}
|
|
3587
4314
|
});
|
|
3588
4315
|
let db = await probeV2StorageAtBoot(storage);
|
|
@@ -3592,14 +4319,14 @@ async function registerContext(context) {
|
|
|
3592
4319
|
if (legacyCleanupStarted)
|
|
3593
4320
|
return;
|
|
3594
4321
|
legacyCleanupStarted = true;
|
|
3595
|
-
await cleanupLegacyHiddenChildren(database, nativeRemove,
|
|
4322
|
+
await cleanupLegacyHiddenChildren(database, nativeRemove, log2);
|
|
3596
4323
|
};
|
|
3597
4324
|
if (db && isDatabasePersisted(db))
|
|
3598
4325
|
await cleanupLegacyOnce(db);
|
|
3599
4326
|
let storageRecoveryAnnounced = false;
|
|
3600
4327
|
const storageNoticeBySession = new Map;
|
|
3601
4328
|
const storeStorageNotice = (sessionID, text, what) => {
|
|
3602
|
-
|
|
4329
|
+
withoutSqliteTransformPass2(() => Promise.resolve().then(() => context.session.wait({ sessionID })).then(() => deliverSynthetic(context, sessionID, text)).catch((error) => sessionLog2(sessionID, `v2 storage ${what} notice could not be delivered:`, error)));
|
|
3603
4330
|
};
|
|
3604
4331
|
const noticeStorageRefusal = (sessionID, reason) => {
|
|
3605
4332
|
const message = formatStorageRefusalNotice(reason);
|
|
@@ -3651,7 +4378,7 @@ async function registerContext(context) {
|
|
|
3651
4378
|
const refusal = hiddenToolCallRefusal(draft, hiddenChildHook);
|
|
3652
4379
|
if (refusal === undefined)
|
|
3653
4380
|
return;
|
|
3654
|
-
|
|
4381
|
+
sessionLog2(draft.sessionID, "hidden child tool call refused", {
|
|
3655
4382
|
tool: draft.tool,
|
|
3656
4383
|
agent: draft.agent ?? null
|
|
3657
4384
|
});
|
|
@@ -3704,6 +4431,7 @@ async function registerContext(context) {
|
|
|
3704
4431
|
const cappedExecutor = withLiveDreamerOutputCap(executor, config, () => liveConfigReader.poll().effective);
|
|
3705
4432
|
const trigger = startDreamTrigger(context, {
|
|
3706
4433
|
config: dreamerAtBoot,
|
|
4434
|
+
smartNotesEnabled: config.smart_notes.enabled,
|
|
3707
4435
|
sample: () => {
|
|
3708
4436
|
const current = dreamerRunConfig(config, liveConfigReader.poll().effective);
|
|
3709
4437
|
return { config: current.dreamer ?? dreamerAtBoot, mural: current.mural };
|
|
@@ -3739,7 +4467,7 @@ async function registerContext(context) {
|
|
|
3739
4467
|
dreamTrigger ??= startDreamer(hiddenCompletionExecutor);
|
|
3740
4468
|
return true;
|
|
3741
4469
|
} catch (error) {
|
|
3742
|
-
|
|
4470
|
+
log2("[magic-context] v2 historian and dreamer could not start after recovery:", error);
|
|
3743
4471
|
return false;
|
|
3744
4472
|
}
|
|
3745
4473
|
})();
|
|
@@ -3798,7 +4526,7 @@ async function registerContext(context) {
|
|
|
3798
4526
|
const baseline = channel1.get(draft.sessionID);
|
|
3799
4527
|
await deliverPendingChannel2(context, db, draft.sessionID, baseline);
|
|
3800
4528
|
} catch (error) {
|
|
3801
|
-
|
|
4529
|
+
log2("[magic-context] v2 Channel 2 delivery deferred", error);
|
|
3802
4530
|
}
|
|
3803
4531
|
});
|
|
3804
4532
|
const pagedRead = createV2RawMessageReader(openStoreReader);
|
|
@@ -3816,7 +4544,7 @@ async function registerContext(context) {
|
|
|
3816
4544
|
} finally {
|
|
3817
4545
|
reader.close();
|
|
3818
4546
|
}
|
|
3819
|
-
})).catch((error) =>
|
|
4547
|
+
})).catch((error) => log2("[session-project-backfill] OpenCode 2 backfill failed:", error));
|
|
3820
4548
|
});
|
|
3821
4549
|
}
|
|
3822
4550
|
const readAllForConversion = (sessionID) => readAllV2RawMessagesForConversion(openStoreReader, sessionID);
|
|
@@ -3861,17 +4589,17 @@ async function registerContext(context) {
|
|
|
3861
4589
|
forkSeedSettled.add(sessionID);
|
|
3862
4590
|
if (outcome.kind === "seeded") {
|
|
3863
4591
|
const { result } = outcome;
|
|
3864
|
-
|
|
4592
|
+
sessionLog2(sessionID, `v2 fork inheritance: seeded from ${outcome.parentSessionID} through the fork boundary; paired rows=${outcome.pairedRows} compartments=${result.compartmentsCopied} tags=${result.tagsCopied} pending_ops=${result.pendingOpsCopied} facts=${result.factsCopied} notes=${result.notesCopied}`);
|
|
3865
4593
|
} else if (outcome.kind === "parent-missing") {
|
|
3866
|
-
|
|
4594
|
+
sessionLog2(sessionID, `v2 fork inheritance: fork of ${outcome.parentSessionID} starts without inherited state (${outcome.reason})`);
|
|
3867
4595
|
} else if (outcome.kind === "destination-not-empty") {
|
|
3868
|
-
|
|
4596
|
+
sessionLog2(sessionID, `v2 fork inheritance: fork of ${outcome.parentSessionID} already has state; not copied again`);
|
|
3869
4597
|
}
|
|
3870
4598
|
} catch (error) {
|
|
3871
|
-
if (
|
|
4599
|
+
if (isTransientSqliteError2(error))
|
|
3872
4600
|
throw error;
|
|
3873
4601
|
forkSeedSettled.add(sessionID);
|
|
3874
|
-
|
|
4602
|
+
sessionLog2(sessionID, "v2 fork inheritance failed; continuing without it:", error);
|
|
3875
4603
|
} finally {
|
|
3876
4604
|
reader?.close();
|
|
3877
4605
|
}
|
|
@@ -3957,6 +4685,14 @@ async function registerContext(context) {
|
|
|
3957
4685
|
}
|
|
3958
4686
|
return unsafe;
|
|
3959
4687
|
};
|
|
4688
|
+
const hostFolds = nativeFoldReplay && nativeFoldMemory ? new HostFoldPolicy({
|
|
4689
|
+
session: context.session,
|
|
4690
|
+
storage: nativeFoldMemory,
|
|
4691
|
+
openReader: openStoreReader,
|
|
4692
|
+
canReplay: (sessionID, reader) => nativeFoldReplay.canReplay(sessionID, reader),
|
|
4693
|
+
threshold: Number(process.env.MC_OC2_HOST_FOLD_ROWS ?? 1000),
|
|
4694
|
+
log: sessionLog2
|
|
4695
|
+
}) : undefined;
|
|
3960
4696
|
const usageController = new AbortController;
|
|
3961
4697
|
const usageDone = (async () => {
|
|
3962
4698
|
try {
|
|
@@ -3967,10 +4703,28 @@ async function registerContext(context) {
|
|
|
3967
4703
|
if (!event.data?.sessionID)
|
|
3968
4704
|
continue;
|
|
3969
4705
|
const sessionID = event.data.sessionID;
|
|
4706
|
+
if (event.type?.startsWith("session.revert.") || event.type === "session.message.content.updated") {
|
|
4707
|
+
nativeFoldReplay?.onEvent(event);
|
|
4708
|
+
restoredRows.forget(sessionID);
|
|
4709
|
+
}
|
|
3970
4710
|
if (event.type === "session.error" || event.type === "session.execution.failed") {
|
|
3971
4711
|
const error = hiddenTerminalError(event);
|
|
3972
4712
|
if (error !== undefined)
|
|
3973
4713
|
hiddenSessionErrors.set(sessionID, error);
|
|
4714
|
+
if (error !== undefined && db && !compactionOff) {
|
|
4715
|
+
const model = liveModels.get(sessionID);
|
|
4716
|
+
try {
|
|
4717
|
+
armLatestThinkingRecoveryFromError({
|
|
4718
|
+
db,
|
|
4719
|
+
sessionId: sessionID,
|
|
4720
|
+
error,
|
|
4721
|
+
providerID: model?.providerID,
|
|
4722
|
+
modelID: model?.modelID
|
|
4723
|
+
});
|
|
4724
|
+
} catch (armError) {
|
|
4725
|
+
log2("[magic-context] v2 latest-thinking recovery arm failed", armError);
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
3974
4728
|
continue;
|
|
3975
4729
|
}
|
|
3976
4730
|
if (event.type === "session.deleted") {
|
|
@@ -3992,6 +4746,7 @@ async function registerContext(context) {
|
|
|
3992
4746
|
pendingMaterializationSessions.delete(sessionID);
|
|
3993
4747
|
lastHeuristicsTurnId.delete(sessionID);
|
|
3994
4748
|
restoredRows.forget(sessionID);
|
|
4749
|
+
await nativeFoldReplay?.forget(sessionID);
|
|
3995
4750
|
generateReplay.forget(sessionID);
|
|
3996
4751
|
systemPromptRefreshSessions.delete(sessionID);
|
|
3997
4752
|
systemPrompt?.clearSession(sessionID);
|
|
@@ -4001,15 +4756,18 @@ async function registerContext(context) {
|
|
|
4001
4756
|
if (event.type !== "session.execution.succeeded")
|
|
4002
4757
|
continue;
|
|
4003
4758
|
const model = liveModels.get(sessionID);
|
|
4004
|
-
if (model)
|
|
4759
|
+
if (model) {
|
|
4005
4760
|
await recordUsage({
|
|
4006
4761
|
sessionID,
|
|
4007
4762
|
model: { providerID: model.providerID, id: model.modelID }
|
|
4008
4763
|
});
|
|
4764
|
+
if (!compactionOff && typeof await nativeFoldReplay?.baseline(sessionID) === "string")
|
|
4765
|
+
hostFolds?.idle(sessionID);
|
|
4766
|
+
}
|
|
4009
4767
|
}
|
|
4010
4768
|
} catch (error) {
|
|
4011
4769
|
if (!usageController.signal.aborted)
|
|
4012
|
-
|
|
4770
|
+
log2("[magic-context] v2 usage subscription failed", error);
|
|
4013
4771
|
}
|
|
4014
4772
|
})();
|
|
4015
4773
|
const materialize = (draft) => {
|
|
@@ -4040,19 +4798,41 @@ async function registerContext(context) {
|
|
|
4040
4798
|
}
|
|
4041
4799
|
if (compactionOff)
|
|
4042
4800
|
return;
|
|
4801
|
+
if (nativeFoldReplay)
|
|
4802
|
+
rememberHostMedia(draft.messages);
|
|
4043
4803
|
const reader = openStoreReader();
|
|
4044
4804
|
try {
|
|
4045
4805
|
const watermark = reader.latestSequenceForIds(draft.sessionID, draft.messages.flatMap((message) => message.id ? [message.id] : []));
|
|
4046
4806
|
const running = reader.latestRunningCompaction(draft.sessionID);
|
|
4047
4807
|
const moduleBaseline = rustModeModuleClient ? servedModuleM0Text(draft.sessionID) : undefined;
|
|
4048
4808
|
const source = !rustModeModuleClient ? "typescript" : moduleBaseline === null ? "typescript_fallback" : "module";
|
|
4809
|
+
if (nativeFoldReplay) {
|
|
4810
|
+
try {
|
|
4811
|
+
if (await nativeFoldReplay.canReplay(draft.sessionID, reader)) {
|
|
4812
|
+
const state = getOrCreateSessionMeta(storage.require(), draft.sessionID);
|
|
4813
|
+
const summary = moduleBaseline ?? await nativeFoldReplay.baseline(draft.sessionID) ?? state.cachedM0Bytes?.toString("utf8") ?? "<session-history></session-history>";
|
|
4814
|
+
if (await nativeFoldReplay.supply({
|
|
4815
|
+
draft,
|
|
4816
|
+
reader,
|
|
4817
|
+
previousCut: reader.latestCompaction(draft.sessionID),
|
|
4818
|
+
summary
|
|
4819
|
+
})) {
|
|
4820
|
+
draft.result = { summary };
|
|
4821
|
+
return;
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
} catch (error) {
|
|
4825
|
+
nativeFoldReplay.invalidate(draft.sessionID, true);
|
|
4826
|
+
sessionLog2(draft.sessionID, `v2 host fold: legacy compaction reason=${JSON.stringify(String(error))}`);
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4049
4829
|
const fold = await folds.supply({
|
|
4050
4830
|
sessionID: draft.sessionID,
|
|
4051
4831
|
watermark,
|
|
4052
4832
|
runningCut: running?.seq,
|
|
4053
4833
|
materialize: () => moduleBaseline ?? materialize(draft)
|
|
4054
4834
|
});
|
|
4055
|
-
|
|
4835
|
+
sessionLog2(draft.sessionID, `v2 compaction hook: fired answered=true source=${source}`);
|
|
4056
4836
|
draft.result = { summary: fold.submitted };
|
|
4057
4837
|
} catch (cause) {
|
|
4058
4838
|
await refuseBeforeProvider(context.session, draft.sessionID, "compaction-fold", cause);
|
|
@@ -4072,17 +4852,11 @@ async function registerContext(context) {
|
|
|
4072
4852
|
removeDreamerOnlyTools(draft);
|
|
4073
4853
|
if (deletedSessions.has(draft.sessionID))
|
|
4074
4854
|
return;
|
|
4075
|
-
const claimRefusal = await checkoutClaim.refusal(draft.sessionID, directory);
|
|
4076
|
-
if (claimRefusal) {
|
|
4077
|
-
pushNotification2("toast", { message: claimRefusal.message, variant: "error" }, draft.sessionID);
|
|
4078
|
-
await refuseBeforeProvider(context.session, draft.sessionID, "checkout-claim-held-elsewhere", claimRefusal);
|
|
4079
|
-
throw new V2ContextRefusal(claimRefusal.message, { cause: claimRefusal });
|
|
4080
|
-
}
|
|
4081
4855
|
const systemAtEntry = structuredClone(draft.system);
|
|
4082
4856
|
const slotAtEntry = getSlot(draft.sessionID);
|
|
4083
4857
|
const restoreLkgSystem = () => {
|
|
4084
4858
|
if (!lkgSystems.restore(draft.sessionID, getSlot(draft.sessionID), systemAtEntry, draft.system)) {
|
|
4085
|
-
|
|
4859
|
+
sessionLog2(draft.sessionID, "lkg_system_state_mismatch");
|
|
4086
4860
|
throw new Error("LKG system identity is unavailable or changed");
|
|
4087
4861
|
}
|
|
4088
4862
|
};
|
|
@@ -4099,15 +4873,68 @@ async function registerContext(context) {
|
|
|
4099
4873
|
});
|
|
4100
4874
|
variants.set(draft.sessionID, draft.model.variant);
|
|
4101
4875
|
if (!modelLimitCacheWarm())
|
|
4102
|
-
|
|
4876
|
+
withoutSqliteTransformPass2(() => warmModelLimitCacheFromCatalog(context));
|
|
4103
4877
|
agents.set(draft.sessionID, draft.agent);
|
|
4104
|
-
applyV2PromptSurfaceTools(draft, promptSurfaceRuntime, config.prompt_surface);
|
|
4878
|
+
applyV2PromptSurfaceTools(draft, promptSurfaceRuntime, config.prompt_surface, config.smart_notes.enabled);
|
|
4879
|
+
const admissionDb = db ?? storage.current();
|
|
4880
|
+
const replayFits = (messages) => {
|
|
4881
|
+
const system = structuredClone(draft.system);
|
|
4882
|
+
if (!admissionDb || !draft.tools || !lkgSystems.restore(draft.sessionID, getSlot(draft.sessionID), draft.system, system))
|
|
4883
|
+
return false;
|
|
4884
|
+
const limit = lkgReplayLimit({
|
|
4885
|
+
db: admissionDb,
|
|
4886
|
+
sessionId: draft.sessionID,
|
|
4887
|
+
model: {
|
|
4888
|
+
providerID: draft.model.providerID,
|
|
4889
|
+
modelID: draft.model.id
|
|
4890
|
+
},
|
|
4891
|
+
modelKey: `${draft.model.providerID}/${draft.model.id}`
|
|
4892
|
+
});
|
|
4893
|
+
if (limit === undefined)
|
|
4894
|
+
return false;
|
|
4895
|
+
let toolDefinitionTokens = 0;
|
|
4896
|
+
for (const tool of Object.values(draft.tools)) {
|
|
4897
|
+
if (!tool || typeof tool.description !== "string" || !tool.input)
|
|
4898
|
+
return false;
|
|
4899
|
+
const schema = JSON.stringify(tool.input);
|
|
4900
|
+
if (!schema)
|
|
4901
|
+
return false;
|
|
4902
|
+
toolDefinitionTokens += estimateTokens(tool.description) + estimateTokens(schema);
|
|
4903
|
+
}
|
|
4904
|
+
return measureLkgReplay({
|
|
4905
|
+
messages,
|
|
4906
|
+
limit,
|
|
4907
|
+
estimate: () => estimateFinalWireInputTokens({
|
|
4908
|
+
messages,
|
|
4909
|
+
toolDefinitionTokens,
|
|
4910
|
+
systemPromptTokens: estimateTokens(JSON.stringify(system)),
|
|
4911
|
+
providerID: draft.model.providerID,
|
|
4912
|
+
modelID: draft.model.id,
|
|
4913
|
+
agentName: draft.agent
|
|
4914
|
+
})
|
|
4915
|
+
}).fit === "under";
|
|
4916
|
+
};
|
|
4105
4917
|
let postFold = false;
|
|
4106
4918
|
try {
|
|
4107
|
-
const admissionDb = db ?? storage.current();
|
|
4108
4919
|
if (!compactionOff && admissionDb)
|
|
4109
|
-
await
|
|
4920
|
+
await withAsyncPrivilegedWriter2(admissionDb, () => {
|
|
4110
4921
|
return;
|
|
4922
|
+
}, {
|
|
4923
|
+
beforeRetry: (error) => {
|
|
4924
|
+
const mapped = adaptPayload(draft);
|
|
4925
|
+
if (tryMessagesTransformLkgReplay({
|
|
4926
|
+
output: mapped,
|
|
4927
|
+
sessionId: draft.sessionID,
|
|
4928
|
+
error,
|
|
4929
|
+
agent: draft.agent,
|
|
4930
|
+
rust: transform?.getRustReplayParticipant() ?? undefined,
|
|
4931
|
+
replayFits,
|
|
4932
|
+
onLkgReplay: restoreLkgSystem
|
|
4933
|
+
})) {
|
|
4934
|
+
mapped.commit();
|
|
4935
|
+
throw new V2LkgAdmissionReplay;
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4111
4938
|
});
|
|
4112
4939
|
if (admissionDb)
|
|
4113
4940
|
hideSubagentTools(draft, admissionDb);
|
|
@@ -4128,7 +4955,7 @@ async function registerContext(context) {
|
|
|
4128
4955
|
storageRecoveryAnnounced = true;
|
|
4129
4956
|
const recovered = await recoverHiddenWork(db);
|
|
4130
4957
|
const message = formatStorageRecoveryNotice(recovered);
|
|
4131
|
-
|
|
4958
|
+
log2(`[magic-context] v2 storage recovered: ${message}`);
|
|
4132
4959
|
pushNotification2("toast", { message, variant: "info" }, draft.sessionID);
|
|
4133
4960
|
storeStorageNotice(draft.sessionID, message, "recovery");
|
|
4134
4961
|
}
|
|
@@ -4143,11 +4970,12 @@ async function registerContext(context) {
|
|
|
4143
4970
|
readMessagePages: (sessionID) => readV2RawMessagePagesForConversion(openStoreReader, sessionID)
|
|
4144
4971
|
});
|
|
4145
4972
|
} catch (error) {
|
|
4146
|
-
|
|
4973
|
+
sessionLog2(draft.sessionID, "store projection rebase failed before system prompt (retrying in transform):", error);
|
|
4147
4974
|
}
|
|
4148
4975
|
systemPrompt ??= createSystemPromptHashHandler({
|
|
4149
4976
|
db,
|
|
4150
4977
|
dreamerEnabled: config.dreamer !== undefined && !config.dreamer.disable,
|
|
4978
|
+
smartNotesEnabled: config.smart_notes.enabled,
|
|
4151
4979
|
memoryEnabled: config.memory.enabled && !!resolveProjectIdentityForSession(directory, config.allow_home_project),
|
|
4152
4980
|
language: config.language,
|
|
4153
4981
|
promptSurface: config.prompt_surface,
|
|
@@ -4210,7 +5038,7 @@ async function registerContext(context) {
|
|
|
4210
5038
|
pendingMaterializationSessions,
|
|
4211
5039
|
lastHeuristicsTurnId,
|
|
4212
5040
|
variantBySession: variants,
|
|
4213
|
-
|
|
5041
|
+
keepReasoningTokens: config.keep_reasoning_tokens,
|
|
4214
5042
|
directory,
|
|
4215
5043
|
sessionDirectoryBySession: sessionDirectories,
|
|
4216
5044
|
projectPath: directory,
|
|
@@ -4262,6 +5090,8 @@ async function registerContext(context) {
|
|
|
4262
5090
|
admitted.add(message.id);
|
|
4263
5091
|
}
|
|
4264
5092
|
const cut = reader.latestCompaction(draft.sessionID);
|
|
5093
|
+
if (nativeFoldReplay)
|
|
5094
|
+
await nativeFoldReplay.observeSource(draft.sessionID, reader);
|
|
4265
5095
|
const boundaryRepair = !rustModeModuleClient && db && !compactionOff ? checkHistoryBoundary(db, reader, draft.sessionID) : undefined;
|
|
4266
5096
|
if (boundaryRepair?.kind === "repaired" || boundaryRepair?.kind === "baseline-reset") {
|
|
4267
5097
|
historyRefreshSessions.add(draft.sessionID);
|
|
@@ -4271,14 +5101,35 @@ async function registerContext(context) {
|
|
|
4271
5101
|
postFold = cut !== undefined;
|
|
4272
5102
|
if (cut && !incoming)
|
|
4273
5103
|
throw new Error("The host checkpoint disappeared from the context draft");
|
|
4274
|
-
|
|
5104
|
+
let nativeRestored;
|
|
5105
|
+
if (cut && nativeFoldReplay && isLocalCheckpoint(cut)) {
|
|
5106
|
+
try {
|
|
5107
|
+
nativeRestored = await nativeFoldReplay.restore(draft.sessionID, cut, `${draft.model.providerID}/${draft.model.id}`, { bounded: !rustModeModuleClient });
|
|
5108
|
+
} catch (error) {
|
|
5109
|
+
nativeFoldReplay.invalidate(draft.sessionID, true);
|
|
5110
|
+
sessionLog2(draft.sessionID, "v2 host fold: cache unavailable; restoring host rows", error);
|
|
5111
|
+
nativeRestored = await nativeFoldReplay.restoreRead(draft.sessionID, cut, draft.model);
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5114
|
+
if (nativeRestored && nativeFoldReplay?.sourceChanged(draft.sessionID)) {
|
|
5115
|
+
historyRefreshSessions.add(draft.sessionID);
|
|
5116
|
+
pendingMaterializationSessions.add(draft.sessionID);
|
|
5117
|
+
}
|
|
5118
|
+
if (cut && incoming && nativeRestored) {
|
|
5119
|
+
const present = new Set(draft.messages.flatMap((message) => message.id ? [message.id] : []));
|
|
5120
|
+
const hidden = nativeRestored.filter((message) => {
|
|
5121
|
+
const id = nativeRowID(message);
|
|
5122
|
+
return !id || !present.has(id);
|
|
5123
|
+
});
|
|
5124
|
+
draft.messages.splice(0, draft.messages.length, ...hidden, ...draft.messages.filter((message) => message !== incoming));
|
|
5125
|
+
} else if (cut && incoming) {
|
|
4275
5126
|
const identity = await folds.observe({
|
|
4276
5127
|
sessionID: draft.sessionID,
|
|
4277
5128
|
cutSeq: cut.seq,
|
|
4278
5129
|
summary: cut.data.summary ?? "",
|
|
4279
5130
|
rendered: incoming,
|
|
4280
5131
|
onHard: (reason) => {
|
|
4281
|
-
|
|
5132
|
+
sessionLog2(draft.sessionID, `HARD reason=${reason}`);
|
|
4282
5133
|
materialize(draft);
|
|
4283
5134
|
pendingMaterializationSessions.add(draft.sessionID);
|
|
4284
5135
|
}
|
|
@@ -4291,11 +5142,11 @@ async function registerContext(context) {
|
|
|
4291
5142
|
const servedBoundary = boundaryID ? servedBoundaryRow(reader, draft.sessionID, boundaryID) : null;
|
|
4292
5143
|
const boundary = (moduleBoundarySeq !== undefined ? moduleBoundarySeq - 1 : undefined) ?? servedBoundary?.seq ?? reader.sequenceForId(draft.sessionID, boundaryID) ?? (reader.earliestSequence(draft.sessionID) ?? 0) - 1;
|
|
4293
5144
|
if (moduleBoundarySeq !== undefined)
|
|
4294
|
-
|
|
5145
|
+
sessionLog2(draft.sessionID, `v2 restore: from the module boundary ${moduleBoundaryID} to the host checkpoint`);
|
|
4295
5146
|
const present = new Set(draft.messages.map((message) => message.id));
|
|
4296
5147
|
const restored = restoredRows.rows(reader, draft.sessionID, boundary, cut.seq).filter((row) => !present.has(row.id)).flatMap((row) => restoreRow(row, draft.model, hostUsesMediaAssets() ? {
|
|
4297
5148
|
asset: hostMediaAsset,
|
|
4298
|
-
unavailable: (detail) =>
|
|
5149
|
+
unavailable: (detail) => sessionLog2(draft.sessionID, `v2 restore: attachment replaced by a note row=${detail.rowID} name=${JSON.stringify(detail.name ?? null)} mediaType=${detail.mediaType} reason=${JSON.stringify(detail.reason)}`)
|
|
4299
5150
|
} : undefined));
|
|
4300
5151
|
draft.messages.splice(0, draft.messages.length, ...restored, ...draft.messages.filter((message) => message !== incoming));
|
|
4301
5152
|
}
|
|
@@ -4305,13 +5156,22 @@ async function registerContext(context) {
|
|
|
4305
5156
|
if (rustModeModuleClient && db) {
|
|
4306
5157
|
const dropped = trimToRecordedBoundary(db, draft.sessionID, draft.messages);
|
|
4307
5158
|
if (dropped > 0)
|
|
4308
|
-
|
|
5159
|
+
sessionLog2(draft.sessionID, `v2 boundary trim: dropped ${dropped} messages before the module boundary`);
|
|
5160
|
+
}
|
|
5161
|
+
let nativeMessages;
|
|
5162
|
+
if (nativeFoldReplay) {
|
|
5163
|
+
try {
|
|
5164
|
+
nativeMessages = copyNativeInput(draft.messages);
|
|
5165
|
+
} catch {
|
|
5166
|
+
nativeFoldReplay.invalidate(draft.sessionID, true);
|
|
5167
|
+
}
|
|
4309
5168
|
}
|
|
4310
5169
|
const mapped = adaptPayload(draft, admitted);
|
|
4311
5170
|
beginV2LkgRequest(draft.sessionID, `${draft.model.providerID}/${draft.model.id}`, latestLkgResponseIds.get(draft.sessionID));
|
|
4312
5171
|
await createMessagesTransformHandler({
|
|
4313
5172
|
magicContext: { "experimental.chat.messages.transform": transform },
|
|
4314
5173
|
compactionOff,
|
|
5174
|
+
replayFits,
|
|
4315
5175
|
onLkgReplay: restoreLkgSystem,
|
|
4316
5176
|
rustReplayParticipant: () => transform?.getRustReplayParticipant() ?? null
|
|
4317
5177
|
})({}, mapped);
|
|
@@ -4337,18 +5197,29 @@ ${baseline}
|
|
|
4337
5197
|
draft.messages.splice(0, draft.messages.length, checkpoint, ...draft.messages.filter((message) => message !== head));
|
|
4338
5198
|
}
|
|
4339
5199
|
}
|
|
5200
|
+
if (nativeMessages && !checkpoint) {
|
|
5201
|
+
try {
|
|
5202
|
+
await nativeFoldReplay?.capture(draft, nativeMessages);
|
|
5203
|
+
} catch (error) {
|
|
5204
|
+
nativeFoldReplay?.invalidate(draft.sessionID, true);
|
|
5205
|
+
sessionLog2(draft.sessionID, "v2 host fold: optional capture unavailable", error);
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
4340
5208
|
const capturedSlot = getSlot(draft.sessionID);
|
|
4341
5209
|
if (capturedSlot && (!slotAtEntry || capturedSlot.capturedAt !== slotAtEntry.capturedAt || capturedSlot.captureSequence !== slotAtEntry.captureSequence || capturedSlot.jsonPrefix !== slotAtEntry.jsonPrefix)) {
|
|
4342
5210
|
lkgSystems.capture(draft.sessionID, capturedSlot, systemAtEntry, draft.system);
|
|
4343
5211
|
}
|
|
4344
5212
|
} catch (error) {
|
|
5213
|
+
if (error instanceof V2LkgAdmissionReplay)
|
|
5214
|
+
return;
|
|
4345
5215
|
if (error instanceof V2ContextRefusal)
|
|
4346
5216
|
throw error;
|
|
4347
|
-
if (!compactionOff && (
|
|
4348
|
-
if (
|
|
5217
|
+
if (!compactionOff && (isTransientSqliteError2(error) || error instanceof StorageBusyRefusalError)) {
|
|
5218
|
+
if (isTransientSqliteError2(error)) {
|
|
4349
5219
|
const mapped = adaptPayload(draft);
|
|
4350
5220
|
try {
|
|
4351
5221
|
await createMessagesTransformHandler({
|
|
5222
|
+
replayFits,
|
|
4352
5223
|
onLkgReplay: restoreLkgSystem,
|
|
4353
5224
|
rustReplayParticipant: () => transform?.getRustReplayParticipant() ?? null,
|
|
4354
5225
|
magicContext: {
|
|
@@ -4381,16 +5252,16 @@ ${baseline}
|
|
|
4381
5252
|
cause: managedError
|
|
4382
5253
|
});
|
|
4383
5254
|
}
|
|
4384
|
-
|
|
5255
|
+
log2("[magic-context] compaction-off: fail-closed inert, passing through", error);
|
|
4385
5256
|
} else if (postFold) {
|
|
4386
5257
|
await refuseBeforeProvider(context.session, draft.sessionID, "post-fold-restore", error);
|
|
4387
5258
|
throw new V2ContextRefusal("Magic Context could not restore the unarchived host history.", { cause: error });
|
|
4388
5259
|
} else {
|
|
4389
|
-
|
|
5260
|
+
log2("[magic-context] v2 context unavailable", error);
|
|
4390
5261
|
}
|
|
4391
5262
|
}
|
|
4392
5263
|
};
|
|
4393
|
-
await context.session.hook("context", (draft) =>
|
|
5264
|
+
await context.session.hook("context", (draft) => withSqliteTransformPass2(async () => {
|
|
4394
5265
|
const anchor = draft.messages.at(-1)?.id;
|
|
4395
5266
|
await runManagedContext(draft);
|
|
4396
5267
|
if (!compactionOff && draft.messages.some((message) => message.id === HEAD_IDS[0] || message.content.some((part) => typeof part.text === "string" && part.text.includes("<session-history>"))))
|
|
@@ -4400,7 +5271,7 @@ ${baseline}
|
|
|
4400
5271
|
if (!compactionOff && !deletedSessions.has(draft.sessionID))
|
|
4401
5272
|
generateReplay.apply(draft);
|
|
4402
5273
|
});
|
|
4403
|
-
|
|
5274
|
+
withoutSqliteTransformPass2(() => warmModelLimitCacheFromCatalog(context));
|
|
4404
5275
|
const rpcLiveSessionState = createV2RpcLiveSessionState({
|
|
4405
5276
|
liveModelBySession: liveModels,
|
|
4406
5277
|
variantBySession: variants,
|
|
@@ -4461,6 +5332,7 @@ ${baseline}
|
|
|
4461
5332
|
runManualDreamNow({
|
|
4462
5333
|
db: runDb,
|
|
4463
5334
|
dreamer: manualDreamer,
|
|
5335
|
+
smartNotesEnabled: config.smart_notes.enabled,
|
|
4464
5336
|
projectIdentity: resolveProjectIdentityForSession(directory, config.allow_home_project) ?? "",
|
|
4465
5337
|
directory,
|
|
4466
5338
|
language: config.language,
|
|
@@ -4499,7 +5371,7 @@ ${baseline}
|
|
|
4499
5371
|
setTimeout(() => {
|
|
4500
5372
|
if (rpcStopped)
|
|
4501
5373
|
return;
|
|
4502
|
-
rpcServer.start().catch((error) =>
|
|
5374
|
+
rpcServer.start().catch((error) => log2("[magic-context] v2 RPC server failed to start", error));
|
|
4503
5375
|
}, 0);
|
|
4504
5376
|
return {
|
|
4505
5377
|
async dispose() {
|