@cortexkit/opencode-magic-context 0.49.5 → 0.49.7
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/auto-search-worker.js +10 -7
- package/dist/{compaction-marker-candidates-dd638ypn.js → compaction-marker-candidates-a0edbqrr.js} +2 -2
- package/dist/compaction-marker-candidates-worker.js +2 -2
- package/dist/{conflict-warning-hook-ae8nmv7c.js → conflict-warning-hook-myk5hbt9.js} +5 -5
- package/dist/{context-yg023c5r.js → context-ffr7e0g7.js} +46 -34
- package/dist/{embedding-backfill-aj488s8x.js → embedding-backfill-re16xncd.js} +6 -6
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/hidden-single-shot.d.ts +6 -3
- package/dist/features/magic-context/dreamer/hidden-single-shot.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +7 -1
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts +2 -2
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/primer-seed.d.ts +2 -0
- package/dist/features/magic-context/dreamer/primer-seed.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/refresh-primers.d.ts +2 -2
- package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +10 -3
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage-replay-document.d.ts +4 -0
- package/dist/features/magic-context/storage-replay-document.d.ts.map +1 -1
- package/dist/hooks/magic-context/apply-operations.d.ts +7 -0
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/history-boundary-repair.d.ts.map +1 -1
- package/dist/hooks/magic-context/lkg-persist.d.ts.map +1 -1
- package/dist/hooks/magic-context/note-nudger.d.ts +5 -3
- package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
- package/dist/hooks/magic-context/postprocess-read-cache.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +7 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/replay-committed-drops.d.ts +9 -0
- package/dist/hooks/magic-context/replay-committed-drops.d.ts.map +1 -0
- package/dist/hooks/magic-context/rust-lkg-freeze-registry.d.ts +2 -2
- package/dist/hooks/magic-context/rust-lkg-freeze-registry.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-mode-transform.d.ts +3 -0
- package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
- package/dist/hooks/magic-context/rust-served-raw.d.ts +29 -0
- package/dist/hooks/magic-context/rust-served-raw.d.ts.map +1 -0
- package/dist/hooks/magic-context/strip-content.d.ts +23 -24
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +2 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +2 -0
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/{index-m4p77myp.js → index-0gyq3djm.js} +1 -1
- package/dist/{index-jpm52v9x.js → index-37q80veb.js} +1 -1
- package/dist/{index-znzgze2v.js → index-4btknbqh.js} +9 -8
- package/dist/{index-v4bqjy56.js → index-5tbvdff4.js} +59 -28
- package/dist/{index-7jzrrxap.js → index-9yyqq3mm.js} +1 -1
- package/dist/{index-qvr78ej6.js → index-a8acx4gc.js} +2 -2
- package/dist/{index-gcdb3rqw.js → index-ax2jhztn.js} +1 -1
- package/dist/{index-2mqps3c1.js → index-ebnngykq.js} +145 -94
- package/dist/{index-41gqa557.js → index-g8qbdb03.js} +1 -1
- package/dist/{index-fst3t0d9.js → index-j58gqrn2.js} +65 -65
- package/dist/{index-xfbfrngb.js → index-j5hce1ab.js} +2 -2
- package/dist/{index-kb9nn0a0.js → index-j8epsc0g.js} +35 -14
- package/dist/{index-7zg9dvx0.js → index-n5a170gs.js} +227 -38
- package/dist/{index-fjtgekd4.js → index-n7kwfw2r.js} +4 -4
- package/dist/{index-dd18gej8.js → index-rwmpkss5.js} +3 -3
- package/dist/{index-7w18gg78.js → index-rzmjkdd6.js} +1 -1
- package/dist/{index-c0b01zrc.js → index-sf94at64.js} +3 -3
- package/dist/{index-yxawqmdd.js → index-sh91t19a.js} +1 -1
- package/dist/{index-x3y1np1h.js → index-tagss15p.js} +857 -125
- package/dist/{index-yqps2zsv.js → index-tmtty2x5.js} +1 -1
- package/dist/{index-bhk710rw.js → index-vt4sf4ep.js} +1 -1
- package/dist/{index-6m7rdv7y.js → index-w4mw92w3.js} +1 -1
- package/dist/{index-7ndnvw7a.js → index-w6bd03dc.js} +1 -1
- package/dist/{index-p2hs3rps.js → index-w6vr754t.js} +4 -4
- package/dist/{index-3wckxwfp.js → index-wq16ahsn.js} +5 -5
- package/dist/index.js +36 -36
- package/dist/{message-index-bounded-pv6ct15d.js → message-index-bounded-04rycpx4.js} +9 -9
- package/dist/{message-index-jqa7qd0s.js → message-index-rqtse28y.js} +9 -9
- package/dist/{message-time-backfill-8x06r9kx.js → message-time-backfill-a5ytnp53.js} +1 -1
- package/dist/migration-worker.js +6 -6
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/{project-embedding-registry-fcmszatg.js → project-embedding-registry-k0sd9k7v.js} +5 -5
- package/dist/raw-ordinal-warmup-worker.js +17 -15
- package/dist/{read-session-chunk-cwgjwjd0.js → read-session-chunk-h1btx938.js} +7 -5
- package/dist/{read-session-raw-z38cn0as.js → read-session-raw-vdf4wh78.js} +2 -2
- package/dist/{recomp-orchestrator-xw9wt5bm.js → recomp-orchestrator-trcgmm23.js} +20 -20
- package/dist/{search-v1t47s2q.js → search-3rbdx6hw.js} +16 -16
- package/dist/{send-session-notification-yp9m5g4p.js → send-session-notification-2tp44tfa.js} +4 -4
- package/dist/shared/processed-image-eligibility.d.ts +22 -0
- package/dist/shared/processed-image-eligibility.d.ts.map +1 -0
- package/dist/shared/recovery-reasoning-prefix.d.ts +9 -0
- package/dist/shared/recovery-reasoning-prefix.d.ts.map +1 -0
- package/dist/shared/sqlite.d.ts +1 -1
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/tag-transcript.d.ts +9 -0
- package/dist/shared/tag-transcript.d.ts.map +1 -1
- package/dist/{sqlite-mhjkerzq.js → sqlite-j3tvg8r0.js} +1 -1
- package/dist/{storage-memory-07fx57k6.js → storage-memory-8168bgef.js} +2 -2
- package/dist/{storage-tags-0ssbmzg9.js → storage-tags-61z5j2f9.js} +2 -2
- package/dist/tools/ctx-expand/search-archive.d.ts +4 -0
- package/dist/tools/ctx-expand/search-archive.d.ts.map +1 -0
- package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
- package/dist/{transform-postprocess-phase-ttra554y.js → transform-postprocess-phase-7qts7h54.js} +20 -20
- package/dist/v2/auto-search-worker.js +10 -7
- package/dist/v2/{compaction-marker-candidates-1s21e2fz.js → compaction-marker-candidates-6nypjkpf.js} +2 -2
- package/dist/v2/compaction-marker-candidates-worker.js +2 -2
- package/dist/v2/{context-6en6a8ar.js → context-xq4rtywr.js} +1019 -233
- package/dist/v2/{embedding-backfill-9hbkfvnz.js → embedding-backfill-xd9ygzgd.js} +6 -6
- package/dist/v2/hooks/payload.d.ts.map +1 -1
- package/dist/v2/{message-index-h8ssgq3q.js → message-index-8e5z2xx8.js} +9 -9
- package/dist/v2/{message-index-bounded-c0810p5w.js → message-index-bounded-5vh1h2g7.js} +9 -9
- package/dist/v2/{message-time-backfill-cje68egj.js → message-time-backfill-2z365239.js} +1 -1
- package/dist/v2/migration-worker.js +6 -6
- package/dist/v2/{project-embedding-registry-42th7k1z.js → project-embedding-registry-b77awytr.js} +5 -5
- package/dist/v2/raw-ordinal-warmup-worker.js +17 -15
- package/dist/v2/{read-session-chunk-n86v9xfg.js → read-session-chunk-4sat83cx.js} +7 -5
- package/dist/v2/{read-session-raw-3b3ehga4.js → read-session-raw-a3hw9jsp.js} +2 -2
- package/dist/v2/{recomp-orchestrator-krvsbnds.js → recomp-orchestrator-n11zv1f1.js} +19 -19
- package/dist/v2/{search-048v8y02.js → search-mvgqtkkz.js} +16 -16
- package/dist/v2/{send-session-notification-a4h2azxa.js → send-session-notification-ppgc8qdz.js} +4 -4
- package/dist/v2/{server-cvgn49tr.js → server-1rfgbjfn.js} +1 -1
- package/dist/v2/{server-3bc6k4k4.js → server-7srtwsa4.js} +5 -5
- package/dist/v2/{server-7jzrrxap.js → server-9yyqq3mm.js} +1 -1
- package/dist/v2/{server-2htmbqbv.js → server-brhj8qtd.js} +1 -1
- package/dist/v2/{server-3dqjhchy.js → server-dd0nja67.js} +1 -1
- package/dist/v2/{server-8wcf79fe.js → server-fpqe39as.js} +1 -1
- package/dist/v2/{server-cjz51jm2.js → server-fw31krgq.js} +3 -3
- package/dist/v2/{server-4n2tknpc.js → server-gtv9wbhq.js} +227 -38
- package/dist/v2/{server-8pqnnmf9.js → server-hcvcxzx7.js} +2 -2
- package/dist/v2/{server-kb9nn0a0.js → server-j8epsc0g.js} +35 -14
- package/dist/v2/{server-rygvffsf.js → server-jdwd03ep.js} +4 -4
- package/dist/v2/{server-q6j5dasr.js → server-kdrezdy0.js} +1 -1
- package/dist/v2/{server-e2v4m8xe.js → server-mv1w7t6z.js} +61 -30
- package/dist/v2/{server-hetyb88z.js → server-nfan6zb6.js} +65 -65
- package/dist/v2/{server-4enr7s6x.js → server-qcw43339.js} +1 -1
- package/dist/v2/{server-5zrpr764.js → server-sy4b1sra.js} +1 -1
- package/dist/v2/{server-9a6v0wth.js → server-v4fwha43.js} +9 -8
- package/dist/v2/{server-pcygfdbs.js → server-w3zeywp1.js} +1 -1
- package/dist/v2/{server-czb2rkpk.js → server-wkdgjjch.js} +1 -1
- package/dist/v2/{server-9290a1mb.js → server-x08d1xf3.js} +1 -1
- package/dist/v2/server.js +4 -4
- package/dist/v2/{sqlite-mhjkerzq.js → sqlite-j3tvg8r0.js} +1 -1
- package/dist/v2/{storage-memory-07fx57k6.js → storage-memory-8168bgef.js} +2 -2
- package/dist/v2/{storage-tags-3a4cavqb.js → storage-tags-j2fzq3sw.js} +2 -2
- package/dist/v2/{wrapup-orchestrator-r2v7kgy3.js → wrapup-orchestrator-s92ebfzv.js} +19 -19
- package/dist/{wrapup-orchestrator-z7agncp8.js → wrapup-orchestrator-df61jmz9.js} +21 -21
- package/package.json +3 -3
- package/src/shared/processed-image-eligibility.test.ts +68 -0
- package/src/shared/processed-image-eligibility.ts +41 -0
- package/src/shared/processed-image-position-review.json +20 -0
- package/src/shared/processed-image-position-review.test.ts +19 -0
- package/src/shared/recovery-reasoning-prefix.test.ts +34 -0
- package/src/shared/recovery-reasoning-prefix.ts +35 -0
- package/src/shared/sqlite-acquisition-routing.test.ts +4 -3
- package/src/shared/sqlite-foreground-wait.test.ts +140 -0
- package/src/shared/sqlite-pass-scope.test.ts +4 -4
- package/src/shared/sqlite-writer-diagnostics.test.ts +31 -0
- package/src/shared/sqlite.ts +64 -23
- package/src/shared/tag-transcript.ts +118 -0
package/dist/v2/server.js
CHANGED
|
@@ -7870,7 +7870,7 @@ var require_src2 = __commonJS(function(exports, module) {
|
|
|
7870
7870
|
// package.json
|
|
7871
7871
|
var package_default = {
|
|
7872
7872
|
name: "@cortexkit/opencode-magic-context",
|
|
7873
|
-
version: "0.49.
|
|
7873
|
+
version: "0.49.7",
|
|
7874
7874
|
type: "module",
|
|
7875
7875
|
description: "OpenCode plugin for Magic Context — cross-session memory and context management",
|
|
7876
7876
|
main: "dist/index.js",
|
|
@@ -7913,8 +7913,8 @@ var package_default = {
|
|
|
7913
7913
|
"check:tui-compiled": 'git diff --exit-code -- src/tui-compiled && test -z "$(git status --porcelain -- src/tui-compiled)"',
|
|
7914
7914
|
"check:transformers-node-wasm": "bun scripts/verify-transformers-node-wasm.ts",
|
|
7915
7915
|
typecheck: "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit && tsc -p tsconfig.scripts.json",
|
|
7916
|
-
test: "bun ../../scripts/check-bun.mjs && bun install --frozen-lockfile && BUN_JSC_useOMGJIT=0 bun test --parallel=4 --timeout 30000",
|
|
7917
|
-
"test:serial": "bun install --frozen-lockfile && BUN_JSC_useOMGJIT=0 bun test --timeout 30000",
|
|
7916
|
+
test: "bun ../../scripts/check-bun.mjs && bun install --frozen-lockfile && MC_REVIEW_ASSERT_LIVENESS=1 BUN_JSC_useOMGJIT=0 bun test --parallel=4 --timeout 30000",
|
|
7917
|
+
"test:serial": "bun install --frozen-lockfile && MC_REVIEW_ASSERT_LIVENESS=1 BUN_JSC_useOMGJIT=0 bun test --timeout 30000",
|
|
7918
7918
|
"test:parallel:probe": "bun scripts/verify-parallel-db-isolation.ts",
|
|
7919
7919
|
lint: "bun ../../scripts/check-bun.mjs && biome check .",
|
|
7920
7920
|
"lint:fix": "biome check --write .",
|
|
@@ -8186,7 +8186,7 @@ async function setup(context) {
|
|
|
8186
8186
|
log2("v2 setup() called on a host without session.hook; v1 server lane owns this host, v2 lane inert");
|
|
8187
8187
|
return async () => {};
|
|
8188
8188
|
}
|
|
8189
|
-
const { registerContext } = await import("./context-
|
|
8189
|
+
const { registerContext } = await import("./context-xq4rtywr.js");
|
|
8190
8190
|
setHarness2("opencode2");
|
|
8191
8191
|
bindStaleBuildNotice({}, import.meta.url, "opencode2");
|
|
8192
8192
|
const duties = await registerContext(context);
|
|
@@ -31,10 +31,10 @@ import {
|
|
|
31
31
|
deleteMemory2,
|
|
32
32
|
getMemoryCount2,
|
|
33
33
|
getMemoryCountsByStatus2
|
|
34
|
-
} from "./server-
|
|
34
|
+
} from "./server-9yyqq3mm.js";
|
|
35
35
|
import"./server-4dhv7mqz.js";
|
|
36
36
|
import"./server-fr0wbhdn.js";
|
|
37
|
-
import"./server-
|
|
37
|
+
import"./server-j8epsc0g.js";
|
|
38
38
|
export {
|
|
39
39
|
COLUMN_MAP2 as COLUMN_MAP,
|
|
40
40
|
ModuleMemoryAuthorityError2 as ModuleMemoryAuthorityError,
|
|
@@ -60,10 +60,10 @@ import {
|
|
|
60
60
|
adoptNullOwnerToolTag2,
|
|
61
61
|
getCandidateToolOwners2,
|
|
62
62
|
pickNearestPriorOwner2
|
|
63
|
-
} from "./server-
|
|
63
|
+
} from "./server-w3zeywp1.js";
|
|
64
64
|
import"./server-1zx87sdm.js";
|
|
65
65
|
import"./server-6byqfbqf.js";
|
|
66
|
-
import"./server-
|
|
66
|
+
import"./server-j8epsc0g.js";
|
|
67
67
|
import"./server-kzns2hbz.js";
|
|
68
68
|
import"./server-t4k4qexw.js";
|
|
69
69
|
import"./server-pavzsdc6.js";
|
|
@@ -2,7 +2,7 @@ import"./server-pavzsdc6.js";
|
|
|
2
2
|
import {
|
|
3
3
|
sessionLog2
|
|
4
4
|
} from "./server-t4k4qexw.js";
|
|
5
|
-
import"./server-
|
|
5
|
+
import"./server-1rfgbjfn.js";
|
|
6
6
|
import {
|
|
7
7
|
COMPARTMENT_LEASE_RENEWAL_MS,
|
|
8
8
|
acquireCompartmentLease,
|
|
@@ -19,13 +19,13 @@ import {
|
|
|
19
19
|
registerActiveCompartmentRun,
|
|
20
20
|
setRecompStarting2,
|
|
21
21
|
setRecompTerminal2
|
|
22
|
-
} from "./server-
|
|
22
|
+
} from "./server-mv1w7t6z.js";
|
|
23
23
|
import"./server-kzns2hbz.js";
|
|
24
24
|
import"./server-hb6xhh9q.js";
|
|
25
|
-
import"./server-
|
|
26
|
-
import"./server-
|
|
27
|
-
import"./server-
|
|
28
|
-
import"./server-
|
|
25
|
+
import"./server-j8epsc0g.js";
|
|
26
|
+
import"./server-brhj8qtd.js";
|
|
27
|
+
import"./server-fpqe39as.js";
|
|
28
|
+
import"./server-7srtwsa4.js";
|
|
29
29
|
import {
|
|
30
30
|
getWrapupInProgressState,
|
|
31
31
|
acquireWrapupInProgress,
|
|
@@ -34,39 +34,39 @@ import {
|
|
|
34
34
|
getHistorianFailureState,
|
|
35
35
|
clearEmergencyRecovery,
|
|
36
36
|
prepareRawSessionOrdinals2
|
|
37
|
-
} from "./server-
|
|
37
|
+
} from "./server-gtv9wbhq.js";
|
|
38
38
|
import"./server-6byqfbqf.js";
|
|
39
|
-
import"./server-
|
|
39
|
+
import"./server-v4fwha43.js";
|
|
40
40
|
import"./server-1zx87sdm.js";
|
|
41
|
-
import"./server-
|
|
41
|
+
import"./server-w3zeywp1.js";
|
|
42
42
|
import"./server-cw6521wn.js";
|
|
43
43
|
import"./server-51779zs2.js";
|
|
44
|
-
import"./server-
|
|
44
|
+
import"./server-dd0nja67.js";
|
|
45
45
|
import"./server-2ra6pwf3.js";
|
|
46
|
-
import"./server-
|
|
46
|
+
import"./server-wkdgjjch.js";
|
|
47
47
|
import"./server-7ekna9zs.js";
|
|
48
48
|
import"./server-fr0wbhdn.js";
|
|
49
|
-
import"./server-
|
|
50
|
-
import"./server-
|
|
51
|
-
import"./server-
|
|
49
|
+
import"./server-hcvcxzx7.js";
|
|
50
|
+
import"./server-kdrezdy0.js";
|
|
51
|
+
import"./server-fw31krgq.js";
|
|
52
52
|
import"./server-fb3zxn06.js";
|
|
53
|
-
import"./server-
|
|
53
|
+
import"./server-jdwd03ep.js";
|
|
54
54
|
import {
|
|
55
55
|
pushNotification2
|
|
56
56
|
} from "./server-x8a7jmkh.js";
|
|
57
|
-
import"./server-
|
|
57
|
+
import"./server-x08d1xf3.js";
|
|
58
58
|
import"./server-qt1qqasc.js";
|
|
59
59
|
import"./server-4dhv7mqz.js";
|
|
60
60
|
import"./server-ras2sxnc.js";
|
|
61
|
-
import"./server-
|
|
62
|
-
import"./server-
|
|
61
|
+
import"./server-sy4b1sra.js";
|
|
62
|
+
import"./server-9yyqq3mm.js";
|
|
63
63
|
import"./server-d6g4whc2.js";
|
|
64
64
|
import"./server-vb7pbd9p.js";
|
|
65
65
|
import"./server-t528t8qm.js";
|
|
66
66
|
import"./server-676xw7at.js";
|
|
67
67
|
import {
|
|
68
68
|
sendStatusNotification2
|
|
69
|
-
} from "./server-
|
|
69
|
+
} from "./server-qcw43339.js";
|
|
70
70
|
import"./server-3t0q2cqx.js";
|
|
71
71
|
|
|
72
72
|
// src/hooks/magic-context/wrapup-orchestrator.ts
|
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runManagedWrapup2
|
|
3
|
-
} from "./index-
|
|
4
|
-
import"./index-
|
|
5
|
-
import"./index-
|
|
6
|
-
import"./index-
|
|
3
|
+
} from "./index-w6vr754t.js";
|
|
4
|
+
import"./index-rwmpkss5.js";
|
|
5
|
+
import"./index-5tbvdff4.js";
|
|
6
|
+
import"./index-sh91t19a.js";
|
|
7
7
|
import"./index-sd3egckz.js";
|
|
8
|
-
import"./index-
|
|
9
|
-
import"./index-
|
|
10
|
-
import"./index-
|
|
11
|
-
import"./index-
|
|
12
|
-
import"./index-
|
|
13
|
-
import"./index-
|
|
8
|
+
import"./index-n7kwfw2r.js";
|
|
9
|
+
import"./index-wq16ahsn.js";
|
|
10
|
+
import"./index-w6bd03dc.js";
|
|
11
|
+
import"./index-9yyqq3mm.js";
|
|
12
|
+
import"./index-sf94at64.js";
|
|
13
|
+
import"./index-ax2jhztn.js";
|
|
14
14
|
import"./index-4dhv7mqz.js";
|
|
15
15
|
import"./index-w23dh319.js";
|
|
16
|
-
import"./index-
|
|
16
|
+
import"./index-vt4sf4ep.js";
|
|
17
17
|
import"./index-qt1qqasc.js";
|
|
18
18
|
import"./index-x8a7jmkh.js";
|
|
19
19
|
import"./index-tqf7yg27.js";
|
|
20
20
|
import"./index-gbmqa9x9.js";
|
|
21
21
|
import"./index-vkbqdnkj.js";
|
|
22
|
-
import"./index-
|
|
22
|
+
import"./index-4btknbqh.js";
|
|
23
23
|
import"./index-fb3zxn06.js";
|
|
24
|
-
import"./index-
|
|
25
|
-
import"./index-
|
|
24
|
+
import"./index-j5hce1ab.js";
|
|
25
|
+
import"./index-w4mw92w3.js";
|
|
26
26
|
import"./index-fr0wbhdn.js";
|
|
27
|
-
import"./index-
|
|
28
|
-
import"./index-
|
|
29
|
-
import"./index-
|
|
30
|
-
import"./index-
|
|
27
|
+
import"./index-g8qbdb03.js";
|
|
28
|
+
import"./index-rzmjkdd6.js";
|
|
29
|
+
import"./index-n5a170gs.js";
|
|
30
|
+
import"./index-tmtty2x5.js";
|
|
31
31
|
import"./index-1zx87sdm.js";
|
|
32
|
-
import"./index-
|
|
33
|
-
import"./index-
|
|
32
|
+
import"./index-a8acx4gc.js";
|
|
33
|
+
import"./index-37q80veb.js";
|
|
34
34
|
import"./index-v0p37hm2.js";
|
|
35
35
|
import"./index-k6xvbj2e.js";
|
|
36
36
|
import"./index-h2wc01qk.js";
|
|
37
37
|
import"./index-1bt88epp.js";
|
|
38
38
|
import"./index-58e3y90n.js";
|
|
39
39
|
import"./index-4mjhqadz.js";
|
|
40
|
-
import"./index-
|
|
40
|
+
import"./index-j8epsc0g.js";
|
|
41
41
|
import"./index-qx53vezw.js";
|
|
42
42
|
import"./index-nsjyysj0.js";
|
|
43
43
|
import"./index-0sahzkwr.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-magic-context",
|
|
3
|
-
"version": "0.49.
|
|
3
|
+
"version": "0.49.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode plugin for Magic Context — cross-session memory and context management",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"check:tui-compiled": "git diff --exit-code -- src/tui-compiled && test -z \"$(git status --porcelain -- src/tui-compiled)\"",
|
|
44
44
|
"check:transformers-node-wasm": "bun scripts/verify-transformers-node-wasm.ts",
|
|
45
45
|
"typecheck": "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit && tsc -p tsconfig.scripts.json",
|
|
46
|
-
"test": "bun ../../scripts/check-bun.mjs && bun install --frozen-lockfile && BUN_JSC_useOMGJIT=0 bun test --parallel=4 --timeout 30000",
|
|
47
|
-
"test:serial": "bun install --frozen-lockfile && BUN_JSC_useOMGJIT=0 bun test --timeout 30000",
|
|
46
|
+
"test": "bun ../../scripts/check-bun.mjs && bun install --frozen-lockfile && MC_REVIEW_ASSERT_LIVENESS=1 BUN_JSC_useOMGJIT=0 bun test --parallel=4 --timeout 30000",
|
|
47
|
+
"test:serial": "bun install --frozen-lockfile && MC_REVIEW_ASSERT_LIVENESS=1 BUN_JSC_useOMGJIT=0 bun test --timeout 30000",
|
|
48
48
|
"test:parallel:probe": "bun scripts/verify-parallel-db-isolation.ts",
|
|
49
49
|
"lint": "bun ../../scripts/check-bun.mjs && biome check .",
|
|
50
50
|
"lint:fix": "biome check --write .",
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { createProcessedImageEligibility } from "./processed-image-eligibility";
|
|
3
|
+
|
|
4
|
+
const messages = ["old", "image", "answer", "tail"];
|
|
5
|
+
|
|
6
|
+
test("image position age inherits its next positive tag, never zero or an earlier tag", () => {
|
|
7
|
+
const policy = createProcessedImageEligibility(
|
|
8
|
+
messages,
|
|
9
|
+
new Map([
|
|
10
|
+
["old", 1],
|
|
11
|
+
["answer", 6],
|
|
12
|
+
]),
|
|
13
|
+
{ watermark: 1 },
|
|
14
|
+
);
|
|
15
|
+
expect(policy.ageTags.get("image")).toBe(6);
|
|
16
|
+
expect(policy.mayStrip("image")).toBe(false);
|
|
17
|
+
expect(policy.ageTags.has("tail")).toBe(false);
|
|
18
|
+
expect(policy.mayStrip("tail")).toBe(false);
|
|
19
|
+
const own = createProcessedImageEligibility(
|
|
20
|
+
messages,
|
|
21
|
+
new Map([
|
|
22
|
+
["image", 3],
|
|
23
|
+
["answer", 6],
|
|
24
|
+
]),
|
|
25
|
+
{ watermark: 4 },
|
|
26
|
+
);
|
|
27
|
+
expect(own.ageTags.get("image")).toBe(3);
|
|
28
|
+
expect(own.mayStrip("image")).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("answered image-only positions outside the protected window are eligible", () => {
|
|
32
|
+
const policy = createProcessedImageEligibility(
|
|
33
|
+
messages,
|
|
34
|
+
new Map([
|
|
35
|
+
["answer", 3],
|
|
36
|
+
["tail", 5],
|
|
37
|
+
]),
|
|
38
|
+
{ watermark: 4, protectedCutoff: 5, contextPercentage: 94.99 },
|
|
39
|
+
);
|
|
40
|
+
expect(policy.ageTags.get("image")).toBe(3);
|
|
41
|
+
expect(policy.mayStrip("image")).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("image positions inside the protected window stay below the 95 percent emergency", () => {
|
|
45
|
+
const policy = createProcessedImageEligibility(messages, new Map([["answer", 6]]), {
|
|
46
|
+
watermark: 10,
|
|
47
|
+
protectedCutoff: 5,
|
|
48
|
+
contextPercentage: 94.99,
|
|
49
|
+
});
|
|
50
|
+
expect(policy.mayStrip("image")).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("image positions follow emergency drops when the newest window yields at 95 percent", () => {
|
|
54
|
+
for (const contextPercentage of [95, 99]) {
|
|
55
|
+
const policy = createProcessedImageEligibility(messages, new Map([["answer", 6]]), {
|
|
56
|
+
watermark: 10,
|
|
57
|
+
protectedCutoff: 5,
|
|
58
|
+
contextPercentage,
|
|
59
|
+
});
|
|
60
|
+
expect(policy.mayStrip("image")).toBe(true);
|
|
61
|
+
const notDroppedYet = createProcessedImageEligibility(messages, new Map([["answer", 11]]), {
|
|
62
|
+
watermark: 10,
|
|
63
|
+
protectedCutoff: 5,
|
|
64
|
+
contextPercentage,
|
|
65
|
+
});
|
|
66
|
+
expect(notDroppedYet.mayStrip("image")).toBe(false);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared processed-image age and recency policy for both OpenCode adapters.
|
|
3
|
+
* Use the owning message's positive maximum tag; an untagged image-only message
|
|
4
|
+
* inherits the next positive tagged message after it, never age zero or a tag
|
|
5
|
+
* from an earlier message. Without either tag, retain the image at the live tail.
|
|
6
|
+
* A positive dropped-tag watermark must cover that position. Below 95% usage,
|
|
7
|
+
* retain every position at or beyond the protected newest-window cutoff. At
|
|
8
|
+
* 95% and above only that window yields, exactly as for emergency tool drops;
|
|
9
|
+
* independent per-tool protections still apply. Content before retained provider-signed
|
|
10
|
+
* thinking must stay unchanged unless an existing whole-turn rewrite removes that
|
|
11
|
+
* thinking first. Callers enforce these protections, later-assistant/stable-id checks, durable
|
|
12
|
+
* persistence and rewrite-only discovery. Frozen replay bypasses eligibility.
|
|
13
|
+
*/
|
|
14
|
+
export function createProcessedImageEligibility<T>(
|
|
15
|
+
messages: readonly T[],
|
|
16
|
+
messageTagNumbers: ReadonlyMap<T, number>,
|
|
17
|
+
options: { watermark: number; protectedCutoff?: number | null; contextPercentage?: number },
|
|
18
|
+
): { ageTags: ReadonlyMap<T, number>; mayStrip: (message: T) => boolean } {
|
|
19
|
+
const ageTags = new Map<T, number>();
|
|
20
|
+
let nextTag: number | undefined;
|
|
21
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
22
|
+
const message = messages[index];
|
|
23
|
+
const ownTag = messageTagNumbers.get(message);
|
|
24
|
+
if (ownTag !== undefined && Number.isFinite(ownTag) && ownTag > 0) nextTag = ownTag;
|
|
25
|
+
if (nextTag !== undefined) ageTags.set(message, nextTag);
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
ageTags,
|
|
29
|
+
mayStrip: (message) => {
|
|
30
|
+
const age = ageTags.get(message);
|
|
31
|
+
return (
|
|
32
|
+
options.watermark > 0 &&
|
|
33
|
+
age !== undefined &&
|
|
34
|
+
age <= options.watermark &&
|
|
35
|
+
((options.contextPercentage ?? 0) >= 95 ||
|
|
36
|
+
options.protectedCutoff == null ||
|
|
37
|
+
age < options.protectedCutoff)
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "name": "own maximum wins over a later smaller tag", "tags": [[2, 10, 4], [3]], "target": 0, "watermark": 8, "cutoff": null, "percentage": 60, "age": 10, "eligible": false },
|
|
3
|
+
{ "name": "own maximum at watermark", "tags": [[2, 10, 4], [30]], "target": 0, "watermark": 10, "cutoff": null, "percentage": 60, "age": 10, "eligible": true },
|
|
4
|
+
{ "name": "first later tag not largest later tag", "tags": [[], [3], [20]], "target": 0, "watermark": 3, "cutoff": null, "percentage": 60, "age": 3, "eligible": true },
|
|
5
|
+
{ "name": "first later message contributes its own maximum", "tags": [[], [2, 9], [3]], "target": 0, "watermark": 5, "cutoff": null, "percentage": 60, "age": 9, "eligible": false },
|
|
6
|
+
{ "name": "skip empty and zero tags when looking forward", "tags": [[1], [], [0], [4]], "target": 1, "watermark": 4, "cutoff": null, "percentage": 60, "age": 4, "eligible": true },
|
|
7
|
+
{ "name": "zero own tag inherits later positive", "tags": [[0], [7]], "target": 0, "watermark": 6, "cutoff": null, "percentage": 60, "age": 7, "eligible": false },
|
|
8
|
+
{ "name": "preceding tag cannot age live tail", "tags": [[1], [], []], "target": 1, "watermark": 100, "cutoff": null, "percentage": 60, "age": null, "eligible": false },
|
|
9
|
+
{ "name": "all untagged is ineligible", "tags": [[], []], "target": 0, "watermark": 100, "cutoff": null, "percentage": 60, "age": null, "eligible": false },
|
|
10
|
+
{ "name": "watermark zero even with emergency", "tags": [[], [1]], "target": 0, "watermark": 0, "cutoff": 1, "percentage": 95, "age": 1, "eligible": false },
|
|
11
|
+
{ "name": "age above watermark even with emergency", "tags": [[], [7]], "target": 0, "watermark": 6, "cutoff": 1, "percentage": 100, "age": 7, "eligible": false },
|
|
12
|
+
{ "name": "cutoff inclusive at 94.99 percent", "tags": [[5], [8]], "target": 0, "watermark": 9, "cutoff": 5, "percentage": 94.99, "age": 5, "eligible": false },
|
|
13
|
+
{ "name": "inside cutoff is a range not set membership", "tags": [[], [6]], "target": 0, "watermark": 9, "cutoff": 5, "percentage": 60, "age": 6, "eligible": false },
|
|
14
|
+
{ "name": "below cutoff is eligible", "tags": [[4], [8]], "target": 0, "watermark": 9, "cutoff": 5, "percentage": 94.99, "age": 4, "eligible": true },
|
|
15
|
+
{ "name": "exactly 95 waives cutoff", "tags": [[], [6]], "target": 0, "watermark": 9, "cutoff": 5, "percentage": 95, "age": 6, "eligible": true },
|
|
16
|
+
{ "name": "above 95 waives cutoff", "tags": [[5], [8]], "target": 0, "watermark": 9, "cutoff": 5, "percentage": 95.01, "age": 5, "eligible": true },
|
|
17
|
+
{ "name": "emergency cannot invent tail age", "tags": [[1], []], "target": 1, "watermark": 100, "cutoff": 1, "percentage": 100, "age": null, "eligible": false },
|
|
18
|
+
{ "name": "own positive tag overrides later greater tag", "tags": [[2], [20]], "target": 0, "watermark": 2, "cutoff": 3, "percentage": 60, "age": 2, "eligible": true },
|
|
19
|
+
{ "name": "last message with own tag is aged", "tags": [[1], [4]], "target": 1, "watermark": 4, "cutoff": null, "percentage": 60, "age": 4, "eligible": true }
|
|
20
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { createProcessedImageEligibility } from "./processed-image-eligibility";
|
|
3
|
+
import cases from "./processed-image-position-review.json";
|
|
4
|
+
|
|
5
|
+
// OpenCode, Rust and Pi can reuse these cases: each row supplies per-message
|
|
6
|
+
// tag numbers rather than provider-specific message objects or image encodings.
|
|
7
|
+
for (const row of cases) {
|
|
8
|
+
test(`independent image position table: ${row.name}`, () => {
|
|
9
|
+
const messages = row.tags.map((_, i) => `message-${i}`);
|
|
10
|
+
const tags = new Map(messages.map((message, i) => [message, Math.max(0, ...row.tags[i])]));
|
|
11
|
+
const policy = createProcessedImageEligibility(messages, tags, {
|
|
12
|
+
watermark: row.watermark,
|
|
13
|
+
protectedCutoff: row.cutoff,
|
|
14
|
+
contextPercentage: row.percentage,
|
|
15
|
+
});
|
|
16
|
+
expect(policy.ageTags.get(messages[row.target]) ?? null).toBe(row.age);
|
|
17
|
+
expect(policy.mayStrip(messages[row.target])).toBe(row.eligible);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { recoveryReasoningPrefixes } from "./recovery-reasoning-prefix";
|
|
3
|
+
|
|
4
|
+
for (const key of ["content", "parts"] as const) {
|
|
5
|
+
test(`recovery reasoning ${key}: exact preceding messages and same-message parts`, () => {
|
|
6
|
+
const messages = [
|
|
7
|
+
{ role: "user", [key]: [{ type: "text", text: "original" }] },
|
|
8
|
+
{
|
|
9
|
+
role: "assistant",
|
|
10
|
+
[key]: [
|
|
11
|
+
{ type: "text", text: "before" },
|
|
12
|
+
{ type: "thinking", thinkingSignature: "first" },
|
|
13
|
+
{ type: "text", text: "between" },
|
|
14
|
+
{ type: "redacted_thinking", data: "opaque" },
|
|
15
|
+
{ type: "text", text: "after" },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
const original = recoveryReasoningPrefixes(messages, key);
|
|
20
|
+
expect(original).toHaveLength(2);
|
|
21
|
+
const edited = structuredClone(messages);
|
|
22
|
+
edited[0]![key]![0]!.text = "changed";
|
|
23
|
+
expect(recoveryReasoningPrefixes(edited, key)).not.toEqual(original);
|
|
24
|
+
const parts = structuredClone(messages);
|
|
25
|
+
parts[1]![key]![2]!.text = "changed";
|
|
26
|
+
expect(recoveryReasoningPrefixes(parts, key)[0]).toBe(original[0]);
|
|
27
|
+
expect(recoveryReasoningPrefixes(parts, key)[1]).not.toBe(original[1]);
|
|
28
|
+
const suffix = structuredClone(messages);
|
|
29
|
+
suffix[1]![key]![4]!.text = "changed";
|
|
30
|
+
expect(recoveryReasoningPrefixes(suffix, key)).toEqual(original);
|
|
31
|
+
suffix[1]![key]!.splice(3, 1);
|
|
32
|
+
expect(recoveryReasoningPrefixes(suffix, key)).not.toEqual(original);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialize each request prefix ending at a thinking/reasoning part, including
|
|
3
|
+
* earlier messages and earlier parts of that message. Comparing these snapshots
|
|
4
|
+
* detects edits to bytes authenticated by a kept signature, or missing blocks.
|
|
5
|
+
* Include reasoning without a visible signature too: a host may carry its
|
|
6
|
+
* provider signature in fields this helper does not interpret.
|
|
7
|
+
*/
|
|
8
|
+
export function recoveryReasoningPrefixes(
|
|
9
|
+
messages: readonly unknown[],
|
|
10
|
+
partsKey: "parts" | "content",
|
|
11
|
+
): string[] {
|
|
12
|
+
const prefixes: string[] = [];
|
|
13
|
+
for (let index = 0; index < messages.length; index++) {
|
|
14
|
+
const message = messages[index] as Record<string, unknown> | null;
|
|
15
|
+
const parts = message?.[partsKey];
|
|
16
|
+
if (!Array.isArray(parts)) continue;
|
|
17
|
+
for (let p = 0; p < parts.length; p++) {
|
|
18
|
+
const type = (parts[p] as { type?: string } | null)?.type;
|
|
19
|
+
if (
|
|
20
|
+
type !== "thinking" &&
|
|
21
|
+
type !== "reasoning" &&
|
|
22
|
+
type !== "redactedThinking" &&
|
|
23
|
+
type !== "redacted_thinking"
|
|
24
|
+
)
|
|
25
|
+
continue;
|
|
26
|
+
prefixes.push(
|
|
27
|
+
JSON.stringify([
|
|
28
|
+
...messages.slice(0, index),
|
|
29
|
+
{ ...message, [partsKey]: parts.slice(0, p + 1) },
|
|
30
|
+
]),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return prefixes;
|
|
35
|
+
}
|
|
@@ -118,7 +118,7 @@ test("exhausted routed acquisition never enters the callback or multiplies privi
|
|
|
118
118
|
db.close();
|
|
119
119
|
rmSync(dir, { recursive: true, force: true });
|
|
120
120
|
}
|
|
121
|
-
});
|
|
121
|
+
}, 30_000);
|
|
122
122
|
|
|
123
123
|
for (const mode of ["run", "get", "all", "exec"] as const) {
|
|
124
124
|
test(`foreground ${mode} autocommit waits share one turn budget and restore production timeout`, () => {
|
|
@@ -147,7 +147,8 @@ for (const mode of ["run", "get", "all", "exec"] as const) {
|
|
|
147
147
|
}
|
|
148
148
|
expect(() => db.exec("BEGIN IMMEDIATE")).toThrow("acquisition remained busy");
|
|
149
149
|
});
|
|
150
|
-
expect(performance.now() - started).
|
|
150
|
+
expect(performance.now() - started).toBeGreaterThanOrEqual(1900);
|
|
151
|
+
expect(performance.now() - started).toBeLessThan(2600);
|
|
151
152
|
expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 5000 });
|
|
152
153
|
expect(db.prepare("SELECT * FROM result").all()).toEqual([]);
|
|
153
154
|
blocker.exec("ROLLBACK");
|
|
@@ -159,5 +160,5 @@ for (const mode of ["run", "get", "all", "exec"] as const) {
|
|
|
159
160
|
db.close();
|
|
160
161
|
rmSync(dir, { recursive: true, force: true });
|
|
161
162
|
}
|
|
162
|
-
});
|
|
163
|
+
}, 30_000);
|
|
163
164
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { StorageBusyRefusalError } from "../hooks/magic-context/storage-busy-refusal";
|
|
6
|
+
import { createMessagesTransformHandler } from "../plugin/messages-transform";
|
|
7
|
+
import { log } from "./logger";
|
|
8
|
+
import {
|
|
9
|
+
Database,
|
|
10
|
+
guardSqliteTransformPass,
|
|
11
|
+
registerSqliteDiagnosticSink,
|
|
12
|
+
SqliteAcquisitionBusyError,
|
|
13
|
+
withPrivilegedWriter,
|
|
14
|
+
withSqliteTransformPass,
|
|
15
|
+
} from "./sqlite";
|
|
16
|
+
import { type SqliteWriteLocker, startSqliteWriteLocker } from "./sqlite-write-locker-test-support";
|
|
17
|
+
import { createTestTempDirFromPath } from "./test-temp-dir";
|
|
18
|
+
|
|
19
|
+
function fixture() {
|
|
20
|
+
const dir = createTestTempDirFromPath(join(tmpdir(), "mc-foreground-wait-"));
|
|
21
|
+
const path = join(dir, "context.db");
|
|
22
|
+
const db = new Database(path);
|
|
23
|
+
db.exec(`
|
|
24
|
+
PRAGMA journal_mode=WAL;
|
|
25
|
+
PRAGMA busy_timeout=5000;
|
|
26
|
+
CREATE TABLE context_privilege_state(id INTEGER PRIMARY KEY, enabled INTEGER);
|
|
27
|
+
CREATE TABLE result(value TEXT);
|
|
28
|
+
`);
|
|
29
|
+
return {
|
|
30
|
+
db,
|
|
31
|
+
path,
|
|
32
|
+
close() {
|
|
33
|
+
db.close();
|
|
34
|
+
rmSync(dir, { recursive: true, force: true });
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const holdMs of [1000, 2500]) {
|
|
40
|
+
test(holdMs === 1000
|
|
41
|
+
? "a transform commits and serves managed output after a 1s in-pass writer lock"
|
|
42
|
+
: "a transform refuses a 2.5s in-pass writer lock without raw fallback and logs the real wait", async () => {
|
|
43
|
+
const { db, path, close } = fixture();
|
|
44
|
+
const sessionId = `foreground-wait-${holdMs}`;
|
|
45
|
+
const output = {
|
|
46
|
+
messages: [
|
|
47
|
+
{
|
|
48
|
+
info: { id: "u", sessionID: sessionId, role: "user" },
|
|
49
|
+
parts: [{ type: "text", text: "raw input must not be served on failure" }],
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
let locker: SqliteWriteLocker | undefined;
|
|
54
|
+
let refusals = 0;
|
|
55
|
+
let served = false;
|
|
56
|
+
let waitedMs = 0;
|
|
57
|
+
const diagnostics: string[] = [];
|
|
58
|
+
registerSqliteDiagnosticSink((line) => diagnostics.push(line));
|
|
59
|
+
const handler = createMessagesTransformHandler({
|
|
60
|
+
onStorageBusyRefusal: async (id) => {
|
|
61
|
+
expect(id).toBe(sessionId);
|
|
62
|
+
refusals++;
|
|
63
|
+
},
|
|
64
|
+
magicContext: {
|
|
65
|
+
"experimental.chat.messages.transform": async (_input, result) => {
|
|
66
|
+
// Start the competing writer only once the transform callback begins;
|
|
67
|
+
// otherwise the wrapper's yielding lock check would absorb the wait.
|
|
68
|
+
locker = await startSqliteWriteLocker(path, holdMs);
|
|
69
|
+
const started = performance.now();
|
|
70
|
+
try {
|
|
71
|
+
withPrivilegedWriter(db, () => {
|
|
72
|
+
db.prepare("INSERT INTO result VALUES ('managed')").run();
|
|
73
|
+
});
|
|
74
|
+
} finally {
|
|
75
|
+
waitedMs = performance.now() - started;
|
|
76
|
+
}
|
|
77
|
+
(result.messages[0].parts[0] as { text: string }).text = "managed output";
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
const pass = handler({}, output as Parameters<typeof handler>[1]).then((messages) => {
|
|
83
|
+
served = true;
|
|
84
|
+
return messages;
|
|
85
|
+
});
|
|
86
|
+
if (holdMs === 1000) {
|
|
87
|
+
await pass;
|
|
88
|
+
expect(served).toBe(true);
|
|
89
|
+
expect(refusals).toBe(0);
|
|
90
|
+
expect(output.messages[0].parts[0].text).toBe("managed output");
|
|
91
|
+
expect(db.prepare("SELECT * FROM result").all()).toEqual([{ value: "managed" }]);
|
|
92
|
+
expect(waitedMs).toBeGreaterThanOrEqual(900);
|
|
93
|
+
} else {
|
|
94
|
+
await expect(pass).rejects.toBeInstanceOf(StorageBusyRefusalError);
|
|
95
|
+
expect(served).toBe(false);
|
|
96
|
+
expect(refusals).toBe(1);
|
|
97
|
+
expect(db.prepare("SELECT * FROM result").all()).toEqual([]);
|
|
98
|
+
expect(waitedMs).toBeGreaterThanOrEqual(1900);
|
|
99
|
+
expect(waitedMs).toBeLessThan(2400);
|
|
100
|
+
const busy = diagnostics.filter((line) => line.includes("outcome=busy"));
|
|
101
|
+
expect(busy).toHaveLength(1);
|
|
102
|
+
expect(busy[0]).toContain("lane=foreground");
|
|
103
|
+
const reportedMs = Number(/acquire_ms=(\d+)/.exec(busy[0])?.[1]);
|
|
104
|
+
expect(Math.abs(reportedMs - waitedMs)).toBeLessThan(100);
|
|
105
|
+
}
|
|
106
|
+
expect(db.inTransaction).toBe(false);
|
|
107
|
+
expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 5000 });
|
|
108
|
+
} finally {
|
|
109
|
+
registerSqliteDiagnosticSink(log);
|
|
110
|
+
if (locker) expect(await locker.exited).toBe(0);
|
|
111
|
+
close();
|
|
112
|
+
}
|
|
113
|
+
}, 30_000);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (const mode of ["BEGIN", "autocommit"]) {
|
|
117
|
+
test(`foreground ${mode} wait is capped by the pass deadline`, () => {
|
|
118
|
+
const { db, path, close } = fixture();
|
|
119
|
+
const blocker = new Database(path);
|
|
120
|
+
blocker.exec("BEGIN IMMEDIATE");
|
|
121
|
+
try {
|
|
122
|
+
const started = performance.now();
|
|
123
|
+
withSqliteTransformPass(() => {
|
|
124
|
+
guardSqliteTransformPass({ assert() {}, remainingMs: () => 100 });
|
|
125
|
+
expect(() => {
|
|
126
|
+
if (mode === "BEGIN") withPrivilegedWriter(db, () => undefined);
|
|
127
|
+
else db.prepare("INSERT INTO result VALUES ('late')").run();
|
|
128
|
+
}).toThrow(SqliteAcquisitionBusyError);
|
|
129
|
+
});
|
|
130
|
+
expect(performance.now() - started).toBeGreaterThanOrEqual(75);
|
|
131
|
+
expect(performance.now() - started).toBeLessThan(700);
|
|
132
|
+
expect(db.prepare("SELECT * FROM result").all()).toEqual([]);
|
|
133
|
+
expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 5000 });
|
|
134
|
+
} finally {
|
|
135
|
+
blocker.exec("ROLLBACK");
|
|
136
|
+
blocker.close();
|
|
137
|
+
close();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|