@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
|
@@ -2182,6 +2182,8 @@ __export(memory_db_exports, {
|
|
|
2182
2182
|
MEMORY_FILE_TITLE_LIKE: () => MEMORY_FILE_TITLE_LIKE,
|
|
2183
2183
|
MEMORY_FILE_TITLE_PREFIX: () => MEMORY_FILE_TITLE_PREFIX,
|
|
2184
2184
|
MEMORY_SCHEMA_VERSION: () => MEMORY_SCHEMA_VERSION,
|
|
2185
|
+
PENDING_SYNC_MAX_AGE_DAYS: () => PENDING_SYNC_MAX_AGE_DAYS,
|
|
2186
|
+
PENDING_SYNC_MAX_ITEMS: () => PENDING_SYNC_MAX_ITEMS,
|
|
2185
2187
|
TOOL_COST_EVENTS_RETENTION_DAYS: () => TOOL_COST_EVENTS_RETENTION_DAYS,
|
|
2186
2188
|
USAGE_COUNTER_ARMED_KEY: () => USAGE_COUNTER_ARMED_KEY,
|
|
2187
2189
|
addConversationTurn: () => addConversationTurn,
|
|
@@ -2190,6 +2192,7 @@ __export(memory_db_exports, {
|
|
|
2190
2192
|
addSummary: () => addSummary,
|
|
2191
2193
|
addToolCallDetail: () => addToolCallDetail,
|
|
2192
2194
|
addUserPrompt: () => addUserPrompt,
|
|
2195
|
+
allPendingSync: () => allPendingSync,
|
|
2193
2196
|
appendIncidentToFailureClass: () => appendIncidentToFailureClass,
|
|
2194
2197
|
armUsageCounter: () => armUsageCounter,
|
|
2195
2198
|
assignImportance: () => assignImportance,
|
|
@@ -2197,6 +2200,7 @@ __export(memory_db_exports, {
|
|
|
2197
2200
|
createSession: () => createSession,
|
|
2198
2201
|
deduplicateFailedAttempt: () => deduplicateFailedAttempt,
|
|
2199
2202
|
dequeuePendingSync: () => dequeuePendingSync,
|
|
2203
|
+
discardPendingSync: () => discardPendingSync,
|
|
2200
2204
|
embedMissingObservations: () => embedMissingObservations,
|
|
2201
2205
|
endSession: () => endSession,
|
|
2202
2206
|
enqueueRulePromotionEvent: () => enqueueRulePromotionEvent,
|
|
@@ -3179,6 +3183,35 @@ function initMemorySchema(db) {
|
|
|
3179
3183
|
}
|
|
3180
3184
|
function enqueueSyncPayload(db, payload) {
|
|
3181
3185
|
db.prepare("INSERT INTO pending_sync (payload) VALUES (?)").run(payload);
|
|
3186
|
+
const overflow = db.prepare(
|
|
3187
|
+
`SELECT id, retry_count, last_error, payload FROM pending_sync
|
|
3188
|
+
WHERE created_at < datetime('now', ?)
|
|
3189
|
+
OR id NOT IN (SELECT id FROM pending_sync ORDER BY created_at DESC, id DESC LIMIT ?)
|
|
3190
|
+
LIMIT 10000`
|
|
3191
|
+
).all(`-${PENDING_SYNC_MAX_AGE_DAYS} days`, PENDING_SYNC_MAX_ITEMS);
|
|
3192
|
+
if (overflow.length > 0) {
|
|
3193
|
+
discardPendingSync(db, overflow, `over the queue limit (${PENDING_SYNC_MAX_ITEMS} items / ${PENDING_SYNC_MAX_AGE_DAYS} days)`);
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
function discardPendingSync(db, rows, why) {
|
|
3197
|
+
if (rows.length === 0) return 0;
|
|
3198
|
+
const rescued = rescueLearningFromStalePayloads(db, rows);
|
|
3199
|
+
for (let i = 0; i < rows.length; i += 500) {
|
|
3200
|
+
const ids = rows.slice(i, i + 500).map((r) => r.id);
|
|
3201
|
+
db.prepare(`DELETE FROM pending_sync WHERE id IN (${ids.map(() => "?").join(",")})`).run(...ids);
|
|
3202
|
+
}
|
|
3203
|
+
if (rescued > 0) {
|
|
3204
|
+
process.stderr.write(
|
|
3205
|
+
`[massu] RESCUED ${rescued} learned rule(s) from ${rows.length} discarded payload(s) \u2014 they were re-queued for delivery, NOT discarded.
|
|
3206
|
+
`
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
process.stderr.write(`[massu] cloud sync: discarded ${rows.length} queued upload(s): ${why}.
|
|
3210
|
+
`);
|
|
3211
|
+
return rows.length;
|
|
3212
|
+
}
|
|
3213
|
+
function allPendingSync(db) {
|
|
3214
|
+
return db.prepare("SELECT id, retry_count, last_error, payload FROM pending_sync").all();
|
|
3182
3215
|
}
|
|
3183
3216
|
function getMemoryMeta(db, key) {
|
|
3184
3217
|
const row = db.prepare("SELECT value FROM memory_meta WHERE key = ?").get(key);
|
|
@@ -3381,7 +3414,9 @@ function dequeuePendingSync(db, limit = 10) {
|
|
|
3381
3414
|
}
|
|
3382
3415
|
}
|
|
3383
3416
|
return db.prepare(
|
|
3384
|
-
|
|
3417
|
+
// Least-retried first: oldest-first retried the same 10 items until they were discarded
|
|
3418
|
+
// while every newer item waited at retry 0 (2026-11-09: 43,492 never attempted).
|
|
3419
|
+
"SELECT id, payload, retry_count FROM pending_sync ORDER BY retry_count ASC, created_at ASC LIMIT ?"
|
|
3385
3420
|
).all(limit);
|
|
3386
3421
|
}
|
|
3387
3422
|
function removePendingSync(db, id) {
|
|
@@ -4054,7 +4089,7 @@ function scoreFailureClasses(db, matchText, filePath, promptContext, weights) {
|
|
|
4054
4089
|
}
|
|
4055
4090
|
return bestMatch;
|
|
4056
4091
|
}
|
|
4057
|
-
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;
|
|
4092
|
+
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;
|
|
4058
4093
|
var init_memory_db = __esm({
|
|
4059
4094
|
"src/memory-db.ts"() {
|
|
4060
4095
|
"use strict";
|
|
@@ -4069,6 +4104,8 @@ var init_memory_db = __esm({
|
|
|
4069
4104
|
init_memory_embed_sweep();
|
|
4070
4105
|
MEMORY_SCHEMA_VERSION = 2;
|
|
4071
4106
|
TOOL_COST_EVENTS_RETENTION_DAYS = 90;
|
|
4107
|
+
PENDING_SYNC_MAX_ITEMS = 500;
|
|
4108
|
+
PENDING_SYNC_MAX_AGE_DAYS = 30;
|
|
4072
4109
|
MAX_DRAFT_TEXT_LEN = 16384;
|
|
4073
4110
|
FUNNEL_EVENT_OUTBOX_CAP = 2e4;
|
|
4074
4111
|
_temporalColCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -4083,7 +4120,7 @@ var init_memory_db = __esm({
|
|
|
4083
4120
|
init_memory_db();
|
|
4084
4121
|
init_db_driver();
|
|
4085
4122
|
init_config();
|
|
4086
|
-
import { existsSync as
|
|
4123
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, writeFileSync as writeFileSync5, readFileSync as readFileSync7, readdirSync as readdirSync3, openSync as openSync3, fstatSync, readSync, closeSync as closeSync3 } from "fs";
|
|
4087
4124
|
import { join as join7 } from "path";
|
|
4088
4125
|
|
|
4089
4126
|
// src/rule-candidate-detector.ts
|
|
@@ -4395,13 +4432,41 @@ function entitledForTeamSharedPromotion(tier) {
|
|
|
4395
4432
|
init_rule_candidate_store();
|
|
4396
4433
|
init_hook_failure_signal();
|
|
4397
4434
|
|
|
4435
|
+
// src/lib/safe-write.ts
|
|
4436
|
+
import {
|
|
4437
|
+
writeFileSync as writeFileSync4,
|
|
4438
|
+
renameSync,
|
|
4439
|
+
openSync as openSync2,
|
|
4440
|
+
fsyncSync as fsyncSync2,
|
|
4441
|
+
closeSync as closeSync2,
|
|
4442
|
+
existsSync as existsSync10,
|
|
4443
|
+
realpathSync,
|
|
4444
|
+
unlinkSync as unlinkSync2,
|
|
4445
|
+
accessSync as accessSync2,
|
|
4446
|
+
statSync as statSync2,
|
|
4447
|
+
chmodSync as chmodSync3,
|
|
4448
|
+
constants
|
|
4449
|
+
} from "fs";
|
|
4450
|
+
function isSameRealPath(a, b) {
|
|
4451
|
+
try {
|
|
4452
|
+
return realpathSync(a) === realpathSync(b);
|
|
4453
|
+
} catch {
|
|
4454
|
+
return false;
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
|
|
4398
4458
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4399
|
-
import { pathToFileURL } from "url";
|
|
4459
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4400
4460
|
function isDirectInvocation(moduleUrl) {
|
|
4401
4461
|
const entry = process.argv[1];
|
|
4402
4462
|
if (!entry) return false;
|
|
4403
4463
|
try {
|
|
4404
|
-
|
|
4464
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4465
|
+
} catch {
|
|
4466
|
+
return false;
|
|
4467
|
+
}
|
|
4468
|
+
try {
|
|
4469
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4405
4470
|
} catch {
|
|
4406
4471
|
return false;
|
|
4407
4472
|
}
|
|
@@ -4439,7 +4504,7 @@ async function main() {
|
|
|
4439
4504
|
const fileRefs = extractFileReferences(prompt);
|
|
4440
4505
|
if (fileRefs.length > 0) {
|
|
4441
4506
|
const knowledgeDbPath = getResolvedPaths().knowledgeDbPath;
|
|
4442
|
-
if (knowledgeDbPath &&
|
|
4507
|
+
if (knowledgeDbPath && existsSync11(knowledgeDbPath)) {
|
|
4443
4508
|
const kdb = openDatabase2(knowledgeDbPath, { readonly: true, selfHeal: false });
|
|
4444
4509
|
try {
|
|
4445
4510
|
const placeholders = fileRefs.map(() => "?").join(",");
|
|
@@ -4499,7 +4564,7 @@ async function main() {
|
|
|
4499
4564
|
mkdirSync8(candidateDir, { recursive: true });
|
|
4500
4565
|
const promptHash = hashPrompt(prompt);
|
|
4501
4566
|
const candidatePath = join7(candidateDir, `${promptHash}.json`);
|
|
4502
|
-
if (!
|
|
4567
|
+
if (!existsSync11(candidatePath)) {
|
|
4503
4568
|
upsertCandidate(db, hookInput.cwd, {
|
|
4504
4569
|
prompt,
|
|
4505
4570
|
prompt_hash: promptHash,
|
|
@@ -4524,7 +4589,7 @@ async function main() {
|
|
|
4524
4589
|
}
|
|
4525
4590
|
} else {
|
|
4526
4591
|
const nudgePath = join7(candidateDir, ".last-tier-nudge");
|
|
4527
|
-
if (!
|
|
4592
|
+
if (!existsSync11(nudgePath)) {
|
|
4528
4593
|
mkdirSync8(candidateDir, { recursive: true });
|
|
4529
4594
|
process.stderr.write(
|
|
4530
4595
|
`
|
|
@@ -4532,18 +4597,18 @@ async function main() {
|
|
|
4532
4597
|
|
|
4533
4598
|
`
|
|
4534
4599
|
);
|
|
4535
|
-
|
|
4600
|
+
writeFileSync5(nudgePath, (/* @__PURE__ */ new Date()).toISOString());
|
|
4536
4601
|
}
|
|
4537
4602
|
}
|
|
4538
4603
|
}
|
|
4539
|
-
if (
|
|
4604
|
+
if (existsSync11(candidateDir)) {
|
|
4540
4605
|
const candidates = readdirSync3(candidateDir).filter(
|
|
4541
4606
|
(f) => f.endsWith(".json") && !f.startsWith(".")
|
|
4542
4607
|
);
|
|
4543
4608
|
const candidateCount = candidates.length;
|
|
4544
4609
|
const surfacedPath = join7(candidateDir, ".last-surfaced");
|
|
4545
4610
|
let lastSurfaced = 0;
|
|
4546
|
-
if (
|
|
4611
|
+
if (existsSync11(surfacedPath)) {
|
|
4547
4612
|
const raw = readFileSync7(surfacedPath, "utf-8").trim();
|
|
4548
4613
|
const parsed = parseInt(raw, 10);
|
|
4549
4614
|
if (!Number.isNaN(parsed)) lastSurfaced = parsed;
|
|
@@ -4555,17 +4620,17 @@ async function main() {
|
|
|
4555
4620
|
|
|
4556
4621
|
`
|
|
4557
4622
|
);
|
|
4558
|
-
|
|
4623
|
+
writeFileSync5(surfacedPath, String(candidateCount));
|
|
4559
4624
|
}
|
|
4560
4625
|
}
|
|
4561
4626
|
} catch (candidateErr) {
|
|
4562
4627
|
try {
|
|
4563
4628
|
const dir = join7(hookInput.cwd, ".massu", "rule-candidates");
|
|
4564
|
-
if (!
|
|
4629
|
+
if (!existsSync11(dir)) mkdirSync8(dir, { recursive: true });
|
|
4565
4630
|
const logPath = join7(dir, ".detector-failures.jsonl");
|
|
4566
|
-
const pre =
|
|
4631
|
+
const pre = existsSync11(logPath) ? readFileSync7(logPath, "utf-8") : "";
|
|
4567
4632
|
const sep = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
4568
|
-
|
|
4633
|
+
writeFileSync5(logPath, pre + sep + JSON.stringify({
|
|
4569
4634
|
session_id,
|
|
4570
4635
|
// Security review (plan-2026-05-27): log the prompt HASH, never a
|
|
4571
4636
|
// raw prompt excerpt — the failure log must not persist PII/secrets.
|
|
@@ -4586,13 +4651,13 @@ async function main() {
|
|
|
4586
4651
|
}
|
|
4587
4652
|
function detectPriorAssistantTurn(transcriptPath) {
|
|
4588
4653
|
try {
|
|
4589
|
-
if (!transcriptPath || !
|
|
4654
|
+
if (!transcriptPath || !existsSync11(transcriptPath)) {
|
|
4590
4655
|
return { hadEditOrWrite: false, files: [] };
|
|
4591
4656
|
}
|
|
4592
4657
|
if (!transcriptPath.includes("/.claude/projects/")) {
|
|
4593
4658
|
return { hadEditOrWrite: false, files: [] };
|
|
4594
4659
|
}
|
|
4595
|
-
const fd =
|
|
4660
|
+
const fd = openSync3(transcriptPath, "r");
|
|
4596
4661
|
let buf;
|
|
4597
4662
|
try {
|
|
4598
4663
|
const stats = fstatSync(fd);
|
|
@@ -4601,7 +4666,7 @@ function detectPriorAssistantTurn(transcriptPath) {
|
|
|
4601
4666
|
buf = Buffer.alloc(readLen);
|
|
4602
4667
|
readSync(fd, buf, 0, readLen, offset);
|
|
4603
4668
|
} finally {
|
|
4604
|
-
|
|
4669
|
+
closeSync3(fd);
|
|
4605
4670
|
}
|
|
4606
4671
|
const lines = buf.toString("utf-8").split("\n").filter(Boolean);
|
|
4607
4672
|
const files = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massu/core",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, tiered tooling (12 free / 73 total), 56 workflow commands, 11 agents, 20+ patterns",
|
|
6
6
|
"main": "./dist/cli.js",
|