@cortexkit/opencode-magic-context 0.49.20 → 0.49.22
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 +6 -6
- package/dist/chunk-embedding-write-worker.js +9 -9
- package/dist/{compaction-marker-candidates-b2q30cft.js → compaction-marker-candidates-31h86ph0.js} +2 -2
- package/dist/compaction-marker-candidates-worker.js +2 -2
- package/dist/{compartment-chunk-embedding-va090as0.js → compartment-chunk-embedding-he60aaa2.js} +5 -5
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +37 -3
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-frp3z2wk.js → conflict-warning-hook-2r32hyx6.js} +5 -5
- package/dist/{context-mkece1db.js → context-j7hp43a0.js} +153 -83
- package/dist/{embedding-backfill-gy3p78py.js → embedding-backfill-dtke6tck.js} +11 -11
- package/dist/embedding-worker-process.js +1 -1
- package/dist/embedding-worker.js +4 -4
- package/dist/features/magic-context/dreamer/docs-proposals.d.ts +1 -0
- package/dist/features/magic-context/dreamer/docs-proposals.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-synapse.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-worker-client.d.ts +7 -1
- package/dist/features/magic-context/memory/embedding-worker-client.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-async.d.ts +7 -2
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +2 -0
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/search-rerank.d.ts +9 -2
- package/dist/features/magic-context/search-rerank.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts +2 -0
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts +2 -0
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +1 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-worker-client.d.ts.map +1 -1
- package/dist/hooks/magic-context/cache-busting-signals.d.ts +1 -0
- package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/served-shape-repair.d.ts +19 -0
- package/dist/hooks/magic-context/served-shape-repair.d.ts.map +1 -0
- package/dist/hooks/magic-context/strip-content.d.ts +4 -2
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-content-primitives.d.ts +1 -1
- package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +2 -1
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/text-complete.d.ts +1 -1
- package/dist/hooks/magic-context/text-complete.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +2 -2
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-t7hx3qcb.js → index-11gzxvf4.js} +101 -46
- package/dist/{index-neb2jaed.js → index-1gtfne2c.js} +1 -1
- package/dist/{index-67nyj0gy.js → index-1kbznkft.js} +48 -24
- package/dist/{index-1pefp8ar.js → index-3svaejkh.js} +4 -4
- package/dist/{index-0wthn7fp.js → index-51rdvtkz.js} +35 -4
- package/dist/{index-kc9pn495.js → index-66eby7hx.js} +1 -1
- package/dist/{index-pnjwa6qa.js → index-9q1g3c5q.js} +2 -2
- package/dist/{index-rmd0069m.js → index-a9zvn5fe.js} +24 -10
- package/dist/{index-jfh3cktr.js → index-ffzhwfzy.js} +3 -3
- package/dist/{v2/server-1zx2pa5d.js → index-gzy5w8ya.js} +193 -4
- package/dist/{index-d6rp4dxh.js → index-j32jj2t8.js} +4 -4
- package/dist/{index-35qc6606.js → index-jfem3dgz.js} +1 -1
- package/dist/{index-y4a3nhtk.js → index-jjvbk92s.js} +1 -1
- package/dist/{index-4n7w9rad.js → index-jmq8kfe7.js} +5 -5
- package/dist/{index-c4b76d4t.js → index-kfyb3k6n.js} +1 -1
- package/dist/{index-vkc1ca9p.js → index-kq5w68dp.js} +1 -1
- package/dist/{index-11zf2wwc.js → index-ksq50rg0.js} +2 -2
- package/dist/{index-153r2q6z.js → index-m32esgmj.js} +2 -2
- package/dist/{index-x4k3f4mf.js → index-mnhcdfxg.js} +148 -8
- package/dist/{index-48stjd56.js → index-mnnkgq0q.js} +10 -4
- package/dist/{index-wgypt3cs.js → index-nxrsns5x.js} +2466 -1290
- package/dist/{index-616rsjrv.js → index-qgh1qwjj.js} +1 -1
- package/dist/{index-jr5na10t.js → index-rxqr723x.js} +4 -4
- package/dist/{index-dc8hy95c.js → index-syaazvy2.js} +123 -40
- package/dist/{index-adk2z5d8.js → index-wfybj1dw.js} +2 -4
- package/dist/{index-305y133n.js → index-whcgcfrv.js} +6 -3
- package/dist/{index-jmycsey7.js → index-x6cbrv9h.js} +2 -2
- package/dist/{index-v7r4td4c.js → index-y76rhsnr.js} +28 -17
- package/dist/{index-2yhtnfcf.js → index-zy67whjr.js} +26 -8
- package/dist/index.js +43 -42
- package/dist/{message-fts-rowid-map-22tevnnj.js → message-fts-rowid-map-e79b7b6x.js} +3 -3
- package/dist/{message-index-5qxcz6h7.js → message-index-4q8ek2fc.js} +10 -10
- package/dist/{message-index-bounded-n0m0t7tv.js → message-index-bounded-rydsg9r5.js} +10 -10
- package/dist/{message-time-backfill-0myx7fz3.js → message-time-backfill-4qva0b4f.js} +1 -1
- package/dist/migration-worker.js +6 -6
- package/dist/plugin/embedding-bootstrap.d.ts +1 -0
- package/dist/plugin/embedding-bootstrap.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/{project-embedding-registry-s2pqgec9.js → project-embedding-registry-hn529988.js} +12 -10
- package/dist/raw-ordinal-warmup-worker.js +8 -8
- package/dist/{read-session-chunk-0a8vdyqp.js → read-session-chunk-t039f0m5.js} +6 -6
- package/dist/{read-session-raw-y5cszf0b.js → read-session-raw-aw6jgvwg.js} +2 -2
- package/dist/{recomp-orchestrator-61vyha9j.js → recomp-orchestrator-hbm53jj5.js} +23 -23
- package/dist/{search-archive-8q9s49p5.js → search-archive-weh164mh.js} +7 -7
- package/dist/{search-v1y9ejws.js → search-hea079d7.js} +19 -19
- package/dist/{send-session-notification-0k6qzydk.js → send-session-notification-gqzkz7da.js} +4 -4
- package/dist/shared/sqlite.d.ts +5 -0
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/tag-transcript.d.ts +2 -0
- package/dist/shared/tag-transcript.d.ts.map +1 -1
- package/dist/{sqlite-1rpvw2rj.js → sqlite-zdgm52sv.js} +5 -1
- package/dist/{storage-memory-nm7v82mj.js → storage-memory-e6a7mb0k.js} +2 -2
- package/dist/{storage-tags-r6nk29n4.js → storage-tags-a77q4v77.js} +2 -2
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/types.d.ts +1 -0
- package/dist/tools/ctx-search/types.d.ts.map +1 -1
- package/dist/{transform-postprocess-phase-vtrgb7m6.js → transform-postprocess-phase-0b04tm1p.js} +23 -23
- package/dist/v2/auto-search-worker.js +6 -6
- package/dist/v2/chunk-embedding-write-worker.js +11 -11
- package/dist/v2/{compaction-marker-candidates-cfab70cs.js → compaction-marker-candidates-m4vmye3w.js} +2 -2
- package/dist/v2/compaction-marker-candidates-worker.js +2 -2
- package/dist/v2/{compartment-chunk-embedding-nbdqx1td.js → compartment-chunk-embedding-8jjmcgeg.js} +5 -5
- package/dist/v2/{context-cc33tv2z.js → context-ea3x7smm.js} +652 -468
- package/dist/v2/{embedding-backfill-yxv2h5mc.js → embedding-backfill-5e74ae0m.js} +11 -11
- package/dist/v2/embedding-worker-process.js +1 -1
- package/dist/v2/embedding-worker.js +4 -4
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/copied-tag-notation.d.ts +1 -1
- package/dist/v2/hooks/copied-tag-notation.d.ts.map +1 -1
- package/dist/v2/hooks/dream-timer.d.ts +2 -0
- package/dist/v2/hooks/dream-timer.d.ts.map +1 -1
- package/dist/v2/hooks/embedding-lifecycle.d.ts +7 -0
- package/dist/v2/hooks/embedding-lifecycle.d.ts.map +1 -0
- package/dist/v2/hooks/payload.d.ts.map +1 -1
- package/dist/v2/hooks/store.d.ts +2 -0
- package/dist/v2/hooks/store.d.ts.map +1 -1
- package/dist/v2/hooks/tools.d.ts +2 -1
- package/dist/v2/hooks/tools.d.ts.map +1 -1
- package/dist/v2/hooks/transform-features.d.ts +2 -2
- package/dist/v2/hooks/transform-features.d.ts.map +1 -1
- package/dist/v2/{message-fts-rowid-map-dg12wymk.js → message-fts-rowid-map-n8fdg2ja.js} +3 -3
- package/dist/v2/{message-index-bounded-f4e01xk0.js → message-index-bounded-8rcd7qq9.js} +12 -12
- package/dist/v2/{message-index-ydzrp6y6.js → message-index-qz9wdp82.js} +12 -12
- package/dist/v2/{message-time-backfill-ddh8n9gy.js → message-time-backfill-zvthw6xb.js} +1 -1
- package/dist/v2/migration-worker.js +8 -8
- package/dist/v2/{project-embedding-registry-deza9ksw.js → project-embedding-registry-v38q4xd5.js} +12 -10
- package/dist/v2/raw-ordinal-warmup-worker.js +8 -8
- package/dist/v2/{read-session-chunk-8cxfagky.js → read-session-chunk-wazfyxcg.js} +8 -8
- package/dist/v2/{read-session-raw-1nrrecvx.js → read-session-raw-b70cpwzj.js} +2 -2
- package/dist/v2/{recomp-orchestrator-47m4hv5g.js → recomp-orchestrator-b12zaptj.js} +25 -25
- package/dist/v2/{search-archive-e80cvc09.js → search-archive-x64rtz1z.js} +9 -9
- package/dist/v2/{search-fjz746zr.js → search-hza9x72a.js} +21 -21
- package/dist/v2/{send-session-notification-nwvrv34d.js → send-session-notification-8eqe259v.js} +4 -4
- package/dist/v2/{server-4sxjs6hn.js → server-132h8twc.js} +4 -4
- package/dist/v2/{server-271qxsf5.js → server-24g8r2d0.js} +2466 -1290
- package/dist/v2/{server-m91f9m5s.js → server-33nqbqkw.js} +123 -40
- package/dist/v2/{server-brj8j2gk.js → server-3vhxy2k5.js} +1 -1
- package/dist/v2/{server-55rgxfrw.js → server-4kjmrjn4.js} +1 -1
- package/dist/v2/{server-wyawsa88.js → server-62g58aaz.js} +2 -2
- package/dist/v2/{server-230fnbws.js → server-6v3cdzsx.js} +1 -1
- package/dist/v2/{server-ygtykank.js → server-806rzhs6.js} +1 -1
- package/dist/v2/{server-5arr9ecn.js → server-94wk1ey3.js} +12 -1
- package/dist/v2/{server-af2pght7.js → server-ab1g0wn9.js} +2 -4
- package/dist/v2/{server-0epqaxhd.js → server-ad18djzs.js} +26 -8
- package/dist/v2/{server-4nw4fqhr.js → server-bsgd4dk1.js} +1 -1
- package/dist/v2/{server-dj7tz4ph.js → server-d1dzgvwd.js} +1 -1
- package/dist/v2/{server-vn2nwcv6.js → server-d48x3m4x.js} +4 -4
- package/dist/v2/{server-br1x0tev.js → server-dtp8hcjh.js} +2 -2
- package/dist/v2/{server-a3dwxfgd.js → server-fa7vtp40.js} +7 -4
- package/dist/{index-1zx2pa5d.js → v2/server-gzy5w8ya.js} +193 -4
- package/dist/v2/{server-hqzyq8n4.js → server-j8h7bsca.js} +26 -12
- package/dist/v2/{server-y4a3nhtk.js → server-jjvbk92s.js} +1 -1
- package/dist/v2/{server-ej96a39v.js → server-m4bfaevz.js} +1186 -1046
- package/dist/v2/{server-63fqdghs.js → server-ppcd1qxh.js} +92 -86
- package/dist/v2/{server-ne8arh8t.js → server-qjvk4a9t.js} +1 -1
- package/dist/v2/{server-xsryvyf6.js → server-qteg2kt9.js} +4 -4
- package/dist/v2/{server-168pb207.js → server-rqebawx1.js} +1 -1
- package/dist/v2/{server-j4jnp9b5.js → server-vr4szghm.js} +35 -4
- package/dist/v2/{server-9x0kmta4.js → server-wwaw5npa.js} +3 -3
- package/dist/v2/{server-c7dxxsj7.js → server-zg5y109h.js} +33 -33
- package/dist/v2/server.js +6 -6
- package/dist/v2/{sqlite-1rpvw2rj.js → sqlite-zdgm52sv.js} +5 -1
- package/dist/v2/{storage-memory-nm7v82mj.js → storage-memory-e6a7mb0k.js} +2 -2
- package/dist/v2/{storage-tags-ss08kj5r.js → storage-tags-q71rnr9f.js} +3 -3
- package/dist/v2/store-reader.d.ts +2 -0
- package/dist/v2/store-reader.d.ts.map +1 -1
- package/dist/v2/{wrapup-orchestrator-639whfhq.js → wrapup-orchestrator-3ssd5yy1.js} +25 -25
- package/dist/wrapup-orchestrator-5vpcd1zx.js +49 -0
- package/package.json +2 -2
- package/src/config/index.test.ts +44 -0
- package/src/config/project-security.ts +15 -0
- package/src/config/rerank.test.ts +60 -0
- package/src/config/schema/magic-context.ts +57 -0
- package/src/shared/model-suggestion-retry.ts +1 -1
- package/src/shared/sqlite-turn-deadline.test.ts +270 -0
- package/src/shared/sqlite.ts +235 -7
- package/src/shared/tag-transcript.ts +9 -1
- package/dist/wrapup-orchestrator-mnvt2g8m.js +0 -49
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { Database as NativeDatabase } from "bun:sqlite";
|
|
2
|
+
import { expect, spyOn, test } from "bun:test";
|
|
3
|
+
import { rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
createSqliteTurnDatabase,
|
|
8
|
+
Database,
|
|
9
|
+
withoutSqliteTransformPass,
|
|
10
|
+
withSqliteTurnDeadline,
|
|
11
|
+
} from "./sqlite";
|
|
12
|
+
import { createTestTempDirFromPath } from "./test-temp-dir";
|
|
13
|
+
|
|
14
|
+
async function fixture(run: (db: Database, path: string) => Promise<void>) {
|
|
15
|
+
const root = createTestTempDirFromPath(join(tmpdir(), "sqlite-turn-deadline-"));
|
|
16
|
+
const path = join(root, "context.db");
|
|
17
|
+
const db = createSqliteTurnDatabase(path);
|
|
18
|
+
db.exec(
|
|
19
|
+
"PRAGMA journal_mode=DELETE; PRAGMA busy_timeout=900; CREATE TABLE t(value); INSERT INTO t VALUES(1)",
|
|
20
|
+
);
|
|
21
|
+
try {
|
|
22
|
+
await run(db, path);
|
|
23
|
+
} finally {
|
|
24
|
+
db.close();
|
|
25
|
+
rmSync(root, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function hold(path: string, reader = false) {
|
|
30
|
+
const child = Bun.spawn(
|
|
31
|
+
[
|
|
32
|
+
process.execPath,
|
|
33
|
+
"-e",
|
|
34
|
+
`
|
|
35
|
+
const { Database } = require('bun:sqlite');
|
|
36
|
+
const db = new Database(process.argv[1]);
|
|
37
|
+
db.exec(${JSON.stringify(reader ? "BEGIN; SELECT * FROM t" : "BEGIN EXCLUSIVE")});
|
|
38
|
+
console.log('HELD');
|
|
39
|
+
setInterval(() => {}, 1000);
|
|
40
|
+
`,
|
|
41
|
+
path,
|
|
42
|
+
],
|
|
43
|
+
{ stdout: "pipe", stderr: "pipe", windowsHide: true },
|
|
44
|
+
);
|
|
45
|
+
const output = child.stdout.getReader();
|
|
46
|
+
const first = await output.read();
|
|
47
|
+
output.releaseLock();
|
|
48
|
+
if (!new TextDecoder().decode(first.value).includes("HELD")) {
|
|
49
|
+
child.kill();
|
|
50
|
+
throw new Error(`holder failed: ${await new Response(child.stderr).text()}`);
|
|
51
|
+
}
|
|
52
|
+
return async () => {
|
|
53
|
+
child.kill();
|
|
54
|
+
await child.exited;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function bounded(operation: () => unknown) {
|
|
59
|
+
const start = performance.now();
|
|
60
|
+
expect(() =>
|
|
61
|
+
withSqliteTurnDeadline(() => Math.max(0, 80 - (performance.now() - start)), operation),
|
|
62
|
+
).toThrow(/locked|busy/i);
|
|
63
|
+
// The connection allows 900 ms; a late turn has only 80 ms left for this wait.
|
|
64
|
+
expect(performance.now() - start).toBeLessThan(400);
|
|
65
|
+
expect(performance.now() - start).toBeGreaterThan(40);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
test("ordinary connections keep native cached SELECT methods", () => {
|
|
69
|
+
const db = new Database(":memory:");
|
|
70
|
+
const native = new NativeDatabase(":memory:");
|
|
71
|
+
try {
|
|
72
|
+
expect(db.prepare("SELECT 1").get).toBe(native.prepare("SELECT 1").get);
|
|
73
|
+
} finally {
|
|
74
|
+
db.close();
|
|
75
|
+
native.close();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("native deadlines read the connection timeout once, not per cached execution", () => {
|
|
80
|
+
let reads = 0;
|
|
81
|
+
const prepare = Database.prototype.prepare;
|
|
82
|
+
const spy = spyOn(Database.prototype, "prepare").mockImplementation(function (
|
|
83
|
+
this: Database,
|
|
84
|
+
sql: string,
|
|
85
|
+
) {
|
|
86
|
+
const statement = prepare.call(this, sql);
|
|
87
|
+
if (sql === "PRAGMA busy_timeout") {
|
|
88
|
+
const get = statement.get.bind(statement);
|
|
89
|
+
statement.get = (...args: unknown[]) => {
|
|
90
|
+
reads++;
|
|
91
|
+
return get(...args);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return statement;
|
|
95
|
+
});
|
|
96
|
+
let db: Database | undefined;
|
|
97
|
+
try {
|
|
98
|
+
db = createSqliteTurnDatabase(":memory:");
|
|
99
|
+
db.exec("PRAGMA busy_timeout=900");
|
|
100
|
+
const query = db.prepare("SELECT 1 AS value");
|
|
101
|
+
withSqliteTurnDeadline(
|
|
102
|
+
() => 80,
|
|
103
|
+
() => {
|
|
104
|
+
for (let i = 0; i < 100; i++) expect(query.get()).toEqual({ value: 1 });
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
expect(reads).toBe(1);
|
|
108
|
+
withSqliteTurnDeadline(
|
|
109
|
+
() => 80,
|
|
110
|
+
() => {
|
|
111
|
+
expect(db!.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 900 });
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
} finally {
|
|
115
|
+
db?.close();
|
|
116
|
+
spy.mockRestore();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("timeout cache follows scripts, prepared setters, comments and failed prefixes", async () => {
|
|
121
|
+
await fixture(async (db, path) => {
|
|
122
|
+
db.exec(
|
|
123
|
+
"PRAGMA main.busy_timeout(900); SELECT '; PRAGMA busy_timeout=0;'; -- PRAGMA busy_timeout=1",
|
|
124
|
+
);
|
|
125
|
+
db.prepare("PRAGMA busy_timeout=800").get();
|
|
126
|
+
expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 800 });
|
|
127
|
+
expect(() => db.exec("PRAGMA busy_timeout=700; SELECT * FROM missing_table")).toThrow();
|
|
128
|
+
const read = db.prepare("SELECT * FROM t");
|
|
129
|
+
const release = await hold(path);
|
|
130
|
+
try {
|
|
131
|
+
bounded(() => read.get());
|
|
132
|
+
expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 700 });
|
|
133
|
+
} finally {
|
|
134
|
+
await release();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("timeout cache follows prepare-time PRAGMA effects", () => {
|
|
140
|
+
const db = createSqliteTurnDatabase(":memory:");
|
|
141
|
+
const physicalTimeout = () =>
|
|
142
|
+
(db.prepare("PRAGMA busy_timeout").get() as { timeout: number }).timeout;
|
|
143
|
+
const harmlessRead = () =>
|
|
144
|
+
withSqliteTurnDeadline(
|
|
145
|
+
() => 80,
|
|
146
|
+
() => db.prepare("SELECT 1").get(),
|
|
147
|
+
);
|
|
148
|
+
try {
|
|
149
|
+
db.exec("PRAGMA busy_timeout=900");
|
|
150
|
+
const setter = db.prepare("PRAGMA busy_timeout=800");
|
|
151
|
+
expect(physicalTimeout()).toBe(800);
|
|
152
|
+
harmlessRead();
|
|
153
|
+
expect(physicalTimeout()).toBe(800);
|
|
154
|
+
db.exec("PRAGMA busy_timeout=700");
|
|
155
|
+
setter.get();
|
|
156
|
+
const before = physicalTimeout();
|
|
157
|
+
harmlessRead();
|
|
158
|
+
expect(physicalTimeout()).toBe(before);
|
|
159
|
+
const iterator = db.prepare("PRAGMA busy_timeout=600").iterate();
|
|
160
|
+
const beforeIteration = physicalTimeout();
|
|
161
|
+
harmlessRead();
|
|
162
|
+
expect(physicalTimeout()).toBe(beforeIteration);
|
|
163
|
+
db.exec("PRAGMA busy_timeout=500");
|
|
164
|
+
iterator.next();
|
|
165
|
+
const afterIteration = physicalTimeout();
|
|
166
|
+
harmlessRead();
|
|
167
|
+
expect(physicalTimeout()).toBe(afterIteration);
|
|
168
|
+
iterator.return?.();
|
|
169
|
+
} finally {
|
|
170
|
+
db.close();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
for (const method of ["get", "all", "run", "values", "iterate"] as const) {
|
|
175
|
+
test(`turn deadline bounds cached SELECT ${method}`, async () => {
|
|
176
|
+
await fixture(async (db, path) => {
|
|
177
|
+
const statement = db.prepare("SELECT * FROM t") as any;
|
|
178
|
+
const release = await hold(path);
|
|
179
|
+
try {
|
|
180
|
+
bounded(() =>
|
|
181
|
+
method === "iterate" ? statement.iterate().next() : statement[method](),
|
|
182
|
+
);
|
|
183
|
+
expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 900 });
|
|
184
|
+
} finally {
|
|
185
|
+
await release();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
test("turn deadline bounds native exec reads", async () => {
|
|
192
|
+
await fixture(async (db, path) => {
|
|
193
|
+
const release = await hold(path);
|
|
194
|
+
try {
|
|
195
|
+
bounded(() => db.exec("SELECT * FROM t"));
|
|
196
|
+
} finally {
|
|
197
|
+
await release();
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("turn deadline bounds native schema preparation", async () => {
|
|
203
|
+
await fixture(async (_db, path) => {
|
|
204
|
+
const release = await hold(path);
|
|
205
|
+
const fresh = createSqliteTurnDatabase(path);
|
|
206
|
+
fresh.exec("PRAGMA busy_timeout=900");
|
|
207
|
+
try {
|
|
208
|
+
bounded(() => fresh.prepare("SELECT * FROM t"));
|
|
209
|
+
} finally {
|
|
210
|
+
fresh.close();
|
|
211
|
+
await release();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("turn deadline bounds COMMIT waiting for a reader", async () => {
|
|
217
|
+
await fixture(async (db, path) => {
|
|
218
|
+
db.exec("BEGIN IMMEDIATE; UPDATE t SET value=2");
|
|
219
|
+
const release = await hold(path, true);
|
|
220
|
+
try {
|
|
221
|
+
bounded(() => db.exec("COMMIT"));
|
|
222
|
+
expect(db.inTransaction).toBe(true);
|
|
223
|
+
db.exec("ROLLBACK");
|
|
224
|
+
} finally {
|
|
225
|
+
await release();
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("turn deadline bounds execution inside a deferred transaction", async () => {
|
|
231
|
+
await fixture(async (db, path) => {
|
|
232
|
+
const read = db.prepare("SELECT * FROM t");
|
|
233
|
+
db.exec("BEGIN DEFERRED");
|
|
234
|
+
const release = await hold(path);
|
|
235
|
+
try {
|
|
236
|
+
bounded(() => read.get());
|
|
237
|
+
} finally {
|
|
238
|
+
db.exec("ROLLBACK");
|
|
239
|
+
await release();
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("turn deadline preserves zero-timeout probes and detached work", async () => {
|
|
245
|
+
await fixture(async (db, path) => {
|
|
246
|
+
const read = db.prepare("SELECT * FROM t");
|
|
247
|
+
const release = await hold(path);
|
|
248
|
+
try {
|
|
249
|
+
withSqliteTurnDeadline(
|
|
250
|
+
() => 80,
|
|
251
|
+
() => {
|
|
252
|
+
db.exec("PRAGMA busy_timeout=0");
|
|
253
|
+
expect(db.prepare("PRAGMA busy_timeout").get()).toEqual({ timeout: 0 });
|
|
254
|
+
expect(() => read.get()).toThrow(/locked|busy/i);
|
|
255
|
+
db.exec("PRAGMA busy_timeout=900");
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
const start = performance.now();
|
|
259
|
+
expect(() =>
|
|
260
|
+
withSqliteTurnDeadline(
|
|
261
|
+
() => 0,
|
|
262
|
+
() => withoutSqliteTransformPass(() => read.get()),
|
|
263
|
+
),
|
|
264
|
+
).toThrow(/locked|busy/i);
|
|
265
|
+
expect(performance.now() - start).toBeGreaterThan(700);
|
|
266
|
+
} finally {
|
|
267
|
+
await release();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
package/src/shared/sqlite.ts
CHANGED
|
@@ -403,11 +403,187 @@ export function getTagIdentityWriteGeneration(): number {
|
|
|
403
403
|
return tagIdentityWriteGeneration;
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
/**
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
406
|
+
/** Only Pi serving connections need a mirror of their native busy handler. */
|
|
407
|
+
const busyTimeoutStates = new WeakMap<Database, { current: number }>();
|
|
408
|
+
const BUSY_TIMEOUT_QUERY =
|
|
409
|
+
/^\s*PRAGMA\s+(?:(?:\w+|"[^"]+"|`[^`]+`|\[[^\]]+\])\s*\.\s*)?(?:busy_timeout|"busy_timeout"|`busy_timeout`|\[busy_timeout\])\s*;?\s*$/i;
|
|
410
|
+
const BUSY_TIMEOUT_SETTER =
|
|
411
|
+
/^\s*PRAGMA\s+(?:(?:\w+|"[^"]+"|`[^`]+`|\[[^\]]+\])\s*\.\s*)?(?:busy_timeout|"busy_timeout"|`busy_timeout`|\[busy_timeout\])\s*(?==|\()/i;
|
|
412
|
+
|
|
413
|
+
function literalBusyTimeout(sql: string): number | undefined {
|
|
414
|
+
const head = BUSY_TIMEOUT_SETTER.exec(sql);
|
|
415
|
+
if (!head) return undefined;
|
|
416
|
+
const match = /^(?:=\s*|\(\s*)(?:'(-?\d+)'|"(-?\d+)"|(-?\d+))\s*\)?\s*;?\s*$/.exec(
|
|
417
|
+
sql.slice(head[0].length),
|
|
418
|
+
);
|
|
419
|
+
if (!match) return undefined;
|
|
420
|
+
const value = Number(match[1] ?? match[2] ?? match[3]);
|
|
421
|
+
return value < 0 ? 0 : value <= 0x7fffffff ? value : undefined;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** Split timeout-setting scripts without mistaking quoted text or comments for pragmas. */
|
|
425
|
+
function busyTimeoutStatements(sql: string): string[] {
|
|
426
|
+
const statements: string[] = [];
|
|
427
|
+
let statement = "";
|
|
428
|
+
let quote = "";
|
|
429
|
+
for (let i = 0; i < sql.length; i++) {
|
|
430
|
+
const char = sql[i];
|
|
431
|
+
if (quote) {
|
|
432
|
+
statement += char;
|
|
433
|
+
if (char === quote) {
|
|
434
|
+
if (sql[i + 1] === quote) statement += sql[++i];
|
|
435
|
+
else quote = "";
|
|
436
|
+
}
|
|
437
|
+
} else if (char === "'" || char === '"' || char === "`" || char === "[") {
|
|
438
|
+
quote = char === "[" ? "]" : char;
|
|
439
|
+
statement += char;
|
|
440
|
+
} else if (char === "-" && sql[i + 1] === "-") {
|
|
441
|
+
while (i + 1 < sql.length && sql[i + 1] !== "\n") i++;
|
|
442
|
+
statement += " ";
|
|
443
|
+
} else if (char === "/" && sql[i + 1] === "*") {
|
|
444
|
+
i += 2;
|
|
445
|
+
while (i < sql.length && !(sql[i] === "*" && sql[i + 1] === "/")) i++;
|
|
446
|
+
i++;
|
|
447
|
+
statement += " ";
|
|
448
|
+
} else if (char === ";") {
|
|
449
|
+
statements.push(statement);
|
|
450
|
+
statement = "";
|
|
451
|
+
} else statement += char;
|
|
452
|
+
}
|
|
453
|
+
statements.push(statement);
|
|
454
|
+
return statements.filter((part) => part.trim().length > 0);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** Install the native deadline layer only on a connection that will serve Pi turns. */
|
|
458
|
+
function installNativeDeadlineRouting(db: BetterSqlite3.Database): void {
|
|
459
|
+
const rawExec = db.exec.bind(db);
|
|
460
|
+
const rawPrepare = db.prepare.bind(db);
|
|
461
|
+
const readTimeout = (): number => {
|
|
462
|
+
const statement = rawPrepare("PRAGMA busy_timeout");
|
|
463
|
+
try {
|
|
464
|
+
return Object.values(statement.get() as Record<string, number>)[0];
|
|
465
|
+
} finally {
|
|
466
|
+
(statement as Partial<FinalizableStatement>).finalize?.();
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
const timeoutState = { current: readTimeout() };
|
|
470
|
+
busyTimeoutStates.set(db, timeoutState);
|
|
471
|
+
const setBusyTimeout = (timeout: number): void => {
|
|
472
|
+
rawExec(`PRAGMA busy_timeout=${timeout}`);
|
|
473
|
+
timeoutState.current = timeout;
|
|
474
|
+
};
|
|
475
|
+
const withinDeadline = <T>(operation: () => T): T => {
|
|
476
|
+
const deadline = nativeWaitScope?.getStore();
|
|
477
|
+
if (!deadline?.active || !deadline.remainingMs) return operation();
|
|
478
|
+
const remaining = deadline.remainingMs();
|
|
479
|
+
if (remaining === Infinity) return operation();
|
|
480
|
+
const previous = timeoutState.current;
|
|
481
|
+
const timeout = Math.min(previous, Math.max(0, Math.floor(remaining)));
|
|
482
|
+
if (timeout === previous) return operation();
|
|
483
|
+
setBusyTimeout(timeout);
|
|
484
|
+
try {
|
|
485
|
+
return operation();
|
|
486
|
+
} finally {
|
|
487
|
+
setBusyTimeout(previous);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
Object.defineProperty(db, "exec", {
|
|
491
|
+
configurable: true,
|
|
492
|
+
writable: true,
|
|
493
|
+
value: (sql: string) => {
|
|
494
|
+
const parts = /\bbusy_timeout\b/i.test(sql) ? busyTimeoutStatements(sql) : [];
|
|
495
|
+
if (parts.length === 1 && BUSY_TIMEOUT_QUERY.test(parts[0])) return rawExec(sql);
|
|
496
|
+
const singleSetter = parts.length === 1 && BUSY_TIMEOUT_SETTER.test(parts[0]);
|
|
497
|
+
const setting = singleSetter ? literalBusyTimeout(parts[0]) : undefined;
|
|
498
|
+
if (singleSetter) {
|
|
499
|
+
const result = rawExec(sql);
|
|
500
|
+
timeoutState.current = setting ?? readTimeout();
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
if (!/\bbusy_timeout\b/i.test(sql)) return withinDeadline(() => rawExec(sql));
|
|
504
|
+
const setters = parts.filter((part) => BUSY_TIMEOUT_SETTER.test(part));
|
|
505
|
+
return withinDeadline(() => {
|
|
506
|
+
try {
|
|
507
|
+
const result = rawExec(sql);
|
|
508
|
+
if (setters.length)
|
|
509
|
+
timeoutState.current =
|
|
510
|
+
literalBusyTimeout(setters[setters.length - 1]) ?? readTimeout();
|
|
511
|
+
return result;
|
|
512
|
+
} catch (error) {
|
|
513
|
+
// exec can apply a timeout-setting prefix before a later statement
|
|
514
|
+
// fails. Only an ambiguous setting needs a native readback.
|
|
515
|
+
if (setters.length) timeoutState.current = readTimeout();
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
// Timeout introspection cannot wait on a database lock. Its getter must
|
|
522
|
+
// report the configured timeout, not a temporary cap that a caller might save.
|
|
523
|
+
const nativePrepare = (sql: string) =>
|
|
524
|
+
BUSY_TIMEOUT_QUERY.test(sql) || BUSY_TIMEOUT_SETTER.test(sql)
|
|
525
|
+
? rawPrepare(sql)
|
|
526
|
+
: withinDeadline(() => rawPrepare(sql));
|
|
527
|
+
Object.defineProperty(db, "prepare", {
|
|
528
|
+
configurable: true,
|
|
529
|
+
writable: true,
|
|
530
|
+
value: (sql: string) => {
|
|
531
|
+
// biome-ignore lint/suspicious/noExplicitAny: Bun adds values() to the common statement interface.
|
|
532
|
+
const statement: any = nativePrepare(sql);
|
|
533
|
+
const normalized = /\bbusy_timeout\b/i.test(sql)
|
|
534
|
+
? (busyTimeoutStatements(sql)[0] ?? sql)
|
|
535
|
+
: sql;
|
|
536
|
+
const setting = literalBusyTimeout(normalized);
|
|
537
|
+
const timeoutSetter = BUSY_TIMEOUT_SETTER.test(normalized);
|
|
538
|
+
const timeoutQuery = BUSY_TIMEOUT_QUERY.test(normalized);
|
|
539
|
+
// SQLite applies this PRAGMA while preparing it, before get/run.
|
|
540
|
+
if (timeoutSetter) timeoutState.current = setting ?? readTimeout();
|
|
541
|
+
for (const method of ["run", "get", "all", "values"] as const) {
|
|
542
|
+
const execute = statement[method]?.bind(statement);
|
|
543
|
+
if (!execute) continue;
|
|
544
|
+
Object.defineProperty(statement, method, {
|
|
545
|
+
configurable: true,
|
|
546
|
+
// biome-ignore lint/suspicious/noExplicitAny: retain native binding overloads.
|
|
547
|
+
value: (...args: any[]) => {
|
|
548
|
+
if (timeoutQuery) return execute(...args);
|
|
549
|
+
if (!timeoutSetter) return withinDeadline(() => execute(...args));
|
|
550
|
+
const result = execute(...args);
|
|
551
|
+
timeoutState.current = readTimeout();
|
|
552
|
+
return result;
|
|
553
|
+
},
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
if (typeof statement.iterate === "function") {
|
|
557
|
+
const iterate = statement.iterate.bind(statement);
|
|
558
|
+
Object.defineProperty(statement, "iterate", {
|
|
559
|
+
configurable: true,
|
|
560
|
+
// biome-ignore lint/suspicious/noExplicitAny: retain native binding overloads.
|
|
561
|
+
value: (...args: any[]) => {
|
|
562
|
+
const iterator =
|
|
563
|
+
!timeoutSetter && !timeoutQuery
|
|
564
|
+
? withinDeadline(() => iterate(...args))
|
|
565
|
+
: iterate(...args);
|
|
566
|
+
const next = iterator.next.bind(iterator);
|
|
567
|
+
if (timeoutSetter) timeoutState.current = readTimeout();
|
|
568
|
+
iterator.next = () => {
|
|
569
|
+
if (timeoutQuery) return next();
|
|
570
|
+
if (!timeoutSetter) return withinDeadline(next);
|
|
571
|
+
const result = next();
|
|
572
|
+
timeoutState.current = readTimeout();
|
|
573
|
+
return result;
|
|
574
|
+
};
|
|
575
|
+
return iterator;
|
|
576
|
+
},
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
return statement;
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** Route native Bun and Node transactions through the same acquisition retry.
|
|
585
|
+
* Native Bun wrappers execute BEGIN internally, so both runtimes use this wrapper.
|
|
586
|
+
* Deadline routing, when selected, is installed underneath it at connection open. */
|
|
411
587
|
function installTransactionRouting(db: BetterSqlite3.Database, readonly: boolean): void {
|
|
412
588
|
const nativeExec = db.exec.bind(db);
|
|
413
589
|
Object.defineProperty(db, "exec", {
|
|
@@ -565,6 +741,7 @@ function trackSqliteConnection(
|
|
|
565
741
|
db: BetterSqlite3.Database,
|
|
566
742
|
filename: unknown,
|
|
567
743
|
options: unknown,
|
|
744
|
+
turnDeadlines = false,
|
|
568
745
|
): BetterSqlite3.Database {
|
|
569
746
|
const originalClose = db.close.bind(db) as (...args: unknown[]) => unknown;
|
|
570
747
|
const sequence = nextSqliteConnectionSequence++;
|
|
@@ -582,6 +759,7 @@ function trackSqliteConnection(
|
|
|
582
759
|
((options as { readonly?: unknown }).readonly === true ||
|
|
583
760
|
(options as { readOnly?: unknown }).readOnly === true),
|
|
584
761
|
};
|
|
762
|
+
if (turnDeadlines) installNativeDeadlineRouting(db);
|
|
585
763
|
installTransactionRouting(db, metadata.readonly);
|
|
586
764
|
Object.defineProperty(db, "close", {
|
|
587
765
|
configurable: true,
|
|
@@ -664,6 +842,18 @@ function buildNodeSqliteDatabaseClass(DatabaseSync: any): typeof BetterSqlite3 {
|
|
|
664
842
|
|
|
665
843
|
export const Database: typeof BetterSqlite3 = TrackedDatabase;
|
|
666
844
|
|
|
845
|
+
/** Open a Pi foreground connection with native-deadline routing before statements are prepared. */
|
|
846
|
+
export function createSqliteTurnDatabase(
|
|
847
|
+
filename?: string | Buffer,
|
|
848
|
+
options?: BetterSqlite3.Options,
|
|
849
|
+
): Database {
|
|
850
|
+
return trackSqliteConnection(new DatabaseImpl(filename, options), filename, options, true);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
export function hasSqliteTurnDeadlineRouting(db: Database): boolean {
|
|
854
|
+
return busyTimeoutStates.has(db);
|
|
855
|
+
}
|
|
856
|
+
|
|
667
857
|
function immutableDatabaseImplementation(): typeof BetterSqlite3 {
|
|
668
858
|
if (!isBun) return DatabaseImpl;
|
|
669
859
|
if (immutableDatabaseImpl) return immutableDatabaseImpl;
|
|
@@ -728,6 +918,10 @@ function pragmaValue(db: BetterSqlite3.Database, name: string): unknown {
|
|
|
728
918
|
}
|
|
729
919
|
|
|
730
920
|
function pragmaNumber(db: BetterSqlite3.Database, name: string): number | null {
|
|
921
|
+
if (name === "busy_timeout") {
|
|
922
|
+
const state = busyTimeoutStates.get(db);
|
|
923
|
+
if (state) return state.current;
|
|
924
|
+
}
|
|
731
925
|
try {
|
|
732
926
|
const value = pragmaValue(db, name);
|
|
733
927
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
@@ -934,6 +1128,38 @@ export function prepareCachedStatement(db: Database, sql: string): Statement {
|
|
|
934
1128
|
}
|
|
935
1129
|
|
|
936
1130
|
const privilegeDepth = new WeakMap<Database, number>();
|
|
1131
|
+
interface NativeWaitLease {
|
|
1132
|
+
active: boolean;
|
|
1133
|
+
remainingMs?: () => number;
|
|
1134
|
+
}
|
|
1135
|
+
let nativeWaitScope: AsyncLocalStorage<NativeWaitLease | undefined> | undefined;
|
|
1136
|
+
|
|
1137
|
+
function releaseNativeWaitLease(lease: NativeWaitLease): void {
|
|
1138
|
+
lease.active = false;
|
|
1139
|
+
// Detached descendants can retain their async-local lease after the turn.
|
|
1140
|
+
// Do not retain the completed request through its deadline callback.
|
|
1141
|
+
lease.remainingMs = undefined;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/** Bound native lock waits for an awaited turn, without changing background timeouts. */
|
|
1145
|
+
export function withSqliteTurnDeadline<T>(remainingMs: () => number, operation: () => T): T {
|
|
1146
|
+
const scope = (nativeWaitScope ??= new AsyncLocalStorage<NativeWaitLease | undefined>());
|
|
1147
|
+
const lease: NativeWaitLease = { active: true, remainingMs };
|
|
1148
|
+
return scope.run(lease, () => {
|
|
1149
|
+
try {
|
|
1150
|
+
const result = operation();
|
|
1151
|
+
if (result && typeof (result as { then?: unknown }).then === "function")
|
|
1152
|
+
return Promise.resolve(result).finally(
|
|
1153
|
+
releaseNativeWaitLease.bind(null, lease),
|
|
1154
|
+
) as T;
|
|
1155
|
+
releaseNativeWaitLease(lease);
|
|
1156
|
+
return result;
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
releaseNativeWaitLease(lease);
|
|
1159
|
+
throw error;
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
937
1163
|
const transformPassScope = new AsyncLocalStorage<
|
|
938
1164
|
| {
|
|
939
1165
|
active: boolean;
|
|
@@ -947,7 +1173,8 @@ const backgroundWriterScope = new AsyncLocalStorage<boolean>();
|
|
|
947
1173
|
|
|
948
1174
|
/** Opt in only a writer that can safely defer its work after a busy acquisition. */
|
|
949
1175
|
export function withSqliteBackgroundWriter<T>(operation: () => T): T {
|
|
950
|
-
return backgroundWriterScope.run(true, operation);
|
|
1176
|
+
if (!nativeWaitScope?.getStore()) return backgroundWriterScope.run(true, operation);
|
|
1177
|
+
return nativeWaitScope.run(undefined, () => backgroundWriterScope.run(true, operation));
|
|
951
1178
|
}
|
|
952
1179
|
|
|
953
1180
|
/** Only an awaited foreground transform may spend the extra acquisition budget.
|
|
@@ -984,7 +1211,8 @@ export function withSqliteTransformPass<T>(operation: () => T): T {
|
|
|
984
1211
|
|
|
985
1212
|
/** Start detached maintenance work without borrowing a foreground pass's budget. */
|
|
986
1213
|
export function withoutSqliteTransformPass<T>(operation: () => T): T {
|
|
987
|
-
return transformPassScope.run(undefined, operation);
|
|
1214
|
+
if (!nativeWaitScope?.getStore()) return transformPassScope.run(undefined, operation);
|
|
1215
|
+
return nativeWaitScope.run(undefined, () => transformPassScope.run(undefined, operation));
|
|
988
1216
|
}
|
|
989
1217
|
|
|
990
1218
|
/** Attach the adapter's existing generation/cancellation/deadline guard to writes. */
|
|
@@ -63,6 +63,7 @@ import { estimateTokens } from "../hooks/magic-context/read-session-formatting";
|
|
|
63
63
|
import {
|
|
64
64
|
byteSize,
|
|
65
65
|
prependTag,
|
|
66
|
+
stripPersistedAssistantText,
|
|
66
67
|
stripTagPrefix,
|
|
67
68
|
stripWellFormedLeadingTagPrefix,
|
|
68
69
|
} from "../hooks/magic-context/tag-content-primitives";
|
|
@@ -75,6 +76,8 @@ import type { Transcript, TranscriptMessage, TranscriptPart } from "./transcript
|
|
|
75
76
|
export { TEXT_TAG_IDENTITY_MARKER };
|
|
76
77
|
|
|
77
78
|
export interface TagTranscriptOptions {
|
|
79
|
+
/** Keep tag-only assistant slots inert when their original text must survive stripping. */
|
|
80
|
+
preserveTagOnlyAssistantText?: boolean;
|
|
78
81
|
/**
|
|
79
82
|
* When true, skip injecting `§N§` prefix into visible text. Tags
|
|
80
83
|
* still get assigned in the DB so historian/drops can reference
|
|
@@ -416,6 +419,7 @@ export function tagTranscript(
|
|
|
416
419
|
db,
|
|
417
420
|
targets,
|
|
418
421
|
skipPrefixInjection,
|
|
422
|
+
preserveTagOnlyAssistantText: options.preserveTagOnlyAssistantText,
|
|
419
423
|
entryFingerprint: options.entryFingerprintByMessageId?.get(messageId) ?? null,
|
|
420
424
|
reuseIdentity: reuseIdentity || contentDerivedTextIds !== undefined,
|
|
421
425
|
timing,
|
|
@@ -996,6 +1000,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
996
1000
|
}
|
|
997
1001
|
|
|
998
1002
|
interface TagTextPartArgs {
|
|
1003
|
+
preserveTagOnlyAssistantText?: boolean;
|
|
999
1004
|
sessionId: string;
|
|
1000
1005
|
message: { info: { id?: string; role: string } };
|
|
1001
1006
|
messageId: string;
|
|
@@ -1022,7 +1027,10 @@ function tagTextPart(args: TagTextPartArgs): void {
|
|
|
1022
1027
|
// into a tag-bearing part that later strip logic could mistake for content.
|
|
1023
1028
|
if (
|
|
1024
1029
|
args.message.info.role === "assistant" &&
|
|
1025
|
-
stripWellFormedLeadingTagPrefix(text).trim().length === 0
|
|
1030
|
+
(stripWellFormedLeadingTagPrefix(text).trim().length === 0 ||
|
|
1031
|
+
(args.preserveTagOnlyAssistantText &&
|
|
1032
|
+
text.includes("§") &&
|
|
1033
|
+
stripPersistedAssistantText(text).length === 0))
|
|
1026
1034
|
) {
|
|
1027
1035
|
if (args.timing) args.timing.identity += performance.now() - identityStart;
|
|
1028
1036
|
return;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
runManagedWrapup2
|
|
3
|
-
} from "./index-4n7w9rad.js";
|
|
4
|
-
import"./index-jfh3cktr.js";
|
|
5
|
-
import"./index-v7r4td4c.js";
|
|
6
|
-
import"./index-616rsjrv.js";
|
|
7
|
-
import"./index-1pefp8ar.js";
|
|
8
|
-
import"./index-48stjd56.js";
|
|
9
|
-
import"./index-sd3egckz.js";
|
|
10
|
-
import"./index-jr5na10t.js";
|
|
11
|
-
import"./index-2yhtnfcf.js";
|
|
12
|
-
import"./index-y4a3nhtk.js";
|
|
13
|
-
import"./index-4dhv7mqz.js";
|
|
14
|
-
import"./index-spwjbkc7.js";
|
|
15
|
-
import"./index-kc9pn495.js";
|
|
16
|
-
import"./index-qt1qqasc.js";
|
|
17
|
-
import"./index-x4k3f4mf.js";
|
|
18
|
-
import"./index-305y133n.js";
|
|
19
|
-
import"./index-1zx87sdm.js";
|
|
20
|
-
import"./index-11zf2wwc.js";
|
|
21
|
-
import"./index-x8a7jmkh.js";
|
|
22
|
-
import"./index-rrv26qjz.js";
|
|
23
|
-
import"./index-0wthn7fp.js";
|
|
24
|
-
import"./index-wgypt3cs.js";
|
|
25
|
-
import"./index-c4b76d4t.js";
|
|
26
|
-
import"./index-153r2q6z.js";
|
|
27
|
-
import"./index-adk2z5d8.js";
|
|
28
|
-
import"./index-rmd0069m.js";
|
|
29
|
-
import"./index-fb3zxn06.js";
|
|
30
|
-
import"./index-pnjwa6qa.js";
|
|
31
|
-
import"./index-vkc1ca9p.js";
|
|
32
|
-
import"./index-jmycsey7.js";
|
|
33
|
-
import"./index-fr0wbhdn.js";
|
|
34
|
-
import"./index-c7phnbvk.js";
|
|
35
|
-
import"./index-j76ah6d7.js";
|
|
36
|
-
import"./index-x9k3mqrf.js";
|
|
37
|
-
import"./index-58e3y90n.js";
|
|
38
|
-
import"./index-35qc6606.js";
|
|
39
|
-
import"./index-v0p37hm2.js";
|
|
40
|
-
import"./index-t5pck3ss.js";
|
|
41
|
-
import"./index-b88dwwrh.js";
|
|
42
|
-
import"./index-1zx2pa5d.js";
|
|
43
|
-
import"./index-h7tgn49j.js";
|
|
44
|
-
import"./index-g2q4htwp.js";
|
|
45
|
-
import"./index-pavzsdc6.js";
|
|
46
|
-
import"./index-cpjk5jd2.js";
|
|
47
|
-
export {
|
|
48
|
-
runManagedWrapup2 as runManagedWrapup
|
|
49
|
-
};
|