@cortexkit/opencode-magic-context 0.46.0 → 0.47.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/auto-search-worker.js +115 -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/schema/magic-context.d.ts +15 -7
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/{conflict-warning-hook-7fj2b9jw.js → conflict-warning-hook-z44ynfz7.js} +5 -4
- package/dist/{context-8dkpszy5.js → context-wq4ffwyb.js} +164 -37
- package/dist/{derive-budgets-7nj7r1rv.js → derive-budgets-ke12jnnh.js} +1 -1
- package/dist/embedding-worker.js +2 -2
- package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/task-gates.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/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 +2 -0
- package/dist/features/magic-context/search.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/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.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +7 -2
- 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/transform-decision-log.d.ts +3 -1
- package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
- package/dist/hooks/magic-context/active-anthropic-turn.d.ts +8 -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 +69 -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/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 +2 -1
- package/dist/hooks/magic-context/hook.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/module-wire.d.ts.map +1 -1
- 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/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/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 +4 -0
- package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +7 -3
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +13 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- 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 +3 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-0d3g3461.js → index-0c82743p.js} +2 -1
- package/dist/{index-e2dtv0ra.js → index-5a2r0p9f.js} +3 -3
- package/dist/{index-p821bjdb.js → index-6d9e1m08.js} +4726 -11050
- package/dist/{index-majxda46.js → index-6wkcswft.js} +1 -1
- package/dist/index-92bp57hg.js +2063 -0
- package/dist/{index-sfnj6h8h.js → index-9pk9exrb.js} +3 -3
- package/dist/{index-7qw742ev.js → index-adwfkpex.js} +5 -24
- package/dist/{index-3h0ybwah.js → index-bb36vgr3.js} +80 -12
- package/dist/{index-k4404na5.js → index-cwf6s3ay.js} +1072 -2422
- package/dist/index-fnn5pjtt.js +6554 -0
- package/dist/{index-j7fvnjye.js → index-gg8j07zz.js} +1 -1
- package/dist/{index-yasgb8ej.js → index-m5fe6jch.js} +9 -3
- package/dist/{v2/server-g0fjrkta.js → index-rrbbspwh.js} +42 -27
- package/dist/index-rrrsepyw.js +3464 -0
- package/dist/{index-qr6r7akd.js → index-w2t4ah5j.js} +950 -502
- package/dist/{v2/server-fg34g6a5.js → index-wx8a6bw3.js} +48 -8
- package/dist/{index-r5hmbx3t.js → index-x3kwrmqk.js} +146 -3571
- package/dist/index-yr3zj7e7.js +23 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +624 -65
- package/dist/{message-time-backfill-68gj6t3e.js → message-time-backfill-r5epkzj3.js} +1 -1
- package/dist/migration-worker.js +7 -5
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +2 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/host-stall-profiler-report.d.ts +67 -0
- package/dist/plugin/host-stall-profiler-report.d.ts.map +1 -0
- package/dist/plugin/host-stall-profiler.d.ts +96 -0
- package/dist/plugin/host-stall-profiler.d.ts.map +1 -0
- package/dist/plugin/messages-transform.d.ts +21 -6
- package/dist/plugin/messages-transform.d.ts.map +1 -1
- package/dist/{read-session-chunk-egz7r68d.js → read-session-chunk-4jey7hpt.js} +4 -4
- package/dist/{recomp-orchestrator-ejq7ex5d.js → recomp-orchestrator-rervg6q4.js} +13 -10
- package/dist/{send-session-notification-dyw42dtr.js → send-session-notification-0m68ms3n.js} +4 -3
- package/dist/shared/auto-search-lifecycle.d.ts +7 -0
- package/dist/shared/auto-search-lifecycle.d.ts.map +1 -0
- 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/sqlite.d.ts +16 -1
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/status-view.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/tools/ctx-expand/render.d.ts.map +1 -1
- package/dist/{transform-postprocess-phase-tz3pat2p.js → transform-postprocess-phase-z5hzpsxv.js} +13 -10
- package/dist/v2/auto-search-worker.js +115 -0
- package/dist/v2/{context-8vgtb6sz.js → context-14v1hr8b.js} +4918 -5760
- package/dist/v2/{derive-budgets-kdgp4s3r.js → derive-budgets-nc9avxz8.js} +2 -2
- package/dist/v2/embedding-worker.js +3 -3
- package/dist/v2/hidden-child-native.d.ts +7 -0
- package/dist/v2/hidden-child-native.d.ts.map +1 -1
- package/dist/v2/hidden-child-record.d.ts +8 -0
- package/dist/v2/hidden-child-record.d.ts.map +1 -1
- package/dist/v2/hidden-completion.d.ts.map +1 -1
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/dream-trigger.d.ts +15 -1
- package/dist/v2/hooks/dream-trigger.d.ts.map +1 -1
- package/dist/v2/hooks/hidden-child.d.ts +54 -3
- package/dist/v2/hooks/hidden-child.d.ts.map +1 -1
- package/dist/v2/hooks/types.d.ts +12 -1
- package/dist/v2/hooks/types.d.ts.map +1 -1
- package/dist/v2/{message-time-backfill-r6qkem1x.js → message-time-backfill-1f9tzvcs.js} +1 -1
- package/dist/v2/migration-worker.js +7 -5
- package/dist/v2/{read-session-chunk-8vjp6581.js → read-session-chunk-gyyv7w2r.js} +4 -4
- package/dist/v2/{recomp-orchestrator-5xj0yzyk.js → recomp-orchestrator-42vjba7p.js} +17 -14
- package/dist/v2/{send-session-notification-qs0j78v6.js → send-session-notification-c0wtscea.js} +5 -4
- package/dist/v2/server-3trjk1hx.js +6360 -0
- package/dist/v2/{server-xtp687da.js → server-50hvkg3v.js} +9 -3
- package/dist/v2/{server-y8maxeft.js → server-cpepxy2q.js} +2 -1
- package/dist/v2/server-d0sraw7m.js +3403 -0
- package/dist/v2/server-e0c1b7a7.js +2063 -0
- package/dist/v2/{server-mcfawjgc.js → server-efhwzx05.js} +5 -24
- package/dist/v2/{server-3n4q9xme.js → server-fcdehk1s.js} +1 -1
- package/dist/{index-q4yp4b52.js → v2/server-fq7kq32k.js} +42 -27
- package/dist/v2/{server-vnh7r35f.js → server-nf82q6pt.js} +2459 -8603
- package/dist/v2/{server-frbm99q5.js → server-njt3np1k.js} +99 -3465
- package/dist/v2/{server-qp5hx1ad.js → server-nm9n3aqq.js} +107 -12
- package/dist/v2/{server-p2en78c8.js → server-rz514f14.js} +7 -2
- package/dist/v2/{server-wvjvfhck.js → server-vd4e7174.js} +1 -1
- package/dist/{index-fg34g6a5.js → v2/server-wx8a6bw3.js} +48 -8
- package/dist/v2/{server-4663ejam.js → server-y10vbq6y.js} +1 -1
- package/dist/v2/server-yr3zj7e7.js +23 -0
- package/dist/v2/server.js +5 -5
- package/dist/v2/{wrapup-orchestrator-r5t1pyj8.js → wrapup-orchestrator-nbz854vc.js} +16 -13
- package/dist/wrapup-orchestrator-b2dztrar.js +29 -0
- package/package.json +6 -5
- package/src/config/index.ts +6 -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/schema/magic-context.test.ts +4 -3
- package/src/config/schema/magic-context.ts +23 -6
- package/src/shared/auto-search-lifecycle.ts +27 -0
- 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/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.ts +84 -14
- 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/window-geometry.ts +10 -0
- package/dist/wrapup-orchestrator-kw3x3myw.js +0 -26
- /package/dist/{index-en1k8h8m.js → index-5prbx026.js} +0 -0
- /package/dist/v2/{index-en1k8h8m.js → index-5prbx026.js} +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { expect, spyOn, test } from "bun:test";
|
|
2
|
+
import { openDatabase } from "../features/magic-context/storage-db";
|
|
3
|
+
import { getOrCreateSessionMeta, updateSessionMeta } from "../features/magic-context/storage-meta";
|
|
4
|
+
import { recordToolDefinition } from "../features/magic-context/tool-definition-tokens";
|
|
5
|
+
import { captureLkgSlot } from "../hooks/magic-context/lkg-replay";
|
|
6
|
+
import { lkgReplayFits } from "../hooks/magic-context/lkg-replay-fit";
|
|
7
|
+
import { resetLkgSlotsForTest } from "../hooks/magic-context/lkg-slot";
|
|
8
|
+
import type { MessageLike } from "../hooks/magic-context/transform-operations";
|
|
9
|
+
import { createMessagesTransformHandler } from "../plugin/messages-transform";
|
|
10
|
+
import { refreshModelLimitsFromApi } from "./models-dev-cache";
|
|
11
|
+
import { Database, SqliteAcquisitionBusyError, withAsyncPrivilegedWriter } from "./sqlite";
|
|
12
|
+
|
|
13
|
+
function fixture() {
|
|
14
|
+
const db = new Database(":memory:");
|
|
15
|
+
db.exec("CREATE TABLE context_privilege_state(id INTEGER PRIMARY KEY, enabled INTEGER)");
|
|
16
|
+
db.exec("CREATE TABLE writes(value INTEGER)");
|
|
17
|
+
return db;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test("review: shared async admission rolls back callback busy without retrying side effects", async () => {
|
|
21
|
+
const db = fixture();
|
|
22
|
+
let callbacks = 0;
|
|
23
|
+
let recoveries = 0;
|
|
24
|
+
try {
|
|
25
|
+
await expect(
|
|
26
|
+
withAsyncPrivilegedWriter(
|
|
27
|
+
db,
|
|
28
|
+
() => {
|
|
29
|
+
callbacks++;
|
|
30
|
+
db.prepare("INSERT INTO writes VALUES (1)").run();
|
|
31
|
+
throw new SqliteAcquisitionBusyError(new Error("nested acquisition busy"));
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
beforeRetry: () => {
|
|
35
|
+
recoveries++;
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
),
|
|
39
|
+
).rejects.toThrow("SQLite writer acquisition remained busy");
|
|
40
|
+
expect(callbacks).toBe(1);
|
|
41
|
+
expect(recoveries).toBe(0);
|
|
42
|
+
expect(db.prepare("SELECT * FROM writes").all()).toEqual([]);
|
|
43
|
+
expect(db.inTransaction).toBe(false);
|
|
44
|
+
} finally {
|
|
45
|
+
db.close();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("review: default OpenCode empty admission retries BEGIN only and preserves output bytes", async () => {
|
|
50
|
+
const db = fixture();
|
|
51
|
+
const exec = db.exec.bind(db);
|
|
52
|
+
let attempts = 0;
|
|
53
|
+
const spy = spyOn(db, "exec").mockImplementation((sql) => {
|
|
54
|
+
if (sql === "BEGIN IMMEDIATE" && ++attempts === 1)
|
|
55
|
+
throw Object.assign(new Error("busy"), { code: "SQLITE_BUSY" });
|
|
56
|
+
return exec(sql);
|
|
57
|
+
});
|
|
58
|
+
try {
|
|
59
|
+
const output = [{ role: "user", content: "§1§ managed input" }];
|
|
60
|
+
const before = JSON.stringify(output);
|
|
61
|
+
await withAsyncPrivilegedWriter(db, () => undefined);
|
|
62
|
+
expect(attempts).toBe(2);
|
|
63
|
+
expect(JSON.stringify(output)).toBe(before);
|
|
64
|
+
expect(db.inTransaction).toBe(false);
|
|
65
|
+
} finally {
|
|
66
|
+
spy.mockRestore();
|
|
67
|
+
db.close();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("review: COMMIT busy after empty admission is surfaced once and transaction is released", async () => {
|
|
72
|
+
const db = fixture();
|
|
73
|
+
const exec = db.exec.bind(db);
|
|
74
|
+
let callbacks = 0;
|
|
75
|
+
let commits = 0;
|
|
76
|
+
const spy = spyOn(db, "exec").mockImplementation((sql) => {
|
|
77
|
+
if (sql === "COMMIT") {
|
|
78
|
+
commits++;
|
|
79
|
+
throw Object.assign(new Error("reader holds rollback-journal lock"), {
|
|
80
|
+
code: "SQLITE_BUSY",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return exec(sql);
|
|
84
|
+
});
|
|
85
|
+
try {
|
|
86
|
+
await expect(
|
|
87
|
+
withAsyncPrivilegedWriter(db, () => {
|
|
88
|
+
callbacks++;
|
|
89
|
+
}),
|
|
90
|
+
).rejects.toThrow("reader holds rollback-journal lock");
|
|
91
|
+
expect(callbacks).toBe(1);
|
|
92
|
+
expect(commits).toBe(1);
|
|
93
|
+
expect(db.inTransaction).toBe(false);
|
|
94
|
+
} finally {
|
|
95
|
+
spy.mockRestore();
|
|
96
|
+
db.close();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("review: OpenCode tries a valid saved request before backed-off writer retry", async () => {
|
|
101
|
+
resetLkgSlotsForTest();
|
|
102
|
+
const db = openDatabase();
|
|
103
|
+
if (!db) throw new Error("isolated test database unavailable");
|
|
104
|
+
const sessionId = "review-opencode-order";
|
|
105
|
+
const model = { providerID: "openai", modelID: "gpt-4.1" };
|
|
106
|
+
await refreshModelLimitsFromApi({
|
|
107
|
+
config: {
|
|
108
|
+
providers: async () => ({
|
|
109
|
+
data: {
|
|
110
|
+
providers: [
|
|
111
|
+
{
|
|
112
|
+
id: model.providerID,
|
|
113
|
+
models: {
|
|
114
|
+
[model.modelID]: { limit: { context: 1000000, output: 8192 } },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
getOrCreateSessionMeta(db, sessionId);
|
|
123
|
+
updateSessionMeta(db, sessionId, { systemPromptTokens: 100 });
|
|
124
|
+
recordToolDefinition(model.providerID, model.modelID, undefined, "read", "read file", {
|
|
125
|
+
type: "object",
|
|
126
|
+
});
|
|
127
|
+
const messages = [
|
|
128
|
+
{
|
|
129
|
+
info: { id: "u0", role: "user", sessionID: sessionId, model },
|
|
130
|
+
parts: [{ type: "text", text: "old raw history" }],
|
|
131
|
+
},
|
|
132
|
+
] as MessageLike[];
|
|
133
|
+
const managed = structuredClone(messages);
|
|
134
|
+
(managed[0].parts[0] as { text: string }).text = "saved managed prefix";
|
|
135
|
+
expect(
|
|
136
|
+
captureLkgSlot({
|
|
137
|
+
sessionId,
|
|
138
|
+
input: messages,
|
|
139
|
+
output: managed,
|
|
140
|
+
modelKey: "openai/gpt-4.1",
|
|
141
|
+
providerKey: "openai",
|
|
142
|
+
}),
|
|
143
|
+
).toBe(true);
|
|
144
|
+
expect(
|
|
145
|
+
lkgReplayFits({
|
|
146
|
+
db,
|
|
147
|
+
sessionId,
|
|
148
|
+
messages: managed,
|
|
149
|
+
model,
|
|
150
|
+
modelKey: "openai/gpt-4.1",
|
|
151
|
+
systemPromptTokens: 100,
|
|
152
|
+
}).fits,
|
|
153
|
+
).toBe(true);
|
|
154
|
+
const exec = db.exec.bind(db);
|
|
155
|
+
let attempts = 0;
|
|
156
|
+
let callbacks = 0;
|
|
157
|
+
const spy = spyOn(db, "exec").mockImplementation((sql) => {
|
|
158
|
+
if (sql === "BEGIN IMMEDIATE" && ++attempts === 1)
|
|
159
|
+
throw Object.assign(new Error("writer busy"), { code: "SQLITE_BUSY" });
|
|
160
|
+
return exec(sql);
|
|
161
|
+
});
|
|
162
|
+
try {
|
|
163
|
+
const output = { messages: structuredClone(messages) };
|
|
164
|
+
const handler = createMessagesTransformHandler({
|
|
165
|
+
magicContext: {
|
|
166
|
+
"experimental.chat.messages.transform": async (_input, result) => {
|
|
167
|
+
callbacks++;
|
|
168
|
+
(result.messages[0].parts[0] as { text: string }).text =
|
|
169
|
+
"recomputed after retry";
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
await handler({}, output as Parameters<typeof handler>[1]);
|
|
174
|
+
expect({ callbacks, attempts, messages: output.messages }).toEqual({
|
|
175
|
+
callbacks: 0,
|
|
176
|
+
attempts: 1,
|
|
177
|
+
messages: managed,
|
|
178
|
+
});
|
|
179
|
+
} finally {
|
|
180
|
+
spy.mockRestore();
|
|
181
|
+
resetLkgSlotsForTest();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import {
|
|
6
6
|
Database,
|
|
7
|
+
SqliteAcquisitionBusyError,
|
|
7
8
|
withAsyncPrivilegedWriter,
|
|
8
9
|
withPrivilegedWriter,
|
|
9
10
|
withSqliteTransformPass,
|
|
@@ -11,6 +12,23 @@ import {
|
|
|
11
12
|
import { startSqliteWriteLocker } from "./sqlite-write-locker-test-support";
|
|
12
13
|
import { createTestTempDirFromPath } from "./test-temp-dir";
|
|
13
14
|
|
|
15
|
+
test("async writer never retries callback acquisition errors after work began", async () => {
|
|
16
|
+
const db = new Database(":memory:");
|
|
17
|
+
db.exec("CREATE TABLE context_privilege_state(id INTEGER PRIMARY KEY, enabled INTEGER)");
|
|
18
|
+
let calls = 0;
|
|
19
|
+
try {
|
|
20
|
+
await expect(
|
|
21
|
+
withAsyncPrivilegedWriter(db, () => {
|
|
22
|
+
calls++;
|
|
23
|
+
throw new SqliteAcquisitionBusyError(new Error("nested BEGIN"));
|
|
24
|
+
}),
|
|
25
|
+
).rejects.toThrow("acquisition remained busy");
|
|
26
|
+
expect(calls).toBe(1);
|
|
27
|
+
} finally {
|
|
28
|
+
db.close();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
14
32
|
for (const mode of ["default", "immediate", "exclusive", "literal"] as const) {
|
|
15
33
|
test(`shared SQLite retries ${mode} acquisition before running any writes`, async () => {
|
|
16
34
|
const dir = createTestTempDirFromPath(join(tmpdir(), "mc-routed-acquisition-"));
|
package/src/shared/sqlite.ts
CHANGED
|
@@ -245,6 +245,7 @@ function installTransactionRouting(db: BetterSqlite3.Database, readonly: boolean
|
|
|
245
245
|
configurable: true,
|
|
246
246
|
writable: true,
|
|
247
247
|
value: (sql: string) => {
|
|
248
|
+
if (/^\s*(?:COMMIT|END)\b/i.test(sql)) assertTransformWrite();
|
|
248
249
|
if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
|
|
249
250
|
// A transaction that ended without passing through exec (for
|
|
250
251
|
// example an automatic rollback) must not be timed as this one.
|
|
@@ -430,6 +431,18 @@ function buildNodeSqliteDatabaseClass(DatabaseSync: any): typeof BetterSqlite3 {
|
|
|
430
431
|
|
|
431
432
|
export const Database: typeof BetterSqlite3 = TrackedDatabase;
|
|
432
433
|
|
|
434
|
+
/** The constructor path, without querying SQLite on the prompt thread. */
|
|
435
|
+
export function getSqliteDatabasePath(db: Database): string | null {
|
|
436
|
+
for (const connection of trackedSqliteConnections.values()) {
|
|
437
|
+
if (connection.reference.deref() === db) {
|
|
438
|
+
return connection.filename === ":memory:" || connection.filename === "<buffer>"
|
|
439
|
+
? null
|
|
440
|
+
: connection.filename;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
|
|
433
446
|
function pragmaValue(db: BetterSqlite3.Database, name: string): unknown {
|
|
434
447
|
const row = db.prepare(`PRAGMA ${name}`).get() as Record<string, unknown> | undefined;
|
|
435
448
|
if (!row) return undefined;
|
|
@@ -617,7 +630,12 @@ export function prepareCachedStatement(db: Database, sql: string): Statement {
|
|
|
617
630
|
|
|
618
631
|
const privilegeDepth = new WeakMap<Database, number>();
|
|
619
632
|
const transformPassScope = new AsyncLocalStorage<
|
|
620
|
-
|
|
633
|
+
| {
|
|
634
|
+
active: boolean;
|
|
635
|
+
remainingWaitMs: number;
|
|
636
|
+
guard?: { assert(): void; remainingMs(): number };
|
|
637
|
+
}
|
|
638
|
+
| undefined
|
|
621
639
|
>();
|
|
622
640
|
const admissionScope = new AsyncLocalStorage<boolean>();
|
|
623
641
|
const backgroundWriterScope = new AsyncLocalStorage<boolean>();
|
|
@@ -655,6 +673,17 @@ export function withoutSqliteTransformPass<T>(operation: () => T): T {
|
|
|
655
673
|
return transformPassScope.run(undefined, operation);
|
|
656
674
|
}
|
|
657
675
|
|
|
676
|
+
/** Attach the adapter's existing generation/cancellation/deadline guard to writes. */
|
|
677
|
+
export function guardSqliteTransformPass(guard: { assert(): void; remainingMs(): number }): void {
|
|
678
|
+
const lease = transformPassScope.getStore();
|
|
679
|
+
if (lease) lease.guard = guard;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/** Check the attached generation/cancellation/deadline guard before SQL or cache publication. */
|
|
683
|
+
export function assertTransformWrite(): void {
|
|
684
|
+
if (!backgroundWriterScope.getStore()) transformPassScope.getStore()?.guard?.assert();
|
|
685
|
+
}
|
|
686
|
+
|
|
658
687
|
/** Bun names SQLite codes; node:sqlite exposes the numeric (possibly extended) errcode. */
|
|
659
688
|
export function isTransientSqliteError(error: unknown): boolean {
|
|
660
689
|
if (!error || typeof error !== "object") return false;
|
|
@@ -698,6 +727,7 @@ interface WriterAcquisition {
|
|
|
698
727
|
startedAt: number;
|
|
699
728
|
attempts: number;
|
|
700
729
|
claimed: boolean;
|
|
730
|
+
remainingMs?: () => number;
|
|
701
731
|
}
|
|
702
732
|
|
|
703
733
|
/** A write transaction whose BEGIN went through `acquireShort` and has not ended yet. */
|
|
@@ -724,6 +754,7 @@ function foregroundWaitLease() {
|
|
|
724
754
|
* native API cannot separate lock waiting from statement execution. Exhaustion
|
|
725
755
|
* switches to zero-wait acquisition, not a refusal when the writer is free. */
|
|
726
756
|
function withAutocommitTimeout<T>(db: Database, operation: () => T): T {
|
|
757
|
+
assertTransformWrite();
|
|
727
758
|
const lease = foregroundWaitLease();
|
|
728
759
|
if (isInTransaction(db) || (!lease && !backgroundWriterScope.getStore())) return operation();
|
|
729
760
|
const previous = pragmaNumber(db, "busy_timeout");
|
|
@@ -749,6 +780,7 @@ function withAutocommitTimeout<T>(db: Database, operation: () => T): T {
|
|
|
749
780
|
|
|
750
781
|
/** The connection is never handed back to another caller with a shortened timeout. */
|
|
751
782
|
function acquireShort(db: Database, acquire: () => unknown, site: string): void {
|
|
783
|
+
assertTransformWrite();
|
|
752
784
|
const started = performance.now();
|
|
753
785
|
const previous = pragmaNumber(db, "busy_timeout");
|
|
754
786
|
const outer = writerAcquisitionScope.getStore();
|
|
@@ -763,7 +795,12 @@ function acquireShort(db: Database, acquire: () => unknown, site: string): void
|
|
|
763
795
|
// stays short. An ordinary BEGIN inside a transform pass cannot yield;
|
|
764
796
|
// give that single attempt enough time for a brief writer to finish.
|
|
765
797
|
const lease = foregroundWaitLease();
|
|
766
|
-
const timeout =
|
|
798
|
+
const timeout = Math.floor(
|
|
799
|
+
Math.min(
|
|
800
|
+
lease ? lease.remainingWaitMs : SHORT_BUSY_TIMEOUT_MS,
|
|
801
|
+
owner?.remainingMs?.() ?? lease?.guard?.remainingMs() ?? Infinity,
|
|
802
|
+
),
|
|
803
|
+
);
|
|
767
804
|
db.exec(`PRAGMA busy_timeout=${timeout}`);
|
|
768
805
|
acquire();
|
|
769
806
|
acquiredAt = performance.now();
|
|
@@ -871,11 +908,28 @@ export async function beginSqliteWriterAsync(db: Database, site: string): Promis
|
|
|
871
908
|
}
|
|
872
909
|
|
|
873
910
|
/** Retry admission before any turn work, yielding between short lock attempts. */
|
|
874
|
-
export async function withAsyncPrivilegedWriter<T>(
|
|
911
|
+
export async function withAsyncPrivilegedWriter<T>(
|
|
912
|
+
db: Database,
|
|
913
|
+
operation: () => T,
|
|
914
|
+
options: {
|
|
915
|
+
signal?: AbortSignal;
|
|
916
|
+
beforeRetry?: (error: SqliteAcquisitionBusyError) => void;
|
|
917
|
+
jitter?: boolean;
|
|
918
|
+
budgetMs?: number;
|
|
919
|
+
assertCurrentPass?: () => void;
|
|
920
|
+
} = {},
|
|
921
|
+
): Promise<T> {
|
|
875
922
|
const started = performance.now();
|
|
923
|
+
const budgetMs = Math.max(
|
|
924
|
+
0,
|
|
925
|
+
Math.min(FOREGROUND_ACQUISITION_BUDGET_MS, options.budgetMs ?? Infinity),
|
|
926
|
+
);
|
|
876
927
|
let attempts = 0;
|
|
877
928
|
for (;;) {
|
|
929
|
+
options.signal?.throwIfAborted();
|
|
930
|
+
options.assertCurrentPass?.();
|
|
878
931
|
attempts++;
|
|
932
|
+
let entered = false;
|
|
879
933
|
try {
|
|
880
934
|
const acquisition: WriterAcquisition = {
|
|
881
935
|
site: "privileged_writer",
|
|
@@ -883,35 +937,51 @@ export async function withAsyncPrivilegedWriter<T>(db: Database, operation: () =
|
|
|
883
937
|
startedAt: started,
|
|
884
938
|
attempts,
|
|
885
939
|
claimed: false,
|
|
940
|
+
remainingMs: () => Math.max(0, budgetMs - (performance.now() - started)),
|
|
886
941
|
};
|
|
887
942
|
// The acquisition and hold of this transaction are reported when it
|
|
888
943
|
// commits or rolls back (see endWriterTransaction).
|
|
889
944
|
return admissionScope.run(true, () =>
|
|
890
945
|
withSqliteTransformPass(() =>
|
|
891
946
|
writerAcquisitionScope.run(acquisition, () =>
|
|
892
|
-
withPrivilegedWriter(db,
|
|
947
|
+
withPrivilegedWriter(db, () => {
|
|
948
|
+
entered = true;
|
|
949
|
+
return operation();
|
|
950
|
+
}),
|
|
893
951
|
),
|
|
894
952
|
),
|
|
895
953
|
);
|
|
896
954
|
} catch (error) {
|
|
897
955
|
// A callback failure can occur after writes; only retry a failed BEGIN.
|
|
898
|
-
if (!(error instanceof SqliteAcquisitionBusyError)) throw error;
|
|
956
|
+
if (entered || !(error instanceof SqliteAcquisitionBusyError)) throw error;
|
|
957
|
+
// Adapters may serve a validated saved request instead of waiting.
|
|
958
|
+
// This runs only after a failed BEGIN, never after callback writes.
|
|
959
|
+
options.beforeRetry?.(error);
|
|
899
960
|
const elapsed = performance.now() - started;
|
|
900
|
-
if (elapsed >=
|
|
961
|
+
if (elapsed >= budgetMs) {
|
|
901
962
|
reportSqliteDiagnostic(
|
|
902
963
|
`sqlite writer site=privileged_writer lane=foreground acquire_ms=${Math.round(elapsed)} attempts=${attempts} outcome=busy`,
|
|
903
964
|
);
|
|
904
965
|
throw error;
|
|
905
966
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
attempts === 1 ? 500 : 1000,
|
|
911
|
-
FOREGROUND_ACQUISITION_BUDGET_MS - elapsed,
|
|
912
|
-
),
|
|
913
|
-
),
|
|
967
|
+
const backoff = attempts === 1 ? 500 : 1000;
|
|
968
|
+
const delay = Math.min(
|
|
969
|
+
options.jitter ? backoff * (0.8 + Math.random() * 0.4) : backoff,
|
|
970
|
+
budgetMs - elapsed,
|
|
914
971
|
);
|
|
972
|
+
await new Promise<void>((resolve, reject) => {
|
|
973
|
+
const abort = () => {
|
|
974
|
+
clearTimeout(timer);
|
|
975
|
+
options.signal?.removeEventListener("abort", abort);
|
|
976
|
+
reject(options.signal?.reason ?? new Error("Writer wait aborted"));
|
|
977
|
+
};
|
|
978
|
+
const timer = setTimeout(() => {
|
|
979
|
+
options.signal?.removeEventListener("abort", abort);
|
|
980
|
+
resolve();
|
|
981
|
+
}, delay);
|
|
982
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
983
|
+
if (options.signal?.aborted) abort();
|
|
984
|
+
});
|
|
915
985
|
}
|
|
916
986
|
}
|
|
917
987
|
}
|
|
@@ -118,7 +118,12 @@ describe("status view model", () => {
|
|
|
118
118
|
expect(rowLabels("Tags")).toEqual(["Active", "Dropped", "Total"]);
|
|
119
119
|
expect(rowLabels("Reductions")).toEqual(["Execute threshold", "Last reduce anchor"]);
|
|
120
120
|
expect(rowLabels("Pending Queue")).toEqual(["Drops", "Marker"]);
|
|
121
|
-
expect(rowLabels("Context Details")).toEqual([
|
|
121
|
+
expect(rowLabels("Context Details")).toEqual([
|
|
122
|
+
"Protected tags",
|
|
123
|
+
"Subagent",
|
|
124
|
+
"Denominator",
|
|
125
|
+
"Window source",
|
|
126
|
+
]);
|
|
122
127
|
expect(rowLabels("Cache TTL")).toEqual([
|
|
123
128
|
"Configured",
|
|
124
129
|
"Last response",
|
|
@@ -31,7 +31,11 @@ import {
|
|
|
31
31
|
type UserFacingFailureKey,
|
|
32
32
|
userFacingFailureCode,
|
|
33
33
|
} from "./user-facing-codes";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
formatWindowDerivationLine,
|
|
36
|
+
formatWindowSource,
|
|
37
|
+
type WindowGeometryResult,
|
|
38
|
+
} from "./window-geometry";
|
|
35
39
|
|
|
36
40
|
/** Theme-independent colour role; each host maps these onto its own palette. */
|
|
37
41
|
export type StatusTone = "accent" | "text" | "muted" | "warning" | "error";
|
|
@@ -656,6 +660,16 @@ function statusSections(source: StatusViewSource, now: number): StatusSection[]
|
|
|
656
660
|
tone: "muted",
|
|
657
661
|
},
|
|
658
662
|
{ label: "Subagent", value: source.isSubagent ? "yes" : "no", tone: "muted" },
|
|
663
|
+
{
|
|
664
|
+
label: "Denominator",
|
|
665
|
+
value: `${Math.round(source.contextLimit)} tokens`,
|
|
666
|
+
tone: "muted",
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
label: "Window source",
|
|
670
|
+
value: formatWindowSource(source.windowGeometry),
|
|
671
|
+
tone: "muted",
|
|
672
|
+
},
|
|
659
673
|
],
|
|
660
674
|
},
|
|
661
675
|
{ title: "Cache TTL", labelWidth: 14, rows: cacheRows(source, now) },
|
|
@@ -1157,6 +1157,10 @@ function buildAggregateTarget(
|
|
|
1157
1157
|
};
|
|
1158
1158
|
|
|
1159
1159
|
return {
|
|
1160
|
+
mutationParts: occurrences.map((occ) => ({
|
|
1161
|
+
message: { info: occ.message.info, parts: [] },
|
|
1162
|
+
part: occ.part,
|
|
1163
|
+
})),
|
|
1160
1164
|
measureReclaim(skeleton) {
|
|
1161
1165
|
let beforeTools = 0;
|
|
1162
1166
|
let afterTools = 0;
|
|
@@ -1319,6 +1323,7 @@ function buildTextTarget(
|
|
|
1319
1323
|
textPrefix: string,
|
|
1320
1324
|
): TagTarget {
|
|
1321
1325
|
return {
|
|
1326
|
+
mutationParts: [{ message: { info: message.info, parts: [] }, part }],
|
|
1322
1327
|
textPrefix,
|
|
1323
1328
|
setContent(content: string): boolean {
|
|
1324
1329
|
return part.setText(content);
|
|
@@ -1351,6 +1356,7 @@ function buildToolTarget(
|
|
|
1351
1356
|
tagId: number,
|
|
1352
1357
|
): TagTarget {
|
|
1353
1358
|
return {
|
|
1359
|
+
mutationParts: [{ message: { info: message.info, parts: [] }, part }],
|
|
1354
1360
|
setContent(content: string): boolean {
|
|
1355
1361
|
return setToolContentOrText(part, content);
|
|
1356
1362
|
},
|
|
@@ -89,6 +89,8 @@ export interface WindowDerivation {
|
|
|
89
89
|
geometry: WindowGeometry;
|
|
90
90
|
windowSource: WindowLimitSource;
|
|
91
91
|
absoluteWall: number;
|
|
92
|
+
/** Accepted provider input used to raise the usable denominator above metadata. */
|
|
93
|
+
provenInputTokens?: number;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
export interface WindowGeometryResult {
|
|
@@ -691,6 +693,7 @@ export function applyProvenInputFloor(
|
|
|
691
693
|
absoluteWall: trustedAbsoluteWall
|
|
692
694
|
? geometry.derivation.absoluteWall
|
|
693
695
|
: Math.max(geometry.derivation.absoluteWall, provenInputTokens),
|
|
696
|
+
provenInputTokens,
|
|
694
697
|
},
|
|
695
698
|
},
|
|
696
699
|
};
|
|
@@ -715,6 +718,13 @@ export function formatWindowDerivationLine(
|
|
|
715
718
|
return `${formatCompactTokens(inputTokens)} / ${formatCompactTokens(result.usableSoft)} usable · window ${formatCompactTokens(result.derivation.window)} · ${formatCompactTokens(result.derivation.reserve)} ${reserveLabel}`;
|
|
716
719
|
}
|
|
717
720
|
|
|
721
|
+
/** Distinguish metadata from a denominator raised by an accepted input measurement. */
|
|
722
|
+
export function formatWindowSource(result: WindowGeometryResult | undefined): string {
|
|
723
|
+
return result?.derivation.provenInputTokens
|
|
724
|
+
? `provider-measured floor (${result.derivation.windowSource})`
|
|
725
|
+
: (result?.derivation.windowSource ?? "unknown");
|
|
726
|
+
}
|
|
727
|
+
|
|
718
728
|
export function formatCompactTokens(value: number): string {
|
|
719
729
|
if (Math.abs(value) >= 1_000_000) {
|
|
720
730
|
return `${(value / 1_000_000).toFixed(1).replace(/\.0$/, "")}m`;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
runManagedWrapup2
|
|
3
|
-
} from "./index-e2dtv0ra.js";
|
|
4
|
-
import"./index-j7fvnjye.js";
|
|
5
|
-
import"./index-p821bjdb.js";
|
|
6
|
-
import"./index-sd3egckz.js";
|
|
7
|
-
import"./index-7qw742ev.js";
|
|
8
|
-
import"./index-0d3g3461.js";
|
|
9
|
-
import"./index-yasgb8ej.js";
|
|
10
|
-
import"./index-r5hmbx3t.js";
|
|
11
|
-
import"./index-3h0ybwah.js";
|
|
12
|
-
import"./index-majxda46.js";
|
|
13
|
-
import"./index-1zx87sdm.js";
|
|
14
|
-
import"./index-q4yp4b52.js";
|
|
15
|
-
import"./index-5gq3b2ya.js";
|
|
16
|
-
import"./index-b35pxds2.js";
|
|
17
|
-
import"./index-5eqk6z3s.js";
|
|
18
|
-
import"./index-sj29b3z2.js";
|
|
19
|
-
import"./index-fg34g6a5.js";
|
|
20
|
-
import"./index-x8a7jmkh.js";
|
|
21
|
-
import"./index-fzbvakh0.js";
|
|
22
|
-
import"./index-hybnrwq0.js";
|
|
23
|
-
import"./index-q6hef7fh.js";
|
|
24
|
-
export {
|
|
25
|
-
runManagedWrapup2 as runManagedWrapup
|
|
26
|
-
};
|
|
File without changes
|
|
File without changes
|