@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
|
@@ -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();
|
|
@@ -4156,13 +4193,41 @@ function toolResponseText(raw) {
|
|
|
4156
4193
|
init_hook_failure_signal();
|
|
4157
4194
|
init_timestamps();
|
|
4158
4195
|
|
|
4196
|
+
// src/lib/safe-write.ts
|
|
4197
|
+
import {
|
|
4198
|
+
writeFileSync as writeFileSync3,
|
|
4199
|
+
renameSync,
|
|
4200
|
+
openSync as openSync2,
|
|
4201
|
+
fsyncSync as fsyncSync2,
|
|
4202
|
+
closeSync as closeSync2,
|
|
4203
|
+
existsSync as existsSync9,
|
|
4204
|
+
realpathSync,
|
|
4205
|
+
unlinkSync as unlinkSync2,
|
|
4206
|
+
accessSync as accessSync2,
|
|
4207
|
+
statSync as statSync2,
|
|
4208
|
+
chmodSync as chmodSync3,
|
|
4209
|
+
constants
|
|
4210
|
+
} from "fs";
|
|
4211
|
+
function isSameRealPath(a, b) {
|
|
4212
|
+
try {
|
|
4213
|
+
return realpathSync(a) === realpathSync(b);
|
|
4214
|
+
} catch {
|
|
4215
|
+
return false;
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
|
|
4159
4219
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4160
|
-
import { pathToFileURL } from "url";
|
|
4220
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4161
4221
|
function isDirectInvocation(moduleUrl) {
|
|
4162
4222
|
const entry = process.argv[1];
|
|
4163
4223
|
if (!entry) return false;
|
|
4164
4224
|
try {
|
|
4165
|
-
|
|
4225
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4226
|
+
} catch {
|
|
4227
|
+
return false;
|
|
4228
|
+
}
|
|
4229
|
+
try {
|
|
4230
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4166
4231
|
} catch {
|
|
4167
4232
|
return false;
|
|
4168
4233
|
}
|
|
@@ -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();
|
|
@@ -4056,7 +4093,7 @@ var init_hook_failure_signal = __esm({
|
|
|
4056
4093
|
|
|
4057
4094
|
// src/hooks/rule-enforcement-pipeline.ts
|
|
4058
4095
|
init_config();
|
|
4059
|
-
import { existsSync as
|
|
4096
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, readdirSync as readdirSync2 } from "fs";
|
|
4060
4097
|
import { basename as basename3, resolve as resolve5 } from "path";
|
|
4061
4098
|
|
|
4062
4099
|
// src/hooks/lib/write-hook-message.ts
|
|
@@ -4097,13 +4134,41 @@ function writeHookContext(hookEventName, message) {
|
|
|
4097
4134
|
// src/hooks/rule-enforcement-pipeline.ts
|
|
4098
4135
|
init_hook_failure_signal();
|
|
4099
4136
|
|
|
4137
|
+
// src/lib/safe-write.ts
|
|
4138
|
+
import {
|
|
4139
|
+
writeFileSync as writeFileSync3,
|
|
4140
|
+
renameSync,
|
|
4141
|
+
openSync as openSync2,
|
|
4142
|
+
fsyncSync as fsyncSync2,
|
|
4143
|
+
closeSync as closeSync2,
|
|
4144
|
+
existsSync as existsSync9,
|
|
4145
|
+
realpathSync,
|
|
4146
|
+
unlinkSync as unlinkSync2,
|
|
4147
|
+
accessSync as accessSync2,
|
|
4148
|
+
statSync as statSync2,
|
|
4149
|
+
chmodSync as chmodSync3,
|
|
4150
|
+
constants
|
|
4151
|
+
} from "fs";
|
|
4152
|
+
function isSameRealPath(a, b) {
|
|
4153
|
+
try {
|
|
4154
|
+
return realpathSync(a) === realpathSync(b);
|
|
4155
|
+
} catch {
|
|
4156
|
+
return false;
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4100
4160
|
// src/hooks/lib/is-direct-invocation.ts
|
|
4101
|
-
import { pathToFileURL } from "url";
|
|
4161
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4102
4162
|
function isDirectInvocation(moduleUrl) {
|
|
4103
4163
|
const entry = process.argv[1];
|
|
4104
4164
|
if (!entry) return false;
|
|
4105
4165
|
try {
|
|
4106
|
-
|
|
4166
|
+
if (moduleUrl === pathToFileURL(entry).href) return true;
|
|
4167
|
+
} catch {
|
|
4168
|
+
return false;
|
|
4169
|
+
}
|
|
4170
|
+
try {
|
|
4171
|
+
return isSameRealPath(fileURLToPath2(moduleUrl), entry);
|
|
4107
4172
|
} catch {
|
|
4108
4173
|
return false;
|
|
4109
4174
|
}
|
|
@@ -4139,7 +4204,7 @@ async function main() {
|
|
|
4139
4204
|
process.exit(0);
|
|
4140
4205
|
return;
|
|
4141
4206
|
}
|
|
4142
|
-
if (!
|
|
4207
|
+
if (!existsSync10(filePath)) {
|
|
4143
4208
|
process.exit(0);
|
|
4144
4209
|
return;
|
|
4145
4210
|
}
|
|
@@ -4154,7 +4219,7 @@ async function main() {
|
|
|
4154
4219
|
const ruleDesc = descMatch?.[1]?.trim() ?? "";
|
|
4155
4220
|
const enforcementDirAbs = resolve5(root, enforcementDir);
|
|
4156
4221
|
let hasEnforcement = false;
|
|
4157
|
-
if (
|
|
4222
|
+
if (existsSync10(enforcementDirAbs)) {
|
|
4158
4223
|
const hookFiles = readdirSync2(enforcementDirAbs).filter((f) => f.endsWith(".sh") || f.endsWith(".ts") || f.endsWith(".js"));
|
|
4159
4224
|
for (const hookFile of hookFiles) {
|
|
4160
4225
|
try {
|