@massu/core 2.5.6 → 2.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/cli.js +98 -4
- package/dist/hooks/auto-learning-pipeline.js +81 -16
- package/dist/hooks/classify-failure.js +73 -8
- package/dist/hooks/cost-tracker.js +69 -4
- package/dist/hooks/fix-detector.js +75 -10
- package/dist/hooks/incident-pipeline.js +72 -7
- package/dist/hooks/intent-suggester.js +69 -4
- package/dist/hooks/memory-recall.js +89 -6
- package/dist/hooks/post-edit-context.js +69 -4
- package/dist/hooks/post-tool-use.js +76 -11
- package/dist/hooks/pre-compact.js +69 -4
- package/dist/hooks/pre-delete-check.js +69 -4
- package/dist/hooks/pre-tool-use-gate.js +69 -4
- package/dist/hooks/quality-event.js +69 -4
- package/dist/hooks/rule-enforcement-pipeline.js +72 -7
- package/dist/hooks/security-gate.js +69 -4
- package/dist/hooks/session-end.js +137 -10
- package/dist/hooks/session-start.js +112 -8
- package/dist/hooks/user-prompt.js +83 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
10
10
|
> redistributed under the **Apache License 2.0**. Full license + NOTICE:
|
|
11
11
|
> `packages/core/assets/embedder/MODEL-LICENSE`.
|
|
12
12
|
|
|
13
|
+
## [2.5.7] - 2026-11-10
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Cloud sync no longer queues uploads the server refuses.** When the massu cloud refused an upload
|
|
18
|
+
for a reason that retrying cannot change (for example `403 Sync not available for current plan
|
|
19
|
+
status`), massu queued it and retried forever. One project's memory database grew to 8.5 GB this way,
|
|
20
|
+
7.6 GB of it refused uploads, and every reply re-sent a ~179 KB upload that was always refused. Now:
|
|
21
|
+
- a refusal that cannot succeed is never queued or retried;
|
|
22
|
+
- when the account itself is refused (401 or 403), massu records the server's reason, pauses sync,
|
|
23
|
+
clears the queue (learned rules are kept), and shows `MASSU CLOUD SYNC PAUSED` with that reason at
|
|
24
|
+
session start. It checks again once a day and resumes on its own when the account allows it;
|
|
25
|
+
- temporary failures (network, 5xx, 429) still queue, but the queue now holds at most 500 items and
|
|
26
|
+
nothing older than 30 days, and every item gets its turn.
|
|
27
|
+
- **`massu install-hooks` now upgrades a project completely.** It also moves the massu MCP server in
|
|
28
|
+
`.mcp.json` to the same version as the hooks (it never did before, so every upgrade left the server
|
|
29
|
+
behind), and it replaces hook entries written by much older installers, which used to stay beside the
|
|
30
|
+
current ones and fail on every tool call. Your own hooks and any hand-written massu entry are left alone.
|
|
31
|
+
- **A hook launched through a symlinked path now runs.** Started through a symlink, a hook decided it was
|
|
32
|
+
not the program being run and exited without doing anything; for the safety gate that meant allowing
|
|
33
|
+
the call. The commands `massu init` writes were not affected.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **The first prompt of a session is faster.** Session start now loads the memory-search model in the
|
|
38
|
+
background, so the first prompt no longer pays for it (measured: about 1.5 s for the first search,
|
|
39
|
+
0.6 s after). Turn it off with `MASSU_RECALL_WARMUP=0`.
|
|
40
|
+
- **`scripts/massu-db-size-audit.sh`** (in the repository) reports where a massu database's space goes:
|
|
41
|
+
live data, space freed but not yet returned to the disk, the largest tables, and the sync queue.
|
|
42
|
+
|
|
13
43
|
## [2.5.6] - 2026-11-09
|
|
14
44
|
|
|
15
45
|
### Security
|
package/dist/cli.js
CHANGED
|
@@ -2490,6 +2490,8 @@ __export(memory_db_exports, {
|
|
|
2490
2490
|
MEMORY_FILE_TITLE_LIKE: () => MEMORY_FILE_TITLE_LIKE,
|
|
2491
2491
|
MEMORY_FILE_TITLE_PREFIX: () => MEMORY_FILE_TITLE_PREFIX,
|
|
2492
2492
|
MEMORY_SCHEMA_VERSION: () => MEMORY_SCHEMA_VERSION,
|
|
2493
|
+
PENDING_SYNC_MAX_AGE_DAYS: () => PENDING_SYNC_MAX_AGE_DAYS,
|
|
2494
|
+
PENDING_SYNC_MAX_ITEMS: () => PENDING_SYNC_MAX_ITEMS,
|
|
2493
2495
|
TOOL_COST_EVENTS_RETENTION_DAYS: () => TOOL_COST_EVENTS_RETENTION_DAYS,
|
|
2494
2496
|
USAGE_COUNTER_ARMED_KEY: () => USAGE_COUNTER_ARMED_KEY,
|
|
2495
2497
|
addConversationTurn: () => addConversationTurn,
|
|
@@ -2498,6 +2500,7 @@ __export(memory_db_exports, {
|
|
|
2498
2500
|
addSummary: () => addSummary,
|
|
2499
2501
|
addToolCallDetail: () => addToolCallDetail,
|
|
2500
2502
|
addUserPrompt: () => addUserPrompt,
|
|
2503
|
+
allPendingSync: () => allPendingSync,
|
|
2501
2504
|
appendIncidentToFailureClass: () => appendIncidentToFailureClass,
|
|
2502
2505
|
armUsageCounter: () => armUsageCounter,
|
|
2503
2506
|
assignImportance: () => assignImportance,
|
|
@@ -2505,6 +2508,7 @@ __export(memory_db_exports, {
|
|
|
2505
2508
|
createSession: () => createSession,
|
|
2506
2509
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
2507
2510
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
2511
|
+
discardPendingSync: () => discardPendingSync,
|
|
2508
2512
|
embedMissingObservations: () => embedMissingObservations,
|
|
2509
2513
|
endSession: () => endSession,
|
|
2510
2514
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -3487,6 +3491,35 @@ function initMemorySchema(db) {
|
|
|
3487
3491
|
}
|
|
3488
3492
|
function enqueueSyncPayload(db, payload) {
|
|
3489
3493
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
3494
|
+
const overflow = db.prepare(
|
|
3495
|
+
`SELECT id, retry_count, last_error, payload FROM pending_sync
|
|
3496
|
+
WHERE created_at < datetime('now', ?)
|
|
3497
|
+
OR id NOT IN (SELECT id FROM pending_sync ORDER BY created_at DESC, id DESC LIMIT ?)
|
|
3498
|
+
LIMIT 10000`
|
|
3499
|
+
).all(`-${PENDING_SYNC_MAX_AGE_DAYS} days`, PENDING_SYNC_MAX_ITEMS);
|
|
3500
|
+
if (overflow.length > 0) {
|
|
3501
|
+
discardPendingSync(db, overflow, `over the queue limit (${PENDING_SYNC_MAX_ITEMS} items / ${PENDING_SYNC_MAX_AGE_DAYS} days)`);
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
function discardPendingSync(db, rows, why) {
|
|
3505
|
+
if (rows.length === 0) return 0;
|
|
3506
|
+
const rescued = rescueLearningFromStalePayloads(db, rows);
|
|
3507
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
3508
|
+
const ids = rows.slice(i, i + 500).map((r3) => r3.id);
|
|
3509
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
3510
|
+
}
|
|
3511
|
+
if (rescued > 0) {
|
|
3512
|
+
process.stderr.write(
|
|
3513
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${rows.length} discarded payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
3514
|
+
`
|
|
3515
|
+
);
|
|
3516
|
+
}
|
|
3517
|
+
process.stderr.write(`[massu] cloud sync: discarded ${rows.length} queued upload(s): ${why}.
|
|
3518
|
+
`);
|
|
3519
|
+
return rows.length;
|
|
3520
|
+
}
|
|
3521
|
+
function allPendingSync(db) {
|
|
3522
|
+
return db.prepare("SELECT id, retry_count, last_error, payload FROM pending_sync").all();
|
|
3490
3523
|
}
|
|
3491
3524
|
function getMemoryMeta(db, key) {
|
|
3492
3525
|
const row = db.prepare("SELECT value FROM memory_meta WHERE key = ?").get(key);
|
|
@@ -3689,7 +3722,9 @@ function dequeuePendingSync(db, limit = 10) {
|
|
|
3689
3722
|
}
|
|
3690
3723
|
}
|
|
3691
3724
|
return db.prepare(
|
|
3692
|
-
|
|
3725
|
+
// Least-retried first: oldest-first retried the same 10 items until they were discarded
|
|
3726
|
+
// while every newer item waited at retry 0 (2026-11-09: 43,492 never attempted).
|
|
3727
|
+
"SELECT id, payload, retry_count FROM pending_sync ORDER BY retry_count ASC, created_at ASC LIMIT ?"
|
|
3693
3728
|
).all(limit);
|
|
3694
3729
|
}
|
|
3695
3730
|
function removePendingSync(db, id) {
|
|
@@ -4362,7 +4397,7 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
4362
4397
|
}
|
|
4363
4398
|
return bestMatch;
|
|
4364
4399
|
}
|
|
4365
|
-
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
|
|
4400
|
+
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, PENDING_SYNC_MAX_ITEMS, PENDING_SYNC_MAX_AGE_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
|
|
4366
4401
|
var init_memory_db = __esm({
|
|
4367
4402
|
"src/memory-db.ts"() {
|
|
4368
4403
|
"use strict";
|
|
@@ -4377,6 +4412,8 @@ var init_memory_db = __esm({
|
|
|
4377
4412
|
init_memory_embed_sweep();
|
|
4378
4413
|
MEMORY_SCHEMA_VERSION = 2;
|
|
4379
4414
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
4415
|
+
PENDING_SYNC_MAX_ITEMS = 500;
|
|
4416
|
+
PENDING_SYNC_MAX_AGE_DAYS = 30;
|
|
4380
4417
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
4381
4418
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
4382
4419
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -16446,6 +16483,7 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
16446
16483
|
// src/commands/init.ts
|
|
16447
16484
|
var init_exports = {};
|
|
16448
16485
|
__export(init_exports, {
|
|
16486
|
+
SUPERSEDED_MASSU_HOOKS: () => SUPERSEDED_MASSU_HOOKS,
|
|
16449
16487
|
applyVariantTemplate: () => applyVariantTemplate,
|
|
16450
16488
|
buildConfigFromDetection: () => buildConfigFromDetection,
|
|
16451
16489
|
buildHooksConfig: () => buildHooksConfig,
|
|
@@ -16468,6 +16506,7 @@ __export(init_exports, {
|
|
|
16468
16506
|
mergeHooksConfig: () => mergeHooksConfig,
|
|
16469
16507
|
parseInitArgs: () => parseInitArgs,
|
|
16470
16508
|
printInitHelp: () => printInitHelp,
|
|
16509
|
+
refreshMcpServerPin: () => refreshMcpServerPin,
|
|
16471
16510
|
registerMcpServer: () => registerMcpServer,
|
|
16472
16511
|
renderConfigYaml: () => renderConfigYaml,
|
|
16473
16512
|
resolveHooksDir: () => resolveHooksDir,
|
|
@@ -17052,6 +17091,32 @@ function getInstallerVersion() {
|
|
|
17052
17091
|
"getInstallerVersion: could not resolve @massu/core package.json. This indicates a corrupt install. Re-install via `npx -y @massu/core init`."
|
|
17053
17092
|
);
|
|
17054
17093
|
}
|
|
17094
|
+
function refreshMcpServerPin(projectRoot) {
|
|
17095
|
+
const mcpPath = resolve12(projectRoot, ".mcp.json");
|
|
17096
|
+
if (!existsSync21(mcpPath)) return "absent";
|
|
17097
|
+
let doc;
|
|
17098
|
+
try {
|
|
17099
|
+
doc = JSON.parse(readFileSync17(mcpPath, "utf-8"));
|
|
17100
|
+
} catch {
|
|
17101
|
+
return "custom";
|
|
17102
|
+
}
|
|
17103
|
+
const servers = doc.mcpServers ?? {};
|
|
17104
|
+
const cur = servers.massu;
|
|
17105
|
+
if (!cur) return "absent";
|
|
17106
|
+
const args2 = Array.isArray(cur.args) ? cur.args.map(String) : [];
|
|
17107
|
+
const SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
17108
|
+
const shimForm = typeof cur.command === "string" && /[/\\]massu-hook$/.test(cur.command) && args2.length === 1 && SEMVER.test(args2[0]);
|
|
17109
|
+
const npxForm = cur.command === "npx" && args2.length === 2 && args2[0] === "-y" && /^@massu\/core@/.test(args2[1]);
|
|
17110
|
+
if (!shimForm && !npxForm) return "custom";
|
|
17111
|
+
const version = getInstallerVersion();
|
|
17112
|
+
const shim = resolveMassuShim();
|
|
17113
|
+
const next = shim !== null ? { type: "stdio", command: shim, args: [version] } : { type: "stdio", command: "npx", args: ["-y", `@massu/core@${version}`] };
|
|
17114
|
+
if (JSON.stringify({ ...cur, ...next }) === JSON.stringify(cur)) return "current";
|
|
17115
|
+
servers.massu = { ...cur, ...next };
|
|
17116
|
+
doc.mcpServers = servers;
|
|
17117
|
+
writeFileSync5(mcpPath, JSON.stringify(doc, null, 2) + "\n", "utf-8");
|
|
17118
|
+
return "updated";
|
|
17119
|
+
}
|
|
17055
17120
|
function registerMcpServer(projectRoot) {
|
|
17056
17121
|
const mcpPath = resolve12(projectRoot, ".mcp.json");
|
|
17057
17122
|
let existing = {};
|
|
@@ -17316,12 +17381,33 @@ function mergeHooksConfig(existing, additions) {
|
|
|
17316
17381
|
}
|
|
17317
17382
|
merged[event] = Array.from(byMatcher.values());
|
|
17318
17383
|
}
|
|
17384
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
17385
|
+
for (const groups of Object.values(additions ?? {})) {
|
|
17386
|
+
for (const g2 of groups) {
|
|
17387
|
+
for (const h2 of g2.hooks ?? []) {
|
|
17388
|
+
const id = typeof h2?.command === "string" ? massuHookIdentity(h2.command) : null;
|
|
17389
|
+
if (id) emitted.add(id);
|
|
17390
|
+
}
|
|
17391
|
+
}
|
|
17392
|
+
}
|
|
17393
|
+
for (const event of Object.keys(merged)) {
|
|
17394
|
+
merged[event] = merged[event].map((g2) => ({
|
|
17395
|
+
...g2,
|
|
17396
|
+
hooks: (g2.hooks ?? []).filter((h2) => {
|
|
17397
|
+
const id = typeof h2?.command === "string" ? massuHookIdentity(h2.command) : null;
|
|
17398
|
+
const by = id ? SUPERSEDED_MASSU_HOOKS[id] : void 0;
|
|
17399
|
+
return !(by && emitted.has(by));
|
|
17400
|
+
})
|
|
17401
|
+
})).filter((g2) => (g2.hooks ?? []).length > 0);
|
|
17402
|
+
}
|
|
17319
17403
|
return merged;
|
|
17320
17404
|
}
|
|
17321
17405
|
function massuHookIdentity(command) {
|
|
17322
17406
|
if (!/@massu\/core|[/\\]\.?massu[/\\]/.test(command)) return null;
|
|
17323
17407
|
const m3 = /\bhook-runner\s+([A-Za-z0-9._-]+)/.exec(command);
|
|
17324
|
-
|
|
17408
|
+
if (m3) return m3[1];
|
|
17409
|
+
const legacy = /@massu[/\\]core[/\\]dist[/\\]hooks[/\\]([a-z0-9-]+)\.js/.exec(command);
|
|
17410
|
+
return legacy ? legacy[1] : null;
|
|
17325
17411
|
}
|
|
17326
17412
|
function mergeHookEntries(existing, additions) {
|
|
17327
17413
|
const seenCommands = /* @__PURE__ */ new Set();
|
|
@@ -17747,7 +17833,7 @@ async function promptStackConfirm() {
|
|
|
17747
17833
|
function capitalize(str) {
|
|
17748
17834
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
17749
17835
|
}
|
|
17750
|
-
var __filename2, __dirname2, FRAMEWORK_TO_TEMPLATE_ID, TEMPLATE_NAMES;
|
|
17836
|
+
var __filename2, __dirname2, FRAMEWORK_TO_TEMPLATE_ID, TEMPLATE_NAMES, SUPERSEDED_MASSU_HOOKS;
|
|
17751
17837
|
var init_init = __esm({
|
|
17752
17838
|
"src/commands/init.ts"() {
|
|
17753
17839
|
"use strict";
|
|
@@ -17780,6 +17866,10 @@ var init_init = __esm({
|
|
|
17780
17866
|
"swift-ios",
|
|
17781
17867
|
"multi-runtime"
|
|
17782
17868
|
];
|
|
17869
|
+
SUPERSEDED_MASSU_HOOKS = Object.freeze({
|
|
17870
|
+
"security-gate": "pre-tool-use-gate",
|
|
17871
|
+
"pre-delete-check": "pre-tool-use-gate"
|
|
17872
|
+
});
|
|
17783
17873
|
}
|
|
17784
17874
|
});
|
|
17785
17875
|
|
|
@@ -19319,6 +19409,10 @@ async function runInstallHooks() {
|
|
|
19319
19409
|
}
|
|
19320
19410
|
const { count } = installHooks(projectRoot);
|
|
19321
19411
|
console.log(` Installed ${count} hooks in .claude/settings.local.json`);
|
|
19412
|
+
const mcp = refreshMcpServerPin(projectRoot);
|
|
19413
|
+
if (mcp === "updated") console.log(` Updated the massu MCP server in .mcp.json to ${version} (commit this file)`);
|
|
19414
|
+
else if (mcp === "current") console.log(` .mcp.json already runs massu ${version}`);
|
|
19415
|
+
else if (mcp === "custom") console.log(" .mcp.json has a hand-written massu entry \u2014 left unchanged; check its version");
|
|
19322
19416
|
console.log("");
|
|
19323
19417
|
console.log("Hooks will activate on your next Claude Code session.");
|
|
19324
19418
|
console.log("");
|
|
@@ -2086,6 +2086,8 @@ __export(memory_db_exports, {
|
|
|
2086
2086
|
MEMORY_FILE_TITLE_LIKE: () => MEMORY_FILE_TITLE_LIKE,
|
|
2087
2087
|
MEMORY_FILE_TITLE_PREFIX: () => MEMORY_FILE_TITLE_PREFIX,
|
|
2088
2088
|
MEMORY_SCHEMA_VERSION: () => MEMORY_SCHEMA_VERSION,
|
|
2089
|
+
PENDING_SYNC_MAX_AGE_DAYS: () => PENDING_SYNC_MAX_AGE_DAYS,
|
|
2090
|
+
PENDING_SYNC_MAX_ITEMS: () => PENDING_SYNC_MAX_ITEMS,
|
|
2089
2091
|
TOOL_COST_EVENTS_RETENTION_DAYS: () => TOOL_COST_EVENTS_RETENTION_DAYS,
|
|
2090
2092
|
USAGE_COUNTER_ARMED_KEY: () => USAGE_COUNTER_ARMED_KEY,
|
|
2091
2093
|
addConversationTurn: () => addConversationTurn,
|
|
@@ -2094,6 +2096,7 @@ __export(memory_db_exports, {
|
|
|
2094
2096
|
addSummary: () => addSummary,
|
|
2095
2097
|
addToolCallDetail: () => addToolCallDetail,
|
|
2096
2098
|
addUserPrompt: () => addUserPrompt,
|
|
2099
|
+
allPendingSync: () => allPendingSync,
|
|
2097
2100
|
appendIncidentToFailureClass: () => appendIncidentToFailureClass,
|
|
2098
2101
|
armUsageCounter: () => armUsageCounter,
|
|
2099
2102
|
assignImportance: () => assignImportance,
|
|
@@ -2101,6 +2104,7 @@ __export(memory_db_exports, {
|
|
|
2101
2104
|
createSession: () => createSession,
|
|
2102
2105
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
2103
2106
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
2107
|
+
discardPendingSync: () => discardPendingSync,
|
|
2104
2108
|
embedMissingObservations: () => embedMissingObservations,
|
|
2105
2109
|
endSession: () => endSession,
|
|
2106
2110
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -3083,6 +3087,35 @@ function initMemorySchema(db) {
|
|
|
3083
3087
|
}
|
|
3084
3088
|
function enqueueSyncPayload(db, payload) {
|
|
3085
3089
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
3090
|
+
const overflow = db.prepare(
|
|
3091
|
+
`SELECT id, retry_count, last_error, payload FROM pending_sync
|
|
3092
|
+
WHERE created_at < datetime('now', ?)
|
|
3093
|
+
OR id NOT IN (SELECT id FROM pending_sync ORDER BY created_at DESC, id DESC LIMIT ?)
|
|
3094
|
+
LIMIT 10000`
|
|
3095
|
+
).all(`-${PENDING_SYNC_MAX_AGE_DAYS} days`, PENDING_SYNC_MAX_ITEMS);
|
|
3096
|
+
if (overflow.length > 0) {
|
|
3097
|
+
discardPendingSync(db, overflow, `over the queue limit (${PENDING_SYNC_MAX_ITEMS} items / ${PENDING_SYNC_MAX_AGE_DAYS} days)`);
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
function discardPendingSync(db, rows, why) {
|
|
3101
|
+
if (rows.length === 0) return 0;
|
|
3102
|
+
const rescued = rescueLearningFromStalePayloads(db, rows);
|
|
3103
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
3104
|
+
const ids = rows.slice(i, i + 500).map((r) => r.id);
|
|
3105
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
3106
|
+
}
|
|
3107
|
+
if (rescued > 0) {
|
|
3108
|
+
process.stderr.write(
|
|
3109
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${rows.length} discarded payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
3110
|
+
`
|
|
3111
|
+
);
|
|
3112
|
+
}
|
|
3113
|
+
process.stderr.write(`[massu] cloud sync: discarded ${rows.length} queued upload(s): ${why}.
|
|
3114
|
+
`);
|
|
3115
|
+
return rows.length;
|
|
3116
|
+
}
|
|
3117
|
+
function allPendingSync(db) {
|
|
3118
|
+
return db.prepare("SELECT id, retry_count, last_error, payload FROM pending_sync").all();
|
|
3086
3119
|
}
|
|
3087
3120
|
function getMemoryMeta(db, key) {
|
|
3088
3121
|
const row = db.prepare("SELECT value FROM memory_meta WHERE key = ?").get(key);
|
|
@@ -3285,7 +3318,9 @@ function dequeuePendingSync(db, limit = 10) {
|
|
|
3285
3318
|
}
|
|
3286
3319
|
}
|
|
3287
3320
|
return db.prepare(
|
|
3288
|
-
|
|
3321
|
+
// Least-retried first: oldest-first retried the same 10 items until they were discarded
|
|
3322
|
+
// while every newer item waited at retry 0 (2026-11-09: 43,492 never attempted).
|
|
3323
|
+
"SELECT id, payload, retry_count FROM pending_sync ORDER BY retry_count ASC, created_at ASC LIMIT ?"
|
|
3289
3324
|
).all(limit);
|
|
3290
3325
|
}
|
|
3291
3326
|
function removePendingSync(db, id) {
|
|
@@ -3958,7 +3993,7 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
3958
3993
|
}
|
|
3959
3994
|
return bestMatch;
|
|
3960
3995
|
}
|
|
3961
|
-
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
|
|
3996
|
+
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, PENDING_SYNC_MAX_ITEMS, PENDING_SYNC_MAX_AGE_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
|
|
3962
3997
|
var init_memory_db = __esm({
|
|
3963
3998
|
"src/memory-db.ts"() {
|
|
3964
3999
|
"use strict";
|
|
@@ -3973,6 +4008,8 @@ var init_memory_db = __esm({
|
|
|
3973
4008
|
init_memory_embed_sweep();
|
|
3974
4009
|
MEMORY_SCHEMA_VERSION = 2;
|
|
3975
4010
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
4011
|
+
PENDING_SYNC_MAX_ITEMS = 500;
|
|
4012
|
+
PENDING_SYNC_MAX_AGE_DAYS = 30;
|
|
3976
4013
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
3977
4014
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
3978
4015
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -4058,7 +4095,7 @@ var init_hook_failure_signal = __esm({
|
|
|
4058
4095
|
init_config();
|
|
4059
4096
|
import { execFileSync } from "child_process";
|
|
4060
4097
|
import { createHash } from "crypto";
|
|
4061
|
-
import { existsSync as
|
|
4098
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync2, statSync as statSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7 } from "fs";
|
|
4062
4099
|
import { tmpdir as tmpdir2 } from "os";
|
|
4063
4100
|
import { join as join6, dirname as dirname8 } from "path";
|
|
4064
4101
|
|
|
@@ -4100,20 +4137,48 @@ function writeHookContext(hookEventName, message) {
|
|
|
4100
4137
|
// src/hooks/auto-learning-pipeline.ts
|
|
4101
4138
|
init_hook_failure_signal();
|
|
4102
4139
|
|
|
4140
|
+
// src/lib/safe-write.ts
|
|
4141
|
+
import {
|
|
4142
|
+
writeFileSync as writeFileSync3,
|
|
4143
|
+
renameSync,
|
|
4144
|
+
openSync as openSync2,
|
|
4145
|
+
fsyncSync as fsyncSync2,
|
|
4146
|
+
closeSync as closeSync2,
|
|
4147
|
+
existsSync as existsSync9,
|
|
4148
|
+
realpathSync,
|
|
4149
|
+
unlinkSync as unlinkSync2,
|
|
4150
|
+
accessSync as accessSync2,
|
|
4151
|
+
statSync as statSync2,
|
|
4152
|
+
chmodSync as chmodSync3,
|
|
4153
|
+
constants
|
|
4154
|
+
} from "fs";
|
|
4155
|
+
function isSameRealPath(a, b) {
|
|
4156
|
+
try {
|
|
4157
|
+
return realpathSync(a) === realpathSync(b);
|
|
4158
|
+
} catch {
|
|
4159
|
+
return false;
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
|
|
4103
4163
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4104
|
-
import { pathToFileURL } from "url";
|
|
4164
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4105
4165
|
function isDirectInvocation(moduleUrl) {
|
|
4106
4166
|
const entry = process.argv[1];
|
|
4107
4167
|
if (!entry) return false;
|
|
4108
4168
|
try {
|
|
4109
|
-
|
|
4169
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4170
|
+
} catch {
|
|
4171
|
+
return false;
|
|
4172
|
+
}
|
|
4173
|
+
try {
|
|
4174
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4110
4175
|
} catch {
|
|
4111
4176
|
return false;
|
|
4112
4177
|
}
|
|
4113
4178
|
}
|
|
4114
4179
|
|
|
4115
4180
|
// src/hooks/lib/session-touched-files.ts
|
|
4116
|
-
import { readFileSync as readFileSync6, existsSync as
|
|
4181
|
+
import { readFileSync as readFileSync6, existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
|
|
4117
4182
|
import { relative, isAbsolute, resolve as resolve5 } from "path";
|
|
4118
4183
|
var FILE_WRITING_TOOLS = Object.freeze({
|
|
4119
4184
|
Edit: ["file_path"],
|
|
@@ -4123,7 +4188,7 @@ var FILE_WRITING_TOOLS = Object.freeze({
|
|
|
4123
4188
|
});
|
|
4124
4189
|
function canonical(p) {
|
|
4125
4190
|
try {
|
|
4126
|
-
return
|
|
4191
|
+
return realpathSync2(p);
|
|
4127
4192
|
} catch {
|
|
4128
4193
|
return p;
|
|
4129
4194
|
}
|
|
@@ -4171,7 +4236,7 @@ function transcriptTouchedFiles(root, transcriptPath) {
|
|
|
4171
4236
|
return out;
|
|
4172
4237
|
}
|
|
4173
4238
|
function flagFileTouchedFiles(root, flagPath) {
|
|
4174
|
-
if (!
|
|
4239
|
+
if (!existsSync10(flagPath)) return null;
|
|
4175
4240
|
let raw;
|
|
4176
4241
|
try {
|
|
4177
4242
|
raw = readFileSync6(flagPath, "utf-8");
|
|
@@ -4592,7 +4657,7 @@ function readShownKeys(ackPath) {
|
|
|
4592
4657
|
}
|
|
4593
4658
|
function writeShownKeys(ackPath, keys) {
|
|
4594
4659
|
mkdirSync7(dirname8(ackPath), { recursive: true });
|
|
4595
|
-
|
|
4660
|
+
writeFileSync4(ackPath, JSON.stringify({ shown: [...keys].sort() }), "utf-8");
|
|
4596
4661
|
}
|
|
4597
4662
|
function freshMatches(matchKeys, shown) {
|
|
4598
4663
|
return [...new Set(matchKeys)].filter((k) => !shown.has(k));
|
|
@@ -4602,7 +4667,7 @@ function recentIncidentFiles(dir, nowMs = Date.now()) {
|
|
|
4602
4667
|
try {
|
|
4603
4668
|
return readdirSync2(dir).filter((f) => f.endsWith(".md")).filter((f) => {
|
|
4604
4669
|
try {
|
|
4605
|
-
return nowMs -
|
|
4670
|
+
return nowMs - statSync3(join6(dir, f)).mtimeMs <= RECENT_INCIDENT_WINDOW_MS;
|
|
4606
4671
|
} catch {
|
|
4607
4672
|
return false;
|
|
4608
4673
|
}
|
|
@@ -4616,7 +4681,7 @@ function readPostImageFrom(root) {
|
|
|
4616
4681
|
return (rel) => {
|
|
4617
4682
|
try {
|
|
4618
4683
|
const abs = join6(root, rel);
|
|
4619
|
-
if (
|
|
4684
|
+
if (statSync3(abs).size > MAX_POST_IMAGE_BYTES) return null;
|
|
4620
4685
|
return readFileSync7(abs, "utf-8");
|
|
4621
4686
|
} catch {
|
|
4622
4687
|
return null;
|
|
@@ -4729,7 +4794,7 @@ async function main() {
|
|
|
4729
4794
|
const autoLearn = config.autoLearning;
|
|
4730
4795
|
const flagPath = getSessionFlagPath(hookInput.session_id);
|
|
4731
4796
|
let sessionFixes = [];
|
|
4732
|
-
if (
|
|
4797
|
+
if (existsSync11(flagPath)) {
|
|
4733
4798
|
try {
|
|
4734
4799
|
sessionFixes = readFileSync7(flagPath, "utf-8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
4735
4800
|
} catch {
|
|
@@ -4821,16 +4886,16 @@ async function main() {
|
|
|
4821
4886
|
}
|
|
4822
4887
|
function cleanup(flagPath) {
|
|
4823
4888
|
try {
|
|
4824
|
-
if (
|
|
4889
|
+
if (existsSync11(flagPath)) unlinkSync3(flagPath);
|
|
4825
4890
|
const dir = join6(tmpdir2(), "massu-auto-learning");
|
|
4826
|
-
if (
|
|
4891
|
+
if (existsSync11(dir)) {
|
|
4827
4892
|
const now = Date.now();
|
|
4828
4893
|
for (const file of readdirSync2(dir)) {
|
|
4829
4894
|
const fullPath = join6(dir, file);
|
|
4830
4895
|
try {
|
|
4831
|
-
const stat =
|
|
4896
|
+
const stat = statSync3(fullPath);
|
|
4832
4897
|
if (now - stat.mtimeMs > 864e5) {
|
|
4833
|
-
|
|
4898
|
+
unlinkSync3(fullPath);
|
|
4834
4899
|
}
|
|
4835
4900
|
} catch {
|
|
4836
4901
|
}
|
|
@@ -2157,6 +2157,8 @@ __export(memory_db_exports, {
|
|
|
2157
2157
|
MEMORY_FILE_TITLE_LIKE: () => MEMORY_FILE_TITLE_LIKE,
|
|
2158
2158
|
MEMORY_FILE_TITLE_PREFIX: () => MEMORY_FILE_TITLE_PREFIX,
|
|
2159
2159
|
MEMORY_SCHEMA_VERSION: () => MEMORY_SCHEMA_VERSION,
|
|
2160
|
+
PENDING_SYNC_MAX_AGE_DAYS: () => PENDING_SYNC_MAX_AGE_DAYS,
|
|
2161
|
+
PENDING_SYNC_MAX_ITEMS: () => PENDING_SYNC_MAX_ITEMS,
|
|
2160
2162
|
TOOL_COST_EVENTS_RETENTION_DAYS: () => TOOL_COST_EVENTS_RETENTION_DAYS,
|
|
2161
2163
|
USAGE_COUNTER_ARMED_KEY: () => USAGE_COUNTER_ARMED_KEY,
|
|
2162
2164
|
addConversationTurn: () => addConversationTurn,
|
|
@@ -2165,6 +2167,7 @@ __export(memory_db_exports, {
|
|
|
2165
2167
|
addSummary: () => addSummary,
|
|
2166
2168
|
addToolCallDetail: () => addToolCallDetail,
|
|
2167
2169
|
addUserPrompt: () => addUserPrompt,
|
|
2170
|
+
allPendingSync: () => allPendingSync,
|
|
2168
2171
|
appendIncidentToFailureClass: () => appendIncidentToFailureClass,
|
|
2169
2172
|
armUsageCounter: () => armUsageCounter,
|
|
2170
2173
|
assignImportance: () => assignImportance,
|
|
@@ -2172,6 +2175,7 @@ __export(memory_db_exports, {
|
|
|
2172
2175
|
createSession: () => createSession,
|
|
2173
2176
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
2174
2177
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
2178
|
+
discardPendingSync: () => discardPendingSync,
|
|
2175
2179
|
embedMissingObservations: () => embedMissingObservations,
|
|
2176
2180
|
endSession: () => endSession,
|
|
2177
2181
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -3154,6 +3158,35 @@ function initMemorySchema(db) {
|
|
|
3154
3158
|
}
|
|
3155
3159
|
function enqueueSyncPayload(db, payload) {
|
|
3156
3160
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
3161
|
+
const overflow = db.prepare(
|
|
3162
|
+
`SELECT id, retry_count, last_error, payload FROM pending_sync
|
|
3163
|
+
WHERE created_at < datetime('now', ?)
|
|
3164
|
+
OR id NOT IN (SELECT id FROM pending_sync ORDER BY created_at DESC, id DESC LIMIT ?)
|
|
3165
|
+
LIMIT 10000`
|
|
3166
|
+
).all(`-${PENDING_SYNC_MAX_AGE_DAYS} days`, PENDING_SYNC_MAX_ITEMS);
|
|
3167
|
+
if (overflow.length > 0) {
|
|
3168
|
+
discardPendingSync(db, overflow, `over the queue limit (${PENDING_SYNC_MAX_ITEMS} items / ${PENDING_SYNC_MAX_AGE_DAYS} days)`);
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
function discardPendingSync(db, rows, why) {
|
|
3172
|
+
if (rows.length === 0) return 0;
|
|
3173
|
+
const rescued = rescueLearningFromStalePayloads(db, rows);
|
|
3174
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
3175
|
+
const ids = rows.slice(i, i + 500).map((r) => r.id);
|
|
3176
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
3177
|
+
}
|
|
3178
|
+
if (rescued > 0) {
|
|
3179
|
+
process.stderr.write(
|
|
3180
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${rows.length} discarded payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
3181
|
+
`
|
|
3182
|
+
);
|
|
3183
|
+
}
|
|
3184
|
+
process.stderr.write(`[massu] cloud sync: discarded ${rows.length} queued upload(s): ${why}.
|
|
3185
|
+
`);
|
|
3186
|
+
return rows.length;
|
|
3187
|
+
}
|
|
3188
|
+
function allPendingSync(db) {
|
|
3189
|
+
return db.prepare("SELECT id, retry_count, last_error, payload FROM pending_sync").all();
|
|
3157
3190
|
}
|
|
3158
3191
|
function getMemoryMeta(db, key) {
|
|
3159
3192
|
const row = db.prepare("SELECT value FROM memory_meta WHERE key = ?").get(key);
|
|
@@ -3356,7 +3389,9 @@ function dequeuePendingSync(db, limit = 10) {
|
|
|
3356
3389
|
}
|
|
3357
3390
|
}
|
|
3358
3391
|
return db.prepare(
|
|
3359
|
-
|
|
3392
|
+
// Least-retried first: oldest-first retried the same 10 items until they were discarded
|
|
3393
|
+
// while every newer item waited at retry 0 (2026-11-09: 43,492 never attempted).
|
|
3394
|
+
"SELECT id, payload, retry_count FROM pending_sync ORDER BY retry_count ASC, created_at ASC LIMIT ?"
|
|
3360
3395
|
).all(limit);
|
|
3361
3396
|
}
|
|
3362
3397
|
function removePendingSync(db, id) {
|
|
@@ -4029,7 +4064,7 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
4029
4064
|
}
|
|
4030
4065
|
return bestMatch;
|
|
4031
4066
|
}
|
|
4032
|
-
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
|
|
4067
|
+
var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, PENDING_SYNC_MAX_ITEMS, PENDING_SYNC_MAX_AGE_DAYS, MAX_DRAFT_TEXT_LEN, FUNNEL_EVENT_OUTBOX_CAP, _temporalColCache, CONSOLIDATION_LESSON_EVIDENCE, MEMORY_FILE_TITLE_PREFIX, MEMORY_FILE_TITLE_LIKE, USAGE_COUNTER_ARMED_KEY;
|
|
4033
4068
|
var init_memory_db = __esm({
|
|
4034
4069
|
"src/memory-db.ts"() {
|
|
4035
4070
|
"use strict";
|
|
@@ -4044,6 +4079,8 @@ var init_memory_db = __esm({
|
|
|
4044
4079
|
init_memory_embed_sweep();
|
|
4045
4080
|
MEMORY_SCHEMA_VERSION = 2;
|
|
4046
4081
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
4082
|
+
PENDING_SYNC_MAX_ITEMS = 500;
|
|
4083
|
+
PENDING_SYNC_MAX_AGE_DAYS = 30;
|
|
4047
4084
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
4048
4085
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
4049
4086
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -4057,7 +4094,7 @@ var init_memory_db = __esm({
|
|
|
4057
4094
|
// src/hooks/classify-failure.ts
|
|
4058
4095
|
init_config();
|
|
4059
4096
|
init_memory_db();
|
|
4060
|
-
import { existsSync as
|
|
4097
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, readdirSync as readdirSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
4061
4098
|
import { tmpdir as tmpdir2 } from "os";
|
|
4062
4099
|
import { join as join6, basename as basename3 } from "path";
|
|
4063
4100
|
|
|
@@ -4109,13 +4146,41 @@ function writeHookContext(hookEventName, message) {
|
|
|
4109
4146
|
// src/hooks/classify-failure.ts
|
|
4110
4147
|
init_hook_failure_signal();
|
|
4111
4148
|
|
|
4149
|
+
// src/lib/safe-write.ts
|
|
4150
|
+
import {
|
|
4151
|
+
writeFileSync as writeFileSync3,
|
|
4152
|
+
renameSync,
|
|
4153
|
+
openSync as openSync2,
|
|
4154
|
+
fsyncSync as fsyncSync2,
|
|
4155
|
+
closeSync as closeSync2,
|
|
4156
|
+
existsSync as existsSync9,
|
|
4157
|
+
realpathSync,
|
|
4158
|
+
unlinkSync as unlinkSync2,
|
|
4159
|
+
accessSync as accessSync2,
|
|
4160
|
+
statSync as statSync2,
|
|
4161
|
+
chmodSync as chmodSync3,
|
|
4162
|
+
constants
|
|
4163
|
+
} from "fs";
|
|
4164
|
+
function isSameRealPath(a, b) {
|
|
4165
|
+
try {
|
|
4166
|
+
return realpathSync(a) === realpathSync(b);
|
|
4167
|
+
} catch {
|
|
4168
|
+
return false;
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
|
|
4112
4172
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4113
|
-
import { pathToFileURL } from "url";
|
|
4173
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4114
4174
|
function isDirectInvocation(moduleUrl) {
|
|
4115
4175
|
const entry = process.argv[1];
|
|
4116
4176
|
if (!entry) return false;
|
|
4117
4177
|
try {
|
|
4118
|
-
|
|
4178
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4179
|
+
} catch {
|
|
4180
|
+
return false;
|
|
4181
|
+
}
|
|
4182
|
+
try {
|
|
4183
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4119
4184
|
} catch {
|
|
4120
4185
|
return false;
|
|
4121
4186
|
}
|
|
@@ -4160,7 +4225,7 @@ function cleanupFailureContextFiles() {
|
|
|
4160
4225
|
const files = readdirSync2(dir).filter((f) => f.startsWith("massu-failure-context-"));
|
|
4161
4226
|
for (const file of files) {
|
|
4162
4227
|
try {
|
|
4163
|
-
|
|
4228
|
+
unlinkSync3(join6(dir, file));
|
|
4164
4229
|
} catch {
|
|
4165
4230
|
}
|
|
4166
4231
|
}
|
|
@@ -4226,12 +4291,12 @@ async function main() {
|
|
|
4226
4291
|
return;
|
|
4227
4292
|
}
|
|
4228
4293
|
const dedupeMarker = getDedupeMarkerPath(hookInput.session_id, filePath);
|
|
4229
|
-
if (
|
|
4294
|
+
if (existsSync10(dedupeMarker)) {
|
|
4230
4295
|
process.exit(0);
|
|
4231
4296
|
return;
|
|
4232
4297
|
}
|
|
4233
4298
|
try {
|
|
4234
|
-
|
|
4299
|
+
writeFileSync4(dedupeMarker, "1");
|
|
4235
4300
|
} catch {
|
|
4236
4301
|
}
|
|
4237
4302
|
const db = getMemoryDb();
|