@retaw106/mmc-pi 2.0.0 → 2.1.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/cli.js +1 -1
- package/dist/config-init.js +1 -1
- package/dist/{index-kxehmcmt.js → index-4n1w7ehq.js} +106 -27
- package/dist/{index-wv28nczk.js → index-ny303arh.js} +76 -3
- package/dist/index.js +1503 -300
- package/dist/{message-time-backfill-wnxmrd09.js → message-time-backfill-a3p9r5rh.js} +1 -1
- package/dist/{read-session-chunk-7m31yz4a.js → read-session-chunk-7wcjah1z.js} +2 -2
- package/dist/web/main.js +705 -53
- package/dist/web/ui/panel.css +2 -0
- package/dist/web/ui/panel.js +135 -77
- package/package.json +1 -1
package/dist/cli.js
CHANGED
package/dist/config-init.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./index-54cgx9gm.js";
|
|
7
7
|
import {
|
|
8
8
|
Database
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-ny303arh.js";
|
|
10
10
|
|
|
11
11
|
// ../plugin/src/hooks/magic-context/tokenizer-calibration-seeds.json
|
|
12
12
|
var tokenizer_calibration_seeds_default = [
|
|
@@ -2553,7 +2553,7 @@ function findPiFallbackToolOwnerTags(db, sessionId) {
|
|
|
2553
2553
|
AND tool_owner_message_id LIKE 'pi-msg-%'
|
|
2554
2554
|
ORDER BY tag_number ASC`).all(sessionId).filter(isPiFallbackToolOwnerTag);
|
|
2555
2555
|
}
|
|
2556
|
-
function adoptPiFallbackToolOwnerTag(db, sessionId, tagNumber, callId, oldOwnerMessageId, newOwnerMessageId) {
|
|
2556
|
+
function adoptPiFallbackToolOwnerTag(db, sessionId, tagNumber, callId, oldOwnerMessageId, newOwnerMessageId, servedTagNumbers = new Set) {
|
|
2557
2557
|
const survivor = getPiFallbackFoldTagRowByNumber(db, sessionId, tagNumber);
|
|
2558
2558
|
if (survivor === null || survivor.type !== "tool" || survivor.messageId !== callId || survivor.toolOwnerMessageId !== oldOwnerMessageId) {
|
|
2559
2559
|
return { action: "skipped" };
|
|
@@ -2572,6 +2572,14 @@ function adoptPiFallbackToolOwnerTag(db, sessionId, tagNumber, callId, oldOwnerM
|
|
|
2572
2572
|
if (existing.tagNumber === tagNumber) {
|
|
2573
2573
|
return { action: "skipped" };
|
|
2574
2574
|
}
|
|
2575
|
+
if (servedTagNumbers.has(survivor.tagNumber)) {
|
|
2576
|
+
if (servedTagNumbers.has(existing.tagNumber)) {
|
|
2577
|
+
throw new Error("Conflicting served Pi tool tag numbers; refusing identity adoption");
|
|
2578
|
+
}
|
|
2579
|
+
foldDuplicateIntoSurvivor(db, sessionId, survivor, existing);
|
|
2580
|
+
db.prepare("UPDATE tags SET tool_owner_message_id = ? WHERE session_id = ? AND tag_number = ?").run(newOwnerMessageId, sessionId, tagNumber);
|
|
2581
|
+
return { action: "folded", tagNumber, deletedTagNumbers: [existing.tagNumber] };
|
|
2582
|
+
}
|
|
2575
2583
|
foldDuplicateIntoSurvivor(db, sessionId, existing, survivor);
|
|
2576
2584
|
return {
|
|
2577
2585
|
action: "folded",
|
|
@@ -2579,7 +2587,7 @@ function adoptPiFallbackToolOwnerTag(db, sessionId, tagNumber, callId, oldOwnerM
|
|
|
2579
2587
|
deletedTagNumbers: [tagNumber]
|
|
2580
2588
|
};
|
|
2581
2589
|
}
|
|
2582
|
-
function adoptPiFallbackMessageTag(db, sessionId, tagNumber, oldFallbackMessageId, newRealMessageId) {
|
|
2590
|
+
function adoptPiFallbackMessageTag(db, sessionId, tagNumber, oldFallbackMessageId, newRealMessageId, servedTagNumbers = new Set) {
|
|
2583
2591
|
if (oldFallbackMessageId.startsWith(WHITESPACE_ASSISTANT_INERT_MESSAGE_PREFIX)) {
|
|
2584
2592
|
return { action: "skipped" };
|
|
2585
2593
|
}
|
|
@@ -2597,15 +2605,23 @@ function adoptPiFallbackMessageTag(db, sessionId, tagNumber, oldFallbackMessageI
|
|
|
2597
2605
|
AND message_id = ?`).run(newRealMessageId, sessionId, tagNumber, oldFallbackMessageId);
|
|
2598
2606
|
return (result.changes ?? 0) > 0 ? { action: "rekeyed", tagNumber } : { action: "skipped" };
|
|
2599
2607
|
}
|
|
2600
|
-
const
|
|
2608
|
+
const servedRows = [survivor, ...duplicates].filter((row) => servedTagNumbers.has(row.tagNumber));
|
|
2609
|
+
if (servedRows.length > 1) {
|
|
2610
|
+
throw new Error("Conflicting served Pi message tag numbers; refusing identity adoption");
|
|
2611
|
+
}
|
|
2612
|
+
const realSurvivor = servedRows[0] ?? duplicates[0];
|
|
2601
2613
|
if (!realSurvivor)
|
|
2602
2614
|
return { action: "skipped" };
|
|
2603
|
-
const deletedTagNumbers = [
|
|
2604
|
-
|
|
2605
|
-
|
|
2615
|
+
const deletedTagNumbers = [];
|
|
2616
|
+
for (const duplicate of [survivor, ...duplicates]) {
|
|
2617
|
+
if (duplicate.tagNumber === realSurvivor.tagNumber)
|
|
2618
|
+
continue;
|
|
2606
2619
|
foldDuplicateIntoSurvivor(db, sessionId, realSurvivor, duplicate);
|
|
2607
2620
|
deletedTagNumbers.push(duplicate.tagNumber);
|
|
2608
2621
|
}
|
|
2622
|
+
if (realSurvivor.tagNumber === tagNumber) {
|
|
2623
|
+
db.prepare("UPDATE tags SET message_id = ? WHERE session_id = ? AND tag_number = ?").run(newRealMessageId, sessionId, tagNumber);
|
|
2624
|
+
}
|
|
2609
2625
|
return {
|
|
2610
2626
|
action: "folded",
|
|
2611
2627
|
tagNumber: realSurvivor.tagNumber,
|
|
@@ -2892,6 +2908,20 @@ function pickNearestPriorOwner(candidates, currentMessageId, times) {
|
|
|
2892
2908
|
}
|
|
2893
2909
|
// ../plugin/src/shared/historian-tool-defaults.json
|
|
2894
2910
|
var historian_tool_defaults_default = {
|
|
2911
|
+
todowrite: 'Todos (${input.todos.count}): ${input.todos.each("${status}: ${content}")}',
|
|
2912
|
+
question: 'Asked user: ${input.questions[*].question.join(" / ")} → ${output}',
|
|
2913
|
+
task: "Task ${input.subagent_type}: ${input.prompt.truncate(600)} → ${output.truncate(200)}",
|
|
2914
|
+
ask: 'Asked: ${input.question} [options: ${input.options.join(" / ")}] ${input.text}${input.resolution} → ${output.truncate(300)}',
|
|
2915
|
+
peer_send: "PM ${input.recipient}: ${input.message}",
|
|
2916
|
+
board: 'Board ${input.verb}${input.board_lane}: ${input.ops.each("${op} ${lane_label} ${item}${props.text}${lane.items} ${state}${props.state}${lane.status}${id}${title}")}',
|
|
2917
|
+
room: "Room ${input.action} ${input.room_label}: ${input.room_detail}",
|
|
2918
|
+
work: "Work ${input.action} ${input.title}${input.id}: ${input.prompt.truncate(600)}${input.notes.truncate(600)} → ${output.truncate(160)}",
|
|
2919
|
+
knowhow: "Looked up how-to: ${input.query}${input.id}",
|
|
2920
|
+
mmc_note: "Note ${input.action} ${input.note_ids}: ${input.content} → ${output.truncate(160)}",
|
|
2921
|
+
mmc_memory: "Memory ${input.action} ${input.category} ${input.ids}: ${input.content} → ${output.truncate(160)}"
|
|
2922
|
+
};
|
|
2923
|
+
// ../plugin/src/shared/historian-tool-preview-defaults.json
|
|
2924
|
+
var historian_tool_preview_defaults_default = {
|
|
2895
2925
|
todowrite: 'Todos (${input.todos.count}): ${input.todos.each("${status}: ${content}")}',
|
|
2896
2926
|
question: 'Asked user: ${input.questions[*].question.join(" / ")} → ${output}',
|
|
2897
2927
|
task: "Task ${input.subagent_type}: ${input.prompt} → ${output.truncate(200)}",
|
|
@@ -2984,7 +3014,7 @@ class Parser {
|
|
|
2984
3014
|
} else
|
|
2985
3015
|
break;
|
|
2986
3016
|
}
|
|
2987
|
-
const expression = { steps
|
|
3017
|
+
const expression = { steps };
|
|
2988
3018
|
if (this.take(".each(")) {
|
|
2989
3019
|
if (relative)
|
|
2990
3020
|
return this.error();
|
|
@@ -3051,9 +3081,23 @@ function compiled(source) {
|
|
|
3051
3081
|
function oneLine(value) {
|
|
3052
3082
|
return value.replace(/[\r\n\u2028\u2029]+/g, " ");
|
|
3053
3083
|
}
|
|
3054
|
-
function truncate(value, cap) {
|
|
3084
|
+
function truncate(value, cap, legacyPreview = false) {
|
|
3055
3085
|
const chars = Array.from(oneLine(value));
|
|
3056
|
-
|
|
3086
|
+
if (cap === undefined || chars.length <= cap)
|
|
3087
|
+
return chars.join("");
|
|
3088
|
+
if (legacyPreview)
|
|
3089
|
+
return `${chars.slice(0, cap).join("")}…`;
|
|
3090
|
+
let end = 0;
|
|
3091
|
+
for (let i = 0;i < Math.min(cap, chars.length); i++) {
|
|
3092
|
+
if (!".!?。!?".includes(chars[i]))
|
|
3093
|
+
continue;
|
|
3094
|
+
let next = i + 1;
|
|
3095
|
+
while (next < chars.length && `"'”’)]`.includes(chars[next]))
|
|
3096
|
+
next++;
|
|
3097
|
+
if (next <= cap && ("。!?".includes(chars[i]) || next === chars.length || /[ \t]/.test(chars[next])))
|
|
3098
|
+
end = next;
|
|
3099
|
+
}
|
|
3100
|
+
return `${end ? `${chars.slice(0, end).join("")} ` : ""}[… ${chars.length - end} more characters]`;
|
|
3057
3101
|
}
|
|
3058
3102
|
function scalar(value) {
|
|
3059
3103
|
if (value === undefined)
|
|
@@ -3070,10 +3114,11 @@ function compactJson(value) {
|
|
|
3070
3114
|
function field(value, key) {
|
|
3071
3115
|
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.hasOwn(value, key) ? value[key] : undefined;
|
|
3072
3116
|
}
|
|
3073
|
-
function render(nodes, root) {
|
|
3117
|
+
function render(nodes, root, legacyPreview) {
|
|
3074
3118
|
return nodes.map((node) => {
|
|
3075
3119
|
if (typeof node === "string")
|
|
3076
3120
|
return node;
|
|
3121
|
+
const cap = node.cap ?? (legacyPreview ? 300 : undefined);
|
|
3077
3122
|
let value = root;
|
|
3078
3123
|
let list = false;
|
|
3079
3124
|
for (const step of node.steps) {
|
|
@@ -3087,19 +3132,19 @@ function render(nodes, root) {
|
|
|
3087
3132
|
}
|
|
3088
3133
|
}
|
|
3089
3134
|
if (node.count)
|
|
3090
|
-
return truncate(Array.isArray(value) ? String(value.length) : "",
|
|
3135
|
+
return truncate(Array.isArray(value) ? String(value.length) : "", cap, legacyPreview);
|
|
3091
3136
|
if (node.each || list || node.join !== undefined) {
|
|
3092
3137
|
if (!Array.isArray(value))
|
|
3093
3138
|
return "";
|
|
3094
|
-
const elements = value.slice(0, 10).map((item) => truncate(node.each ? render(node.each, item) : scalar(item), 300));
|
|
3095
|
-
if (value.length > 10)
|
|
3139
|
+
const elements = value.slice(0, legacyPreview ? 10 : undefined).map((item) => truncate(node.each ? render(node.each, item, legacyPreview) : scalar(item), legacyPreview ? 300 : undefined, legacyPreview));
|
|
3140
|
+
if (legacyPreview && value.length > 10)
|
|
3096
3141
|
elements.push(`… +${value.length - 10} more`);
|
|
3097
|
-
return truncate(elements.join(node.join ?? (node.each ? "; " : ", ")),
|
|
3142
|
+
return truncate(elements.join(node.join ?? (node.each ? "; " : ", ")), cap, legacyPreview);
|
|
3098
3143
|
}
|
|
3099
|
-
return truncate(scalar(value),
|
|
3144
|
+
return truncate(scalar(value), cap, legacyPreview);
|
|
3100
3145
|
}).join("");
|
|
3101
3146
|
}
|
|
3102
|
-
function renderToolTemplate(source, input, output) {
|
|
3147
|
+
function renderToolTemplate(source, input, output, legacyPreview = false) {
|
|
3103
3148
|
const nodes = compiled(source);
|
|
3104
3149
|
if (!nodes)
|
|
3105
3150
|
return null;
|
|
@@ -3114,22 +3159,53 @@ function renderToolTemplate(source, input, output) {
|
|
|
3114
3159
|
}
|
|
3115
3160
|
}
|
|
3116
3161
|
const root = { input, output: structured };
|
|
3117
|
-
const bareOutputNodes = nodes.map((node) => typeof node !== "string" && node.steps.length === 1 && ("field" in node.steps[0]) && node.steps[0].field === "output" && !node.each && !node.count && node.join === undefined ? truncate(text ?? "", node.cap) : node);
|
|
3118
|
-
return truncate(render(bareOutputNodes, root), 1000);
|
|
3162
|
+
const bareOutputNodes = nodes.map((node) => typeof node !== "string" && node.steps.length === 1 && ("field" in node.steps[0]) && node.steps[0].field === "output" && !node.each && !node.count && node.join === undefined ? truncate(text ?? "", node.cap ?? (legacyPreview ? 300 : undefined), legacyPreview) : node);
|
|
3163
|
+
return truncate(render(bareOutputNodes, root, legacyPreview), legacyPreview ? 1000 : undefined, legacyPreview);
|
|
3119
3164
|
}
|
|
3120
3165
|
|
|
3121
3166
|
// ../plugin/src/shared/historian-tool-expansions.ts
|
|
3122
|
-
function
|
|
3167
|
+
function builtinInput(tool, input) {
|
|
3168
|
+
if (!input || typeof input !== "object")
|
|
3169
|
+
return input;
|
|
3170
|
+
const i = input;
|
|
3171
|
+
if (tool === "peer_send") {
|
|
3172
|
+
const external = i.external;
|
|
3173
|
+
const recipient = i.reply_to_pmid ? `reply to ${i.reply_to_pmid}` : i.agent || i.agent_id ? `to ${i.agent || i.agent_id}` : external?.session_id || i.session_id ? `to session ${external?.session_id || i.session_id}` : "to unknown recipient";
|
|
3174
|
+
return { ...i, recipient };
|
|
3175
|
+
}
|
|
3176
|
+
if (tool === "room") {
|
|
3177
|
+
return {
|
|
3178
|
+
...i,
|
|
3179
|
+
room_label: i.room_id || i.title,
|
|
3180
|
+
room_detail: i.action === "create" || i.action === "invite" ? `invitees: ${renderToolTemplate("${input.invitees}", i)}` : i.text
|
|
3181
|
+
};
|
|
3182
|
+
}
|
|
3183
|
+
if (tool === "board") {
|
|
3184
|
+
return {
|
|
3185
|
+
...i,
|
|
3186
|
+
board_lane: i.lane ? ` ${i.lane}` : "",
|
|
3187
|
+
ops: Array.isArray(i.ops) ? i.ops.map((op) => op && typeof op === "object" ? {
|
|
3188
|
+
...op,
|
|
3189
|
+
lane_label: typeof op.lane === "string" ? op.lane : op.lane?.title || op.lane?.id
|
|
3190
|
+
} : op) : i.ops
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
return input;
|
|
3194
|
+
}
|
|
3195
|
+
function expandToolPart(part, overrides, legacyPreview = false) {
|
|
3123
3196
|
if (!part || typeof part !== "object")
|
|
3124
3197
|
return null;
|
|
3125
3198
|
const p = part;
|
|
3126
3199
|
if (p.type !== "tool" || typeof p.tool !== "string")
|
|
3127
3200
|
return null;
|
|
3128
|
-
const
|
|
3201
|
+
const builtinDefaults = legacyPreview ? historian_tool_preview_defaults_default : historian_tool_defaults_default;
|
|
3202
|
+
const overridden = overrides && Object.hasOwn(overrides, p.tool);
|
|
3203
|
+
const template = overridden ? overrides[p.tool] : Object.hasOwn(builtinDefaults, p.tool) ? builtinDefaults[p.tool] : undefined;
|
|
3129
3204
|
if (typeof template !== "string")
|
|
3130
3205
|
return null;
|
|
3131
3206
|
const state = p.state;
|
|
3132
|
-
|
|
3207
|
+
const input = overridden || legacyPreview ? state?.input : builtinInput(p.tool, state?.input);
|
|
3208
|
+
return renderToolTemplate(template, input, state?.output ?? state?.error, legacyPreview);
|
|
3133
3209
|
}
|
|
3134
3210
|
|
|
3135
3211
|
// ../plugin/src/hooks/magic-context/host-served-rows.ts
|
|
@@ -4121,20 +4197,22 @@ function fenceBoundaryForCompletedToolArcs(candidate, arcs, publicationFloorOrdi
|
|
|
4121
4197
|
return minInvocation < publicationFloorOrdinal ? maxResult + 1 : minInvocation;
|
|
4122
4198
|
}
|
|
4123
4199
|
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
|
|
4124
|
-
|
|
4200
|
+
let boundary = fenceBoundaryForCompletedToolArcs(candidate, arcs, lastCompartmentEndOrdinal + 1);
|
|
4125
4201
|
for (const arc of arcs) {
|
|
4126
4202
|
if (arc.resOrdinal !== null)
|
|
4127
4203
|
continue;
|
|
4128
4204
|
if (arc.invOrdinal < recentOpenArcCutoff)
|
|
4129
4205
|
continue;
|
|
4130
4206
|
if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
|
|
4131
|
-
|
|
4207
|
+
boundary = arc.invOrdinal;
|
|
4208
|
+
break;
|
|
4132
4209
|
}
|
|
4133
4210
|
if (arc.invOrdinal >= boundary) {
|
|
4134
|
-
|
|
4211
|
+
boundary = arc.invOrdinal;
|
|
4212
|
+
break;
|
|
4135
4213
|
}
|
|
4136
4214
|
}
|
|
4137
|
-
return boundary;
|
|
4215
|
+
return fenceBoundaryForCompletedToolArcs(boundary, arcs, 1);
|
|
4138
4216
|
}
|
|
4139
4217
|
function tokenForMessage(message, options) {
|
|
4140
4218
|
const key = messageCacheKey(message, options);
|
|
@@ -4651,7 +4729,8 @@ function detectThinkingBindingMismatch(error) {
|
|
|
4651
4729
|
var PREFIX_BOUND_THINKING_MODELS = [
|
|
4652
4730
|
["fable", 5, 1],
|
|
4653
4731
|
["opus", 5, 5],
|
|
4654
|
-
["sonnet", 5, 5]
|
|
4732
|
+
["sonnet", 5, 5],
|
|
4733
|
+
["haiku", 5, 5]
|
|
4655
4734
|
];
|
|
4656
4735
|
var PREFIX_BOUND_THINKING_PATTERN = new RegExp(`(?:^|[-_.:/])(?:${PREFIX_BOUND_THINKING_MODELS.map(([family, major, minor]) => `${family}[-_.]?${major}[-_.]${minor}`).join("|")})(?:$|[-_.:/@])`, "i");
|
|
4657
4736
|
function isPrefixBoundThinkingModel(_providerID, modelID) {
|
|
@@ -69,6 +69,8 @@ function installTransactionRouting(db, readonly) {
|
|
|
69
69
|
configurable: true,
|
|
70
70
|
writable: true,
|
|
71
71
|
value: (sql) => {
|
|
72
|
+
if (/^\s*(?:COMMIT|END)\b/i.test(sql))
|
|
73
|
+
assertTransformWrite();
|
|
72
74
|
if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
|
|
73
75
|
openWriterTransactions.delete(db);
|
|
74
76
|
if (transformPassScope.getStore()?.active || backgroundWriterScope.getStore())
|
|
@@ -221,6 +223,15 @@ function pragmaNumber(db, name) {
|
|
|
221
223
|
}
|
|
222
224
|
var statementOwners = new WeakMap;
|
|
223
225
|
var FIRST_PRUNE_AT = 256;
|
|
226
|
+
function closeReleasingStatements(close, args) {
|
|
227
|
+
if (args[0] === true)
|
|
228
|
+
return close(true);
|
|
229
|
+
try {
|
|
230
|
+
return close(true);
|
|
231
|
+
} catch {
|
|
232
|
+
return close(...args);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
224
235
|
function ownPreparedStatement(db, statement) {
|
|
225
236
|
const finalizable = statement;
|
|
226
237
|
if (typeof finalizable.finalize !== "function")
|
|
@@ -244,7 +255,7 @@ function ownPreparedStatement(db, statement) {
|
|
|
244
255
|
}
|
|
245
256
|
}
|
|
246
257
|
references.clear();
|
|
247
|
-
const result = close
|
|
258
|
+
const result = closeReleasingStatements(close, args);
|
|
248
259
|
if (failure)
|
|
249
260
|
throw failure;
|
|
250
261
|
return result;
|
|
@@ -298,6 +309,15 @@ function withSqliteTransformPass(operation) {
|
|
|
298
309
|
function withoutSqliteTransformPass(operation) {
|
|
299
310
|
return transformPassScope.run(undefined, operation);
|
|
300
311
|
}
|
|
312
|
+
function guardSqliteTransformPass(guard) {
|
|
313
|
+
const lease = transformPassScope.getStore();
|
|
314
|
+
if (lease)
|
|
315
|
+
lease.guard = guard;
|
|
316
|
+
}
|
|
317
|
+
function assertTransformWrite() {
|
|
318
|
+
if (!backgroundWriterScope.getStore())
|
|
319
|
+
transformPassScope.getStore()?.guard?.assert();
|
|
320
|
+
}
|
|
301
321
|
function isTransientSqliteError(error) {
|
|
302
322
|
if (!error || typeof error !== "object")
|
|
303
323
|
return false;
|
|
@@ -316,6 +336,7 @@ class SqliteAcquisitionBusyError extends Error {
|
|
|
316
336
|
}
|
|
317
337
|
var SHORT_BUSY_TIMEOUT_MS = 25;
|
|
318
338
|
var FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 250;
|
|
339
|
+
var FOREGROUND_ACQUISITION_BUDGET_MS = 16500;
|
|
319
340
|
var SLOW_WRITER_LOG_MS = 250;
|
|
320
341
|
var WRITER_TRANSACTION_END = /^\s*(?:COMMIT|END|ROLLBACK)(?:\s+TRANSACTION)?\s*;?\s*$/i;
|
|
321
342
|
var writerAcquisitionScope = new AsyncLocalStorage;
|
|
@@ -325,6 +346,7 @@ function foregroundWaitLease() {
|
|
|
325
346
|
return lease?.active && !admissionScope.getStore() && !backgroundWriterScope.getStore() ? lease : undefined;
|
|
326
347
|
}
|
|
327
348
|
function withAutocommitTimeout(db, operation) {
|
|
349
|
+
assertTransformWrite();
|
|
328
350
|
const lease = foregroundWaitLease();
|
|
329
351
|
if (isInTransaction(db) || !lease && !backgroundWriterScope.getStore())
|
|
330
352
|
return operation();
|
|
@@ -345,6 +367,7 @@ function withAutocommitTimeout(db, operation) {
|
|
|
345
367
|
}
|
|
346
368
|
}
|
|
347
369
|
function acquireShort(db, acquire, site) {
|
|
370
|
+
assertTransformWrite();
|
|
348
371
|
const started = performance.now();
|
|
349
372
|
const previous = pragmaNumber(db, "busy_timeout");
|
|
350
373
|
const outer = writerAcquisitionScope.getStore();
|
|
@@ -356,7 +379,7 @@ function acquireShort(db, acquire, site) {
|
|
|
356
379
|
let acquiredAt;
|
|
357
380
|
try {
|
|
358
381
|
const lease = foregroundWaitLease();
|
|
359
|
-
const timeout =
|
|
382
|
+
const timeout = Math.floor(Math.min(lease ? lease.remainingWaitMs : SHORT_BUSY_TIMEOUT_MS, owner?.remainingMs?.() ?? lease?.guard?.remainingMs() ?? Infinity));
|
|
360
383
|
db.exec(`PRAGMA busy_timeout=${timeout}`);
|
|
361
384
|
acquire();
|
|
362
385
|
acquiredAt = performance.now();
|
|
@@ -402,6 +425,56 @@ function endWriterTransaction(db, end, sql) {
|
|
|
402
425
|
}
|
|
403
426
|
}
|
|
404
427
|
}
|
|
428
|
+
async function withAsyncPrivilegedWriter(db, operation, options = {}) {
|
|
429
|
+
const started = performance.now();
|
|
430
|
+
const budgetMs = Math.max(0, Math.min(FOREGROUND_ACQUISITION_BUDGET_MS, options.budgetMs ?? Infinity));
|
|
431
|
+
let attempts = 0;
|
|
432
|
+
for (;; ) {
|
|
433
|
+
options.signal?.throwIfAborted();
|
|
434
|
+
options.assertCurrentPass?.();
|
|
435
|
+
attempts++;
|
|
436
|
+
let entered = false;
|
|
437
|
+
try {
|
|
438
|
+
const acquisition = {
|
|
439
|
+
site: "privileged_writer",
|
|
440
|
+
lane: "foreground",
|
|
441
|
+
startedAt: started,
|
|
442
|
+
attempts,
|
|
443
|
+
claimed: false,
|
|
444
|
+
remainingMs: () => Math.max(0, budgetMs - (performance.now() - started))
|
|
445
|
+
};
|
|
446
|
+
return admissionScope.run(true, () => withSqliteTransformPass(() => writerAcquisitionScope.run(acquisition, () => withPrivilegedWriter(db, () => {
|
|
447
|
+
entered = true;
|
|
448
|
+
return operation();
|
|
449
|
+
}))));
|
|
450
|
+
} catch (error) {
|
|
451
|
+
if (entered || !(error instanceof SqliteAcquisitionBusyError))
|
|
452
|
+
throw error;
|
|
453
|
+
options.beforeRetry?.(error);
|
|
454
|
+
const elapsed = performance.now() - started;
|
|
455
|
+
if (elapsed >= budgetMs) {
|
|
456
|
+
reportSqliteDiagnostic(`sqlite writer site=privileged_writer lane=foreground acquire_ms=${Math.round(elapsed)} attempts=${attempts} outcome=busy`);
|
|
457
|
+
throw error;
|
|
458
|
+
}
|
|
459
|
+
const backoff = attempts === 1 ? 500 : 1000;
|
|
460
|
+
const delay = Math.min(options.jitter ? backoff * (0.8 + Math.random() * 0.4) : backoff, budgetMs - elapsed);
|
|
461
|
+
await new Promise((resolve, reject) => {
|
|
462
|
+
const abort = () => {
|
|
463
|
+
clearTimeout(timer);
|
|
464
|
+
options.signal?.removeEventListener("abort", abort);
|
|
465
|
+
reject(options.signal?.reason ?? new Error("Writer wait aborted"));
|
|
466
|
+
};
|
|
467
|
+
const timer = setTimeout(() => {
|
|
468
|
+
options.signal?.removeEventListener("abort", abort);
|
|
469
|
+
resolve();
|
|
470
|
+
}, delay);
|
|
471
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
472
|
+
if (options.signal?.aborted)
|
|
473
|
+
abort();
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
405
478
|
function isInTransaction(db) {
|
|
406
479
|
const candidate = db;
|
|
407
480
|
return candidate.inTransaction === true || candidate.isTransaction === true;
|
|
@@ -461,4 +534,4 @@ function withPrivilegedWriter(db, operation) {
|
|
|
461
534
|
}
|
|
462
535
|
}
|
|
463
536
|
|
|
464
|
-
export { registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, Database, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, isTransientSqliteError, withPrivilegedWriter };
|
|
537
|
+
export { registerSlowWriteReporter, registerSqliteDiagnosticSink, detectSqliteRuntime, Database, prepareCachedStatement, withSqliteBackgroundWriter, withSqliteTransformPass, withoutSqliteTransformPass, guardSqliteTransformPass, isTransientSqliteError, withAsyncPrivilegedWriter, withPrivilegedWriter };
|