@massu/core 2.4.0 → 2.5.1
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 +126 -0
- package/commands/massu-bearings.md +1 -1
- package/commands/massu-recap.md +16 -5
- package/dist/cli.js +291 -80
- package/dist/hooks/auto-learning-pipeline.js +53 -9
- package/dist/hooks/classify-failure.js +55 -11
- package/dist/hooks/cost-tracker.js +23 -9
- package/dist/hooks/fix-detector.js +54 -9
- package/dist/hooks/incident-pipeline.js +53 -9
- package/dist/hooks/intent-suggester.js +53 -9
- package/dist/hooks/memory-recall.js +53 -9
- package/dist/hooks/post-edit-context.js +53 -9
- package/dist/hooks/post-tool-use.js +19 -6
- package/dist/hooks/pre-compact.js +19 -6
- package/dist/hooks/pre-delete-check.js +53 -9
- package/dist/hooks/pre-tool-use-gate.js +57 -11
- package/dist/hooks/quality-event.js +23 -9
- package/dist/hooks/rule-enforcement-pipeline.js +53 -9
- package/dist/hooks/security-gate.js +53 -9
- package/dist/hooks/session-end.js +19 -6
- package/dist/hooks/session-start.js +120 -37
- package/dist/hooks/user-prompt.js +19 -6
- package/package.json +1 -1
|
@@ -1339,6 +1339,16 @@ var init_memory_vector = __esm({
|
|
|
1339
1339
|
}
|
|
1340
1340
|
});
|
|
1341
1341
|
|
|
1342
|
+
// src/lib/timestamps.ts
|
|
1343
|
+
function nowIso() {
|
|
1344
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1345
|
+
}
|
|
1346
|
+
var init_timestamps = __esm({
|
|
1347
|
+
"src/lib/timestamps.ts"() {
|
|
1348
|
+
"use strict";
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1342
1352
|
// src/db-backup.ts
|
|
1343
1353
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1344
1354
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3599,8 +3609,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3599
3609
|
}
|
|
3600
3610
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3601
3611
|
const result = db.prepare(`
|
|
3602
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3603
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3612
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3613
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3604
3614
|
`).run(
|
|
3605
3615
|
sessionId,
|
|
3606
3616
|
turnNumber,
|
|
@@ -3609,14 +3619,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3609
3619
|
toolCallsJson,
|
|
3610
3620
|
toolCallCount,
|
|
3611
3621
|
promptTokens,
|
|
3612
|
-
responseTokens
|
|
3622
|
+
responseTokens,
|
|
3623
|
+
nowIso()
|
|
3613
3624
|
);
|
|
3614
3625
|
return Number(result.lastInsertRowid);
|
|
3615
3626
|
}
|
|
3616
3627
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3617
3628
|
db.prepare(`
|
|
3618
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3619
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3629
|
+
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved, created_at)
|
|
3630
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3620
3631
|
`).run(
|
|
3621
3632
|
sessionId,
|
|
3622
3633
|
turnNumber,
|
|
@@ -3625,7 +3636,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3625
3636
|
inputSize,
|
|
3626
3637
|
outputSize,
|
|
3627
3638
|
success ? 1 : 0,
|
|
3628
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3639
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3640
|
+
nowIso()
|
|
3629
3641
|
);
|
|
3630
3642
|
}
|
|
3631
3643
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3884,6 +3896,7 @@ var init_memory_db = __esm({
|
|
|
3884
3896
|
init_db_driver();
|
|
3885
3897
|
init_config();
|
|
3886
3898
|
init_memory_vector();
|
|
3899
|
+
init_timestamps();
|
|
3887
3900
|
init_db_backup();
|
|
3888
3901
|
init_rule_delivery();
|
|
3889
3902
|
init_rule_candidate_store();
|
|
@@ -3973,12 +3986,43 @@ var init_hook_failure_signal = __esm({
|
|
|
3973
3986
|
});
|
|
3974
3987
|
|
|
3975
3988
|
// src/hooks/lib/write-hook-message.ts
|
|
3976
|
-
|
|
3977
|
-
|
|
3989
|
+
var HOOK_EVENTS = [
|
|
3990
|
+
"UserPromptSubmit",
|
|
3991
|
+
"UserPromptExpansion",
|
|
3992
|
+
"SessionStart",
|
|
3993
|
+
"PreToolUse",
|
|
3994
|
+
"PostToolUse",
|
|
3995
|
+
"Stop",
|
|
3996
|
+
"SubagentStop",
|
|
3997
|
+
"PreCompact"
|
|
3998
|
+
];
|
|
3999
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4000
|
+
function isHookEvent(value) {
|
|
4001
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4002
|
+
}
|
|
4003
|
+
function writeHookContext(hookEventName, message) {
|
|
4004
|
+
if (!isHookEvent(hookEventName)) {
|
|
4005
|
+
process.stderr.write(
|
|
4006
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4007
|
+
`
|
|
4008
|
+
);
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
4011
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4012
|
+
let additionalContext = message;
|
|
4013
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4014
|
+
const marker = `
|
|
4015
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4016
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4017
|
+
}
|
|
4018
|
+
process.stdout.write(
|
|
4019
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4020
|
+
);
|
|
3978
4021
|
}
|
|
3979
4022
|
|
|
3980
4023
|
// src/hooks/security-gate.ts
|
|
3981
4024
|
init_hook_failure_signal();
|
|
4025
|
+
var HOOK_EVENT = "PreToolUse";
|
|
3982
4026
|
var DANGEROUS_BASH_PATTERNS = [
|
|
3983
4027
|
// BLOCK — destructive and irreversible, or arbitrary remote code execution.
|
|
3984
4028
|
{ pattern: /rm\s+-[a-z]*r[a-z]*f[a-z]*\s+\/(?:\s|$)/, label: "rm -rf /", severity: "block" },
|
|
@@ -4098,7 +4142,7 @@ async function main() {
|
|
|
4098
4142
|
const input = await readStdin();
|
|
4099
4143
|
const hookInput = JSON.parse(input);
|
|
4100
4144
|
const messages = runSecurityGateChecks(hookInput);
|
|
4101
|
-
for (const msg of messages)
|
|
4145
|
+
for (const msg of messages) writeHookContext(HOOK_EVENT, msg);
|
|
4102
4146
|
} catch (err) {
|
|
4103
4147
|
recordHookFailure("security-gate", err);
|
|
4104
4148
|
process.stderr.write("MASSU SECURITY GATE \u2014 BLOCKED (gate could not evaluate this call)\n");
|
|
@@ -4220,6 +4264,7 @@ function getFeatureImpact(db, filePaths) {
|
|
|
4220
4264
|
// src/hooks/pre-delete-check.ts
|
|
4221
4265
|
init_config();
|
|
4222
4266
|
init_hook_failure_signal();
|
|
4267
|
+
var HOOK_EVENT2 = "PreToolUse";
|
|
4223
4268
|
var PROJECT_ROOT2 = getProjectRoot();
|
|
4224
4269
|
var KNOWLEDGE_PROTECTED_FILES = [
|
|
4225
4270
|
"knowledge-db.ts",
|
|
@@ -4363,7 +4408,7 @@ async function main2() {
|
|
|
4363
4408
|
const input = await readStdin2();
|
|
4364
4409
|
const hookInput = JSON.parse(input);
|
|
4365
4410
|
const messages = runPreDeleteChecks(hookInput);
|
|
4366
|
-
for (const msg of messages)
|
|
4411
|
+
for (const msg of messages) writeHookContext(HOOK_EVENT2, msg);
|
|
4367
4412
|
} catch (err) {
|
|
4368
4413
|
recordHookFailure("pre-delete-check", err);
|
|
4369
4414
|
process.stderr.write("MASSU SECURITY GATE \u2014 BLOCKED (gate could not evaluate this call)\n");
|
|
@@ -4423,6 +4468,7 @@ function readStdinToEof(deadlineMs = 4e3) {
|
|
|
4423
4468
|
|
|
4424
4469
|
// src/hooks/pre-tool-use-gate.ts
|
|
4425
4470
|
init_hook_failure_signal();
|
|
4471
|
+
var HOOK_EVENT3 = "PreToolUse";
|
|
4426
4472
|
var EXIT_DENY = 2;
|
|
4427
4473
|
var EXIT_ALLOW = 0;
|
|
4428
4474
|
function deny(reason, detail) {
|
|
@@ -4472,7 +4518,7 @@ async function main3() {
|
|
|
4472
4518
|
}
|
|
4473
4519
|
const blocking = findings.filter((f) => f.severity === "block");
|
|
4474
4520
|
for (const f of findings.filter((x) => x.severity === "warn")) {
|
|
4475
|
-
|
|
4521
|
+
writeHookContext(HOOK_EVENT3, f.message);
|
|
4476
4522
|
}
|
|
4477
4523
|
if (blocking.length > 0) {
|
|
4478
4524
|
deny(blocking.map((f) => f.message).join("\n\n"));
|
|
@@ -1339,6 +1339,16 @@ var init_memory_vector = __esm({
|
|
|
1339
1339
|
}
|
|
1340
1340
|
});
|
|
1341
1341
|
|
|
1342
|
+
// src/lib/timestamps.ts
|
|
1343
|
+
function nowIso() {
|
|
1344
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1345
|
+
}
|
|
1346
|
+
var init_timestamps = __esm({
|
|
1347
|
+
"src/lib/timestamps.ts"() {
|
|
1348
|
+
"use strict";
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1342
1352
|
// src/db-backup.ts
|
|
1343
1353
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1344
1354
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3670,8 +3680,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3670
3680
|
}
|
|
3671
3681
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3672
3682
|
const result = db.prepare(`
|
|
3673
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3674
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3683
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3684
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3675
3685
|
`).run(
|
|
3676
3686
|
sessionId,
|
|
3677
3687
|
turnNumber,
|
|
@@ -3680,14 +3690,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3680
3690
|
toolCallsJson,
|
|
3681
3691
|
toolCallCount,
|
|
3682
3692
|
promptTokens,
|
|
3683
|
-
responseTokens
|
|
3693
|
+
responseTokens,
|
|
3694
|
+
nowIso()
|
|
3684
3695
|
);
|
|
3685
3696
|
return Number(result.lastInsertRowid);
|
|
3686
3697
|
}
|
|
3687
3698
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3688
3699
|
db.prepare(`
|
|
3689
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3690
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3700
|
+
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved, created_at)
|
|
3701
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3691
3702
|
`).run(
|
|
3692
3703
|
sessionId,
|
|
3693
3704
|
turnNumber,
|
|
@@ -3696,7 +3707,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3696
3707
|
inputSize,
|
|
3697
3708
|
outputSize,
|
|
3698
3709
|
success ? 1 : 0,
|
|
3699
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3710
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3711
|
+
nowIso()
|
|
3700
3712
|
);
|
|
3701
3713
|
}
|
|
3702
3714
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3955,6 +3967,7 @@ var init_memory_db = __esm({
|
|
|
3955
3967
|
init_db_driver();
|
|
3956
3968
|
init_config();
|
|
3957
3969
|
init_memory_vector();
|
|
3970
|
+
init_timestamps();
|
|
3958
3971
|
init_db_backup();
|
|
3959
3972
|
init_rule_delivery();
|
|
3960
3973
|
init_rule_candidate_store();
|
|
@@ -4072,6 +4085,7 @@ function toolResponseText(raw) {
|
|
|
4072
4085
|
|
|
4073
4086
|
// src/hooks/quality-event.ts
|
|
4074
4087
|
init_hook_failure_signal();
|
|
4088
|
+
init_timestamps();
|
|
4075
4089
|
var TEST_FAILURE_PATTERNS = [
|
|
4076
4090
|
/\bFAIL\b/,
|
|
4077
4091
|
/✗/,
|
|
@@ -4135,11 +4149,11 @@ async function main() {
|
|
|
4135
4149
|
const db = getMemoryDb();
|
|
4136
4150
|
try {
|
|
4137
4151
|
const stmt = db.prepare(`
|
|
4138
|
-
INSERT INTO quality_events (session_id, event_type, tool_name, details)
|
|
4139
|
-
VALUES (?, ?, ?, ?)
|
|
4152
|
+
INSERT INTO quality_events (session_id, event_type, tool_name, details, created_at)
|
|
4153
|
+
VALUES (?, ?, ?, ?, ?)
|
|
4140
4154
|
`);
|
|
4141
4155
|
for (const signal of signals) {
|
|
4142
|
-
stmt.run(session_id, signal.event_type, tool_name, signal.details);
|
|
4156
|
+
stmt.run(session_id, signal.event_type, tool_name, signal.details, nowIso());
|
|
4143
4157
|
}
|
|
4144
4158
|
} finally {
|
|
4145
4159
|
db.close();
|
|
@@ -1339,6 +1339,16 @@ var init_memory_vector = __esm({
|
|
|
1339
1339
|
}
|
|
1340
1340
|
});
|
|
1341
1341
|
|
|
1342
|
+
// src/lib/timestamps.ts
|
|
1343
|
+
function nowIso() {
|
|
1344
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1345
|
+
}
|
|
1346
|
+
var init_timestamps = __esm({
|
|
1347
|
+
"src/lib/timestamps.ts"() {
|
|
1348
|
+
"use strict";
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1342
1352
|
// src/db-backup.ts
|
|
1343
1353
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1344
1354
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3599,8 +3609,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3599
3609
|
}
|
|
3600
3610
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3601
3611
|
const result = db.prepare(`
|
|
3602
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3603
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3612
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3613
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3604
3614
|
`).run(
|
|
3605
3615
|
sessionId,
|
|
3606
3616
|
turnNumber,
|
|
@@ -3609,14 +3619,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3609
3619
|
toolCallsJson,
|
|
3610
3620
|
toolCallCount,
|
|
3611
3621
|
promptTokens,
|
|
3612
|
-
responseTokens
|
|
3622
|
+
responseTokens,
|
|
3623
|
+
nowIso()
|
|
3613
3624
|
);
|
|
3614
3625
|
return Number(result.lastInsertRowid);
|
|
3615
3626
|
}
|
|
3616
3627
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3617
3628
|
db.prepare(`
|
|
3618
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3619
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3629
|
+
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved, created_at)
|
|
3630
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3620
3631
|
`).run(
|
|
3621
3632
|
sessionId,
|
|
3622
3633
|
turnNumber,
|
|
@@ -3625,7 +3636,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3625
3636
|
inputSize,
|
|
3626
3637
|
outputSize,
|
|
3627
3638
|
success ? 1 : 0,
|
|
3628
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3639
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3640
|
+
nowIso()
|
|
3629
3641
|
);
|
|
3630
3642
|
}
|
|
3631
3643
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3884,6 +3896,7 @@ var init_memory_db = __esm({
|
|
|
3884
3896
|
init_db_driver();
|
|
3885
3897
|
init_config();
|
|
3886
3898
|
init_memory_vector();
|
|
3899
|
+
init_timestamps();
|
|
3887
3900
|
init_db_backup();
|
|
3888
3901
|
init_rule_delivery();
|
|
3889
3902
|
init_rule_candidate_store();
|
|
@@ -3978,12 +3991,43 @@ import { existsSync as existsSync9, readFileSync as readFileSync6, readdirSync a
|
|
|
3978
3991
|
import { basename as basename3, resolve as resolve5 } from "path";
|
|
3979
3992
|
|
|
3980
3993
|
// src/hooks/lib/write-hook-message.ts
|
|
3981
|
-
|
|
3982
|
-
|
|
3994
|
+
var HOOK_EVENTS = [
|
|
3995
|
+
"UserPromptSubmit",
|
|
3996
|
+
"UserPromptExpansion",
|
|
3997
|
+
"SessionStart",
|
|
3998
|
+
"PreToolUse",
|
|
3999
|
+
"PostToolUse",
|
|
4000
|
+
"Stop",
|
|
4001
|
+
"SubagentStop",
|
|
4002
|
+
"PreCompact"
|
|
4003
|
+
];
|
|
4004
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4005
|
+
function isHookEvent(value) {
|
|
4006
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4007
|
+
}
|
|
4008
|
+
function writeHookContext(hookEventName, message) {
|
|
4009
|
+
if (!isHookEvent(hookEventName)) {
|
|
4010
|
+
process.stderr.write(
|
|
4011
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4012
|
+
`
|
|
4013
|
+
);
|
|
4014
|
+
return;
|
|
4015
|
+
}
|
|
4016
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4017
|
+
let additionalContext = message;
|
|
4018
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4019
|
+
const marker = `
|
|
4020
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4021
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4022
|
+
}
|
|
4023
|
+
process.stdout.write(
|
|
4024
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4025
|
+
);
|
|
3983
4026
|
}
|
|
3984
4027
|
|
|
3985
4028
|
// src/hooks/rule-enforcement-pipeline.ts
|
|
3986
4029
|
init_hook_failure_signal();
|
|
4030
|
+
var HOOK_EVENT = "PostToolUse";
|
|
3987
4031
|
async function main() {
|
|
3988
4032
|
try {
|
|
3989
4033
|
const input = await readStdin();
|
|
@@ -4081,7 +4125,7 @@ async function main() {
|
|
|
4081
4125
|
lines.push(" This step is MANDATORY per the auto-learning pipeline.");
|
|
4082
4126
|
lines.push("============================================================================");
|
|
4083
4127
|
lines.push("");
|
|
4084
|
-
|
|
4128
|
+
writeHookContext(HOOK_EVENT, lines.join("\n"));
|
|
4085
4129
|
} catch (err) {
|
|
4086
4130
|
recordHookFailure("rule-enforcement-pipeline", err);
|
|
4087
4131
|
}
|
|
@@ -1339,6 +1339,16 @@ var init_memory_vector = __esm({
|
|
|
1339
1339
|
}
|
|
1340
1340
|
});
|
|
1341
1341
|
|
|
1342
|
+
// src/lib/timestamps.ts
|
|
1343
|
+
function nowIso() {
|
|
1344
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1345
|
+
}
|
|
1346
|
+
var init_timestamps = __esm({
|
|
1347
|
+
"src/lib/timestamps.ts"() {
|
|
1348
|
+
"use strict";
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1342
1352
|
// src/db-backup.ts
|
|
1343
1353
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1344
1354
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3599,8 +3609,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3599
3609
|
}
|
|
3600
3610
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3601
3611
|
const result = db.prepare(`
|
|
3602
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3603
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3612
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3613
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3604
3614
|
`).run(
|
|
3605
3615
|
sessionId,
|
|
3606
3616
|
turnNumber,
|
|
@@ -3609,14 +3619,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3609
3619
|
toolCallsJson,
|
|
3610
3620
|
toolCallCount,
|
|
3611
3621
|
promptTokens,
|
|
3612
|
-
responseTokens
|
|
3622
|
+
responseTokens,
|
|
3623
|
+
nowIso()
|
|
3613
3624
|
);
|
|
3614
3625
|
return Number(result.lastInsertRowid);
|
|
3615
3626
|
}
|
|
3616
3627
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3617
3628
|
db.prepare(`
|
|
3618
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3619
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3629
|
+
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved, created_at)
|
|
3630
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3620
3631
|
`).run(
|
|
3621
3632
|
sessionId,
|
|
3622
3633
|
turnNumber,
|
|
@@ -3625,7 +3636,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3625
3636
|
inputSize,
|
|
3626
3637
|
outputSize,
|
|
3627
3638
|
success ? 1 : 0,
|
|
3628
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3639
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3640
|
+
nowIso()
|
|
3629
3641
|
);
|
|
3630
3642
|
}
|
|
3631
3643
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3884,6 +3896,7 @@ var init_memory_db = __esm({
|
|
|
3884
3896
|
init_db_driver();
|
|
3885
3897
|
init_config();
|
|
3886
3898
|
init_memory_vector();
|
|
3899
|
+
init_timestamps();
|
|
3887
3900
|
init_db_backup();
|
|
3888
3901
|
init_rule_delivery();
|
|
3889
3902
|
init_rule_candidate_store();
|
|
@@ -3973,12 +3986,43 @@ var init_hook_failure_signal = __esm({
|
|
|
3973
3986
|
});
|
|
3974
3987
|
|
|
3975
3988
|
// src/hooks/lib/write-hook-message.ts
|
|
3976
|
-
|
|
3977
|
-
|
|
3989
|
+
var HOOK_EVENTS = [
|
|
3990
|
+
"UserPromptSubmit",
|
|
3991
|
+
"UserPromptExpansion",
|
|
3992
|
+
"SessionStart",
|
|
3993
|
+
"PreToolUse",
|
|
3994
|
+
"PostToolUse",
|
|
3995
|
+
"Stop",
|
|
3996
|
+
"SubagentStop",
|
|
3997
|
+
"PreCompact"
|
|
3998
|
+
];
|
|
3999
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4000
|
+
function isHookEvent(value) {
|
|
4001
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4002
|
+
}
|
|
4003
|
+
function writeHookContext(hookEventName, message) {
|
|
4004
|
+
if (!isHookEvent(hookEventName)) {
|
|
4005
|
+
process.stderr.write(
|
|
4006
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4007
|
+
`
|
|
4008
|
+
);
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
4011
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4012
|
+
let additionalContext = message;
|
|
4013
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4014
|
+
const marker = `
|
|
4015
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4016
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4017
|
+
}
|
|
4018
|
+
process.stdout.write(
|
|
4019
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4020
|
+
);
|
|
3978
4021
|
}
|
|
3979
4022
|
|
|
3980
4023
|
// src/hooks/security-gate.ts
|
|
3981
4024
|
init_hook_failure_signal();
|
|
4025
|
+
var HOOK_EVENT = "PreToolUse";
|
|
3982
4026
|
var DANGEROUS_BASH_PATTERNS = [
|
|
3983
4027
|
// BLOCK — destructive and irreversible, or arbitrary remote code execution.
|
|
3984
4028
|
{ pattern: /rm\s+-[a-z]*r[a-z]*f[a-z]*\s+\/(?:\s|$)/, label: "rm -rf /", severity: "block" },
|
|
@@ -4098,7 +4142,7 @@ async function main() {
|
|
|
4098
4142
|
const input = await readStdin();
|
|
4099
4143
|
const hookInput = JSON.parse(input);
|
|
4100
4144
|
const messages = runSecurityGateChecks(hookInput);
|
|
4101
|
-
for (const msg of messages)
|
|
4145
|
+
for (const msg of messages) writeHookContext(HOOK_EVENT, msg);
|
|
4102
4146
|
} catch (err) {
|
|
4103
4147
|
recordHookFailure("security-gate", err);
|
|
4104
4148
|
process.stderr.write("MASSU SECURITY GATE \u2014 BLOCKED (gate could not evaluate this call)\n");
|
|
@@ -1360,6 +1360,16 @@ var init_memory_vector = __esm({
|
|
|
1360
1360
|
}
|
|
1361
1361
|
});
|
|
1362
1362
|
|
|
1363
|
+
// src/lib/timestamps.ts
|
|
1364
|
+
function nowIso() {
|
|
1365
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1366
|
+
}
|
|
1367
|
+
var init_timestamps = __esm({
|
|
1368
|
+
"src/lib/timestamps.ts"() {
|
|
1369
|
+
"use strict";
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1363
1373
|
// src/db-backup.ts
|
|
1364
1374
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1365
1375
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3853,8 +3863,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3853
3863
|
}
|
|
3854
3864
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3855
3865
|
const result = db.prepare(`
|
|
3856
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3857
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3866
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3867
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3858
3868
|
`).run(
|
|
3859
3869
|
sessionId,
|
|
3860
3870
|
turnNumber,
|
|
@@ -3863,14 +3873,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3863
3873
|
toolCallsJson,
|
|
3864
3874
|
toolCallCount,
|
|
3865
3875
|
promptTokens,
|
|
3866
|
-
responseTokens
|
|
3876
|
+
responseTokens,
|
|
3877
|
+
nowIso()
|
|
3867
3878
|
);
|
|
3868
3879
|
return Number(result.lastInsertRowid);
|
|
3869
3880
|
}
|
|
3870
3881
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3871
3882
|
db.prepare(`
|
|
3872
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3873
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3883
|
+
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved, created_at)
|
|
3884
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3874
3885
|
`).run(
|
|
3875
3886
|
sessionId,
|
|
3876
3887
|
turnNumber,
|
|
@@ -3879,7 +3890,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3879
3890
|
inputSize,
|
|
3880
3891
|
outputSize,
|
|
3881
3892
|
success ? 1 : 0,
|
|
3882
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3893
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3894
|
+
nowIso()
|
|
3883
3895
|
);
|
|
3884
3896
|
}
|
|
3885
3897
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -4138,6 +4150,7 @@ var init_memory_db = __esm({
|
|
|
4138
4150
|
init_db_driver();
|
|
4139
4151
|
init_config();
|
|
4140
4152
|
init_memory_vector();
|
|
4153
|
+
init_timestamps();
|
|
4141
4154
|
init_db_backup();
|
|
4142
4155
|
init_rule_delivery();
|
|
4143
4156
|
init_rule_candidate_store();
|