@massu/core 2.5.5 → 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 +63 -0
- package/dist/cli.js +167 -46
- package/dist/hooks/auto-learning-pipeline.js +81 -16
- package/dist/hooks/classify-failure.js +90 -652
- package/dist/hooks/cost-tracker.js +69 -4
- package/dist/hooks/fix-detector.js +79 -12
- 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 +446 -71
- package/dist/hooks/pre-tool-use-gate.js +237 -223
- package/dist/hooks/quality-event.js +69 -4
- package/dist/hooks/rule-enforcement-pipeline.js +72 -7
- package/dist/hooks/security-gate.js +611 -181
- package/dist/hooks/session-end.js +607 -73
- package/dist/hooks/session-start.js +112 -8
- package/dist/hooks/user-prompt.js +83 -18
- package/package.json +1 -1
|
@@ -2193,6 +2193,8 @@ __export(memory_db_exports, {
|
|
|
2193
2193
|
MEMORY_FILE_TITLE_LIKE: () => MEMORY_FILE_TITLE_LIKE,
|
|
2194
2194
|
MEMORY_FILE_TITLE_PREFIX: () => MEMORY_FILE_TITLE_PREFIX,
|
|
2195
2195
|
MEMORY_SCHEMA_VERSION: () => MEMORY_SCHEMA_VERSION,
|
|
2196
|
+
PENDING_SYNC_MAX_AGE_DAYS: () => PENDING_SYNC_MAX_AGE_DAYS,
|
|
2197
|
+
PENDING_SYNC_MAX_ITEMS: () => PENDING_SYNC_MAX_ITEMS,
|
|
2196
2198
|
TOOL_COST_EVENTS_RETENTION_DAYS: () => TOOL_COST_EVENTS_RETENTION_DAYS,
|
|
2197
2199
|
USAGE_COUNTER_ARMED_KEY: () => USAGE_COUNTER_ARMED_KEY,
|
|
2198
2200
|
addConversationTurn: () => addConversationTurn,
|
|
@@ -2201,6 +2203,7 @@ __export(memory_db_exports, {
|
|
|
2201
2203
|
addSummary: () => addSummary,
|
|
2202
2204
|
addToolCallDetail: () => addToolCallDetail,
|
|
2203
2205
|
addUserPrompt: () => addUserPrompt,
|
|
2206
|
+
allPendingSync: () => allPendingSync,
|
|
2204
2207
|
appendIncidentToFailureClass: () => appendIncidentToFailureClass,
|
|
2205
2208
|
armUsageCounter: () => armUsageCounter,
|
|
2206
2209
|
assignImportance: () => assignImportance,
|
|
@@ -2208,6 +2211,7 @@ __export(memory_db_exports, {
|
|
|
2208
2211
|
createSession: () => createSession,
|
|
2209
2212
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
2210
2213
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
2214
|
+
discardPendingSync: () => discardPendingSync,
|
|
2211
2215
|
embedMissingObservations: () => embedMissingObservations,
|
|
2212
2216
|
endSession: () => endSession,
|
|
2213
2217
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -3190,6 +3194,35 @@ function initMemorySchema(db) {
|
|
|
3190
3194
|
}
|
|
3191
3195
|
function enqueueSyncPayload(db, payload) {
|
|
3192
3196
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
3197
|
+
const overflow = db.prepare(
|
|
3198
|
+
`SELECT id, retry_count, last_error, payload FROM pending_sync
|
|
3199
|
+
WHERE created_at < datetime('now', ?)
|
|
3200
|
+
OR id NOT IN (SELECT id FROM pending_sync ORDER BY created_at DESC, id DESC LIMIT ?)
|
|
3201
|
+
LIMIT 10000`
|
|
3202
|
+
).all(`-${PENDING_SYNC_MAX_AGE_DAYS} days`, PENDING_SYNC_MAX_ITEMS);
|
|
3203
|
+
if (overflow.length > 0) {
|
|
3204
|
+
discardPendingSync(db, overflow, `over the queue limit (${PENDING_SYNC_MAX_ITEMS} items / ${PENDING_SYNC_MAX_AGE_DAYS} days)`);
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
function discardPendingSync(db, rows, why) {
|
|
3208
|
+
if (rows.length === 0) return 0;
|
|
3209
|
+
const rescued = rescueLearningFromStalePayloads(db, rows);
|
|
3210
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
3211
|
+
const ids = rows.slice(i, i + 500).map((r) => r.id);
|
|
3212
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
3213
|
+
}
|
|
3214
|
+
if (rescued > 0) {
|
|
3215
|
+
process.stderr.write(
|
|
3216
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${rows.length} discarded payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
3217
|
+
`
|
|
3218
|
+
);
|
|
3219
|
+
}
|
|
3220
|
+
process.stderr.write(`[massu] cloud sync: discarded ${rows.length} queued upload(s): ${why}.
|
|
3221
|
+
`);
|
|
3222
|
+
return rows.length;
|
|
3223
|
+
}
|
|
3224
|
+
function allPendingSync(db) {
|
|
3225
|
+
return db.prepare("SELECT id, retry_count, last_error, payload FROM pending_sync").all();
|
|
3193
3226
|
}
|
|
3194
3227
|
function getMemoryMeta(db, key) {
|
|
3195
3228
|
const row = db.prepare("SELECT value FROM memory_meta WHERE key = ?").get(key);
|
|
@@ -3392,7 +3425,9 @@ function dequeuePendingSync(db, limit = 10) {
|
|
|
3392
3425
|
}
|
|
3393
3426
|
}
|
|
3394
3427
|
return db.prepare(
|
|
3395
|
-
|
|
3428
|
+
// Least-retried first: oldest-first retried the same 10 items until they were discarded
|
|
3429
|
+
// while every newer item waited at retry 0 (2026-11-09: 43,492 never attempted).
|
|
3430
|
+
"SELECT id, payload, retry_count FROM pending_sync ORDER BY retry_count ASC, created_at ASC LIMIT ?"
|
|
3396
3431
|
).all(limit);
|
|
3397
3432
|
}
|
|
3398
3433
|
function removePendingSync(db, id) {
|
|
@@ -4065,7 +4100,7 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
4065
4100
|
}
|
|
4066
4101
|
return bestMatch;
|
|
4067
4102
|
}
|
|
4068
|
-
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;
|
|
4103
|
+
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;
|
|
4069
4104
|
var init_memory_db = __esm({
|
|
4070
4105
|
"src/memory-db.ts"() {
|
|
4071
4106
|
"use strict";
|
|
@@ -4080,6 +4115,8 @@ var init_memory_db = __esm({
|
|
|
4080
4115
|
init_memory_embed_sweep();
|
|
4081
4116
|
MEMORY_SCHEMA_VERSION = 2;
|
|
4082
4117
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
4118
|
+
PENDING_SYNC_MAX_ITEMS = 500;
|
|
4119
|
+
PENDING_SYNC_MAX_AGE_DAYS = 30;
|
|
4083
4120
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
4084
4121
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
4085
4122
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -4527,6 +4564,27 @@ init_config();
|
|
|
4527
4564
|
init_memory_db();
|
|
4528
4565
|
|
|
4529
4566
|
// src/lib/safe-write.ts
|
|
4567
|
+
import {
|
|
4568
|
+
writeFileSync as writeFileSync3,
|
|
4569
|
+
renameSync,
|
|
4570
|
+
openSync as openSync2,
|
|
4571
|
+
fsyncSync as fsyncSync2,
|
|
4572
|
+
closeSync as closeSync2,
|
|
4573
|
+
existsSync as existsSync9,
|
|
4574
|
+
realpathSync,
|
|
4575
|
+
unlinkSync as unlinkSync2,
|
|
4576
|
+
accessSync as accessSync2,
|
|
4577
|
+
statSync as statSync2,
|
|
4578
|
+
chmodSync as chmodSync3,
|
|
4579
|
+
constants
|
|
4580
|
+
} from "fs";
|
|
4581
|
+
function isSameRealPath(a, b) {
|
|
4582
|
+
try {
|
|
4583
|
+
return realpathSync(a) === realpathSync(b);
|
|
4584
|
+
} catch {
|
|
4585
|
+
return false;
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4530
4588
|
var SLUG_ALLOWED = /^[a-z0-9_]+$/;
|
|
4531
4589
|
function deriveSlug(input) {
|
|
4532
4590
|
const cleaned = input.toLowerCase().replace(/[^a-z0-9\s]+/g, " ").replace(/\s+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -4639,15 +4697,20 @@ function writeHookContext(hookEventName, message) {
|
|
|
4639
4697
|
// src/hooks/memory-recall.ts
|
|
4640
4698
|
init_db_driver();
|
|
4641
4699
|
init_hook_failure_signal();
|
|
4642
|
-
import { existsSync as
|
|
4700
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4643
4701
|
|
|
4644
4702
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4645
|
-
import { pathToFileURL } from "url";
|
|
4703
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4646
4704
|
function isDirectInvocation(moduleUrl) {
|
|
4647
4705
|
const entry = process.argv[1];
|
|
4648
4706
|
if (!entry) return false;
|
|
4649
4707
|
try {
|
|
4650
|
-
|
|
4708
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4709
|
+
} catch {
|
|
4710
|
+
return false;
|
|
4711
|
+
}
|
|
4712
|
+
try {
|
|
4713
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4651
4714
|
} catch {
|
|
4652
4715
|
return false;
|
|
4653
4716
|
}
|
|
@@ -4690,6 +4753,12 @@ function withTimeout(p, ms) {
|
|
|
4690
4753
|
]);
|
|
4691
4754
|
}
|
|
4692
4755
|
async function main() {
|
|
4756
|
+
if (process.argv.includes("--warm-cache")) {
|
|
4757
|
+
const r = await warmCache();
|
|
4758
|
+
process.stderr.write(`warm-cache: ${r}
|
|
4759
|
+
`);
|
|
4760
|
+
return void process.exit(0);
|
|
4761
|
+
}
|
|
4693
4762
|
try {
|
|
4694
4763
|
const input = await readStdin();
|
|
4695
4764
|
if (!input.trim()) return void process.exit(0);
|
|
@@ -4724,7 +4793,7 @@ async function computeRecall(prompt, cfg, sessionId) {
|
|
|
4724
4793
|
);
|
|
4725
4794
|
memDb = getMemoryDb();
|
|
4726
4795
|
const knowledgeDbPath = getResolvedPaths().knowledgeDbPath;
|
|
4727
|
-
if (cfg.sources.includes("knowledge_chunk") &&
|
|
4796
|
+
if (cfg.sources.includes("knowledge_chunk") && existsSync10(knowledgeDbPath)) {
|
|
4728
4797
|
try {
|
|
4729
4798
|
knowledgeDb = openDatabase2(knowledgeDbPath, { readonly: true, selfHeal: false });
|
|
4730
4799
|
} catch {
|
|
@@ -4806,6 +4875,20 @@ function readStdin() {
|
|
|
4806
4875
|
setTimeout(() => resolve5(data), 3e3);
|
|
4807
4876
|
});
|
|
4808
4877
|
}
|
|
4878
|
+
var WARM_CACHE_BUDGET_MS = 8e3;
|
|
4879
|
+
async function warmCache() {
|
|
4880
|
+
const timer = new Promise((resolve5) => setTimeout(() => resolve5(null), WARM_CACHE_BUDGET_MS).unref());
|
|
4881
|
+
try {
|
|
4882
|
+
const v = await Promise.race([embed("warm-up"), timer]);
|
|
4883
|
+
return v ? "ok" : "null";
|
|
4884
|
+
} catch {
|
|
4885
|
+
return "null";
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4809
4888
|
if (isDirectInvocation(import.meta.url)) {
|
|
4810
4889
|
main();
|
|
4811
4890
|
}
|
|
4891
|
+
export {
|
|
4892
|
+
WARM_CACHE_BUDGET_MS,
|
|
4893
|
+
warmCache
|
|
4894
|
+
};
|
|
@@ -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();
|
|
@@ -4179,13 +4216,41 @@ function writeHookContext(hookEventName, message) {
|
|
|
4179
4216
|
// src/hooks/post-edit-context.ts
|
|
4180
4217
|
init_hook_failure_signal();
|
|
4181
4218
|
|
|
4219
|
+
// src/lib/safe-write.ts
|
|
4220
|
+
import {
|
|
4221
|
+
writeFileSync as writeFileSync3,
|
|
4222
|
+
renameSync,
|
|
4223
|
+
openSync as openSync2,
|
|
4224
|
+
fsyncSync as fsyncSync2,
|
|
4225
|
+
closeSync as closeSync2,
|
|
4226
|
+
existsSync as existsSync9,
|
|
4227
|
+
realpathSync,
|
|
4228
|
+
unlinkSync as unlinkSync2,
|
|
4229
|
+
accessSync as accessSync2,
|
|
4230
|
+
statSync as statSync2,
|
|
4231
|
+
chmodSync as chmodSync3,
|
|
4232
|
+
constants
|
|
4233
|
+
} from "fs";
|
|
4234
|
+
function isSameRealPath(a, b) {
|
|
4235
|
+
try {
|
|
4236
|
+
return realpathSync(a) === realpathSync(b);
|
|
4237
|
+
} catch {
|
|
4238
|
+
return false;
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
|
|
4182
4242
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4183
|
-
import { pathToFileURL } from "url";
|
|
4243
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4184
4244
|
function isDirectInvocation(moduleUrl) {
|
|
4185
4245
|
const entry = process.argv[1];
|
|
4186
4246
|
if (!entry) return false;
|
|
4187
4247
|
try {
|
|
4188
|
-
|
|
4248
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4249
|
+
} catch {
|
|
4250
|
+
return false;
|
|
4251
|
+
}
|
|
4252
|
+
try {
|
|
4253
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4189
4254
|
} catch {
|
|
4190
4255
|
return false;
|
|
4191
4256
|
}
|
|
@@ -2163,6 +2163,8 @@ __export(memory_db_exports, {
|
|
|
2163
2163
|
MEMORY_FILE_TITLE_LIKE: () => MEMORY_FILE_TITLE_LIKE,
|
|
2164
2164
|
MEMORY_FILE_TITLE_PREFIX: () => MEMORY_FILE_TITLE_PREFIX,
|
|
2165
2165
|
MEMORY_SCHEMA_VERSION: () => MEMORY_SCHEMA_VERSION,
|
|
2166
|
+
PENDING_SYNC_MAX_AGE_DAYS: () => PENDING_SYNC_MAX_AGE_DAYS,
|
|
2167
|
+
PENDING_SYNC_MAX_ITEMS: () => PENDING_SYNC_MAX_ITEMS,
|
|
2166
2168
|
TOOL_COST_EVENTS_RETENTION_DAYS: () => TOOL_COST_EVENTS_RETENTION_DAYS,
|
|
2167
2169
|
USAGE_COUNTER_ARMED_KEY: () => USAGE_COUNTER_ARMED_KEY,
|
|
2168
2170
|
addConversationTurn: () => addConversationTurn,
|
|
@@ -2171,6 +2173,7 @@ __export(memory_db_exports, {
|
|
|
2171
2173
|
addSummary: () => addSummary,
|
|
2172
2174
|
addToolCallDetail: () => addToolCallDetail,
|
|
2173
2175
|
addUserPrompt: () => addUserPrompt,
|
|
2176
|
+
allPendingSync: () => allPendingSync,
|
|
2174
2177
|
appendIncidentToFailureClass: () => appendIncidentToFailureClass,
|
|
2175
2178
|
armUsageCounter: () => armUsageCounter,
|
|
2176
2179
|
assignImportance: () => assignImportance,
|
|
@@ -2178,6 +2181,7 @@ __export(memory_db_exports, {
|
|
|
2178
2181
|
createSession: () => createSession,
|
|
2179
2182
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
2180
2183
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
2184
|
+
discardPendingSync: () => discardPendingSync,
|
|
2181
2185
|
embedMissingObservations: () => embedMissingObservations,
|
|
2182
2186
|
endSession: () => endSession,
|
|
2183
2187
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -3160,6 +3164,35 @@ function initMemorySchema(db) {
|
|
|
3160
3164
|
}
|
|
3161
3165
|
function enqueueSyncPayload(db, payload) {
|
|
3162
3166
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
3167
|
+
const overflow = db.prepare(
|
|
3168
|
+
`SELECT id, retry_count, last_error, payload FROM pending_sync
|
|
3169
|
+
WHERE created_at < datetime('now', ?)
|
|
3170
|
+
OR id NOT IN (SELECT id FROM pending_sync ORDER BY created_at DESC, id DESC LIMIT ?)
|
|
3171
|
+
LIMIT 10000`
|
|
3172
|
+
).all(`-${PENDING_SYNC_MAX_AGE_DAYS} days`, PENDING_SYNC_MAX_ITEMS);
|
|
3173
|
+
if (overflow.length > 0) {
|
|
3174
|
+
discardPendingSync(db, overflow, `over the queue limit (${PENDING_SYNC_MAX_ITEMS} items / ${PENDING_SYNC_MAX_AGE_DAYS} days)`);
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
function discardPendingSync(db, rows, why) {
|
|
3178
|
+
if (rows.length === 0) return 0;
|
|
3179
|
+
const rescued = rescueLearningFromStalePayloads(db, rows);
|
|
3180
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
3181
|
+
const ids = rows.slice(i, i + 500).map((r) => r.id);
|
|
3182
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
3183
|
+
}
|
|
3184
|
+
if (rescued > 0) {
|
|
3185
|
+
process.stderr.write(
|
|
3186
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${rows.length} discarded payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
3187
|
+
`
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
process.stderr.write(`[massu] cloud sync: discarded ${rows.length} queued upload(s): ${why}.
|
|
3191
|
+
`);
|
|
3192
|
+
return rows.length;
|
|
3193
|
+
}
|
|
3194
|
+
function allPendingSync(db) {
|
|
3195
|
+
return db.prepare("SELECT id, retry_count, last_error, payload FROM pending_sync").all();
|
|
3163
3196
|
}
|
|
3164
3197
|
function getMemoryMeta(db, key) {
|
|
3165
3198
|
const row = db.prepare("SELECT value FROM memory_meta WHERE key = ?").get(key);
|
|
@@ -3362,7 +3395,9 @@ function dequeuePendingSync(db, limit = 10) {
|
|
|
3362
3395
|
}
|
|
3363
3396
|
}
|
|
3364
3397
|
return db.prepare(
|
|
3365
|
-
|
|
3398
|
+
// Least-retried first: oldest-first retried the same 10 items until they were discarded
|
|
3399
|
+
// while every newer item waited at retry 0 (2026-11-09: 43,492 never attempted).
|
|
3400
|
+
"SELECT id, payload, retry_count FROM pending_sync ORDER BY retry_count ASC, created_at ASC LIMIT ?"
|
|
3366
3401
|
).all(limit);
|
|
3367
3402
|
}
|
|
3368
3403
|
function removePendingSync(db, id) {
|
|
@@ -4035,7 +4070,7 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
4035
4070
|
}
|
|
4036
4071
|
return bestMatch;
|
|
4037
4072
|
}
|
|
4038
|
-
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;
|
|
4073
|
+
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;
|
|
4039
4074
|
var init_memory_db = __esm({
|
|
4040
4075
|
"src/memory-db.ts"() {
|
|
4041
4076
|
"use strict";
|
|
@@ -4050,6 +4085,8 @@ var init_memory_db = __esm({
|
|
|
4050
4085
|
init_memory_embed_sweep();
|
|
4051
4086
|
MEMORY_SCHEMA_VERSION = 2;
|
|
4052
4087
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
4088
|
+
PENDING_SYNC_MAX_ITEMS = 500;
|
|
4089
|
+
PENDING_SYNC_MAX_AGE_DAYS = 30;
|
|
4053
4090
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
4054
4091
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
4055
4092
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -4864,7 +4901,7 @@ function storeSecurityScore(db, sessionId, filePath, riskScore, findings) {
|
|
|
4864
4901
|
|
|
4865
4902
|
// src/hooks/post-tool-use.ts
|
|
4866
4903
|
init_config();
|
|
4867
|
-
import { readFileSync as readFileSync10, writeFileSync as
|
|
4904
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, existsSync as existsSync14, mkdirSync as mkdirSync7, statSync as statSync4 } from "fs";
|
|
4868
4905
|
import { join as join7, basename as pathBasename2, dirname as pathDirname, resolve as pathResolve } from "path";
|
|
4869
4906
|
|
|
4870
4907
|
// src/lib/recurrence-incrementer.ts
|
|
@@ -5121,13 +5158,41 @@ function mapMemoryTypeToObservationType(memoryType) {
|
|
|
5121
5158
|
}
|
|
5122
5159
|
}
|
|
5123
5160
|
|
|
5161
|
+
// src/lib/safe-write.ts
|
|
5162
|
+
import {
|
|
5163
|
+
writeFileSync as writeFileSync3,
|
|
5164
|
+
renameSync,
|
|
5165
|
+
openSync as openSync2,
|
|
5166
|
+
fsyncSync as fsyncSync2,
|
|
5167
|
+
closeSync as closeSync2,
|
|
5168
|
+
existsSync as existsSync13,
|
|
5169
|
+
realpathSync,
|
|
5170
|
+
unlinkSync as unlinkSync2,
|
|
5171
|
+
accessSync as accessSync2,
|
|
5172
|
+
statSync as statSync3,
|
|
5173
|
+
chmodSync as chmodSync3,
|
|
5174
|
+
constants
|
|
5175
|
+
} from "fs";
|
|
5176
|
+
function isSameRealPath(a, b) {
|
|
5177
|
+
try {
|
|
5178
|
+
return realpathSync(a) === realpathSync(b);
|
|
5179
|
+
} catch {
|
|
5180
|
+
return false;
|
|
5181
|
+
}
|
|
5182
|
+
}
|
|
5183
|
+
|
|
5124
5184
|
// src/hooks/lib/is-direct-invocation.ts
|
|
5125
|
-
import { pathToFileURL } from "url";
|
|
5185
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
5126
5186
|
function isDirectInvocation(moduleUrl) {
|
|
5127
5187
|
const entry = process.argv[1];
|
|
5128
5188
|
if (!entry) return false;
|
|
5129
5189
|
try {
|
|
5130
|
-
|
|
5190
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
5191
|
+
} catch {
|
|
5192
|
+
return false;
|
|
5193
|
+
}
|
|
5194
|
+
try {
|
|
5195
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
5131
5196
|
} catch {
|
|
5132
5197
|
return false;
|
|
5133
5198
|
}
|
|
@@ -5305,11 +5370,11 @@ async function main() {
|
|
|
5305
5370
|
try {
|
|
5306
5371
|
const projectRoot = process.cwd();
|
|
5307
5372
|
const dir = join7(projectRoot, ".massu", "rule-candidates");
|
|
5308
|
-
if (!
|
|
5373
|
+
if (!existsSync14(dir)) mkdirSync7(dir, { recursive: true });
|
|
5309
5374
|
const logPath = join7(dir, ".cr53-increment-failures.jsonl");
|
|
5310
|
-
const pre =
|
|
5375
|
+
const pre = existsSync14(logPath) ? readFileSync10(logPath, "utf-8") : "";
|
|
5311
5376
|
const sep = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
5312
|
-
|
|
5377
|
+
writeFileSync4(logPath, pre + sep + JSON.stringify({
|
|
5313
5378
|
session_id,
|
|
5314
5379
|
scanner_output_excerpt: (tool_response ?? "").slice(0, 200),
|
|
5315
5380
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -5413,8 +5478,8 @@ function readConventions(cwd) {
|
|
|
5413
5478
|
try {
|
|
5414
5479
|
const projectRoot = cwd ?? process.cwd();
|
|
5415
5480
|
const configPath = join7(projectRoot, "massu.config.yaml");
|
|
5416
|
-
if (!
|
|
5417
|
-
const mtimeMs =
|
|
5481
|
+
if (!existsSync14(configPath)) return _conventionDefaults;
|
|
5482
|
+
const mtimeMs = statSync4(configPath).mtimeMs;
|
|
5418
5483
|
if (_cachedConventions !== null && _cachedConventionsPath === configPath && _cachedConventionsMtimeMs === mtimeMs) {
|
|
5419
5484
|
return _cachedConventions;
|
|
5420
5485
|
}
|
|
@@ -5450,7 +5515,7 @@ function isKnowledgeSourceFile(filePath) {
|
|
|
5450
5515
|
function checkMemoryFileIntegrity(filePath) {
|
|
5451
5516
|
const issues = [];
|
|
5452
5517
|
try {
|
|
5453
|
-
if (!
|
|
5518
|
+
if (!existsSync14(filePath)) {
|
|
5454
5519
|
issues.push("MEMORY.md file does not exist after write");
|
|
5455
5520
|
return issues;
|
|
5456
5521
|
}
|
|
@@ -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();
|
|
@@ -4080,13 +4117,41 @@ function logAuditEntry(db, entry) {
|
|
|
4080
4117
|
// src/hooks/pre-compact.ts
|
|
4081
4118
|
init_hook_failure_signal();
|
|
4082
4119
|
|
|
4120
|
+
// src/lib/safe-write.ts
|
|
4121
|
+
import {
|
|
4122
|
+
writeFileSync as writeFileSync3,
|
|
4123
|
+
renameSync,
|
|
4124
|
+
openSync as openSync2,
|
|
4125
|
+
fsyncSync as fsyncSync2,
|
|
4126
|
+
closeSync as closeSync2,
|
|
4127
|
+
existsSync as existsSync9,
|
|
4128
|
+
realpathSync,
|
|
4129
|
+
unlinkSync as unlinkSync2,
|
|
4130
|
+
accessSync as accessSync2,
|
|
4131
|
+
statSync as statSync2,
|
|
4132
|
+
chmodSync as chmodSync3,
|
|
4133
|
+
constants
|
|
4134
|
+
} from "fs";
|
|
4135
|
+
function isSameRealPath(a, b) {
|
|
4136
|
+
try {
|
|
4137
|
+
return realpathSync(a) === realpathSync(b);
|
|
4138
|
+
} catch {
|
|
4139
|
+
return false;
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
|
|
4083
4143
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4084
|
-
import { pathToFileURL } from "url";
|
|
4144
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4085
4145
|
function isDirectInvocation(moduleUrl) {
|
|
4086
4146
|
const entry = process.argv[1];
|
|
4087
4147
|
if (!entry) return false;
|
|
4088
4148
|
try {
|
|
4089
|
-
|
|
4149
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4150
|
+
} catch {
|
|
4151
|
+
return false;
|
|
4152
|
+
}
|
|
4153
|
+
try {
|
|
4154
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4090
4155
|
} catch {
|
|
4091
4156
|
return false;
|
|
4092
4157
|
}
|