@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();
|
|
@@ -3980,12 +3993,43 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
3980
3993
|
import { join as join6 } from "path";
|
|
3981
3994
|
|
|
3982
3995
|
// src/hooks/lib/write-hook-message.ts
|
|
3983
|
-
|
|
3984
|
-
|
|
3996
|
+
var HOOK_EVENTS = [
|
|
3997
|
+
"UserPromptSubmit",
|
|
3998
|
+
"UserPromptExpansion",
|
|
3999
|
+
"SessionStart",
|
|
4000
|
+
"PreToolUse",
|
|
4001
|
+
"PostToolUse",
|
|
4002
|
+
"Stop",
|
|
4003
|
+
"SubagentStop",
|
|
4004
|
+
"PreCompact"
|
|
4005
|
+
];
|
|
4006
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4007
|
+
function isHookEvent(value) {
|
|
4008
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4009
|
+
}
|
|
4010
|
+
function writeHookContext(hookEventName, message) {
|
|
4011
|
+
if (!isHookEvent(hookEventName)) {
|
|
4012
|
+
process.stderr.write(
|
|
4013
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4014
|
+
`
|
|
4015
|
+
);
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
4018
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4019
|
+
let additionalContext = message;
|
|
4020
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4021
|
+
const marker = `
|
|
4022
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4023
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4024
|
+
}
|
|
4025
|
+
process.stdout.write(
|
|
4026
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4027
|
+
);
|
|
3985
4028
|
}
|
|
3986
4029
|
|
|
3987
4030
|
// src/hooks/auto-learning-pipeline.ts
|
|
3988
4031
|
init_hook_failure_signal();
|
|
4032
|
+
var HOOK_EVENT = "Stop";
|
|
3989
4033
|
var MAX_FULL_DIFF_BYTES = 2 * 1024 * 1024;
|
|
3990
4034
|
function getSessionFlagPath(sessionId) {
|
|
3991
4035
|
return join6(tmpdir2(), "massu-auto-learning", `fixes-${sessionId.slice(0, 12)}.jsonl`);
|
|
@@ -4104,7 +4148,7 @@ async function main() {
|
|
|
4104
4148
|
lines.push("");
|
|
4105
4149
|
lines.push("============================================================================");
|
|
4106
4150
|
lines.push("");
|
|
4107
|
-
|
|
4151
|
+
writeHookContext(HOOK_EVENT, lines.join("\n"));
|
|
4108
4152
|
cleanup(flagPath);
|
|
4109
4153
|
} catch (err) {
|
|
4110
4154
|
recordHookFailure("auto-learning-pipeline", err);
|
|
@@ -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();
|
|
@@ -3980,12 +3993,43 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
3980
3993
|
import { join as join6, basename as basename3 } from "path";
|
|
3981
3994
|
|
|
3982
3995
|
// src/hooks/lib/write-hook-message.ts
|
|
3983
|
-
|
|
3984
|
-
|
|
3996
|
+
var HOOK_EVENTS = [
|
|
3997
|
+
"UserPromptSubmit",
|
|
3998
|
+
"UserPromptExpansion",
|
|
3999
|
+
"SessionStart",
|
|
4000
|
+
"PreToolUse",
|
|
4001
|
+
"PostToolUse",
|
|
4002
|
+
"Stop",
|
|
4003
|
+
"SubagentStop",
|
|
4004
|
+
"PreCompact"
|
|
4005
|
+
];
|
|
4006
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4007
|
+
function isHookEvent(value) {
|
|
4008
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4009
|
+
}
|
|
4010
|
+
function writeHookContext(hookEventName, message) {
|
|
4011
|
+
if (!isHookEvent(hookEventName)) {
|
|
4012
|
+
process.stderr.write(
|
|
4013
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4014
|
+
`
|
|
4015
|
+
);
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
4018
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4019
|
+
let additionalContext = message;
|
|
4020
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4021
|
+
const marker = `
|
|
4022
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4023
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4024
|
+
}
|
|
4025
|
+
process.stdout.write(
|
|
4026
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4027
|
+
);
|
|
3985
4028
|
}
|
|
3986
4029
|
|
|
3987
4030
|
// src/hooks/classify-failure.ts
|
|
3988
4031
|
init_hook_failure_signal();
|
|
4032
|
+
var HOOK_EVENT = "PostToolUse";
|
|
3989
4033
|
var BUG_FIX_INDICATORS = [
|
|
3990
4034
|
/\b(catch|error|throw|fail|fix|bug|broken|missing|crash|wrong|typo|incorrect)\b/i
|
|
3991
4035
|
];
|
|
@@ -4127,7 +4171,7 @@ function outputKnownPattern(match) {
|
|
|
4127
4171
|
lines.push(` Covered by: ${match.rules.join(", ")}`);
|
|
4128
4172
|
}
|
|
4129
4173
|
lines.push(" No new rules needed. Reference existing incident if logging.");
|
|
4130
|
-
|
|
4174
|
+
writeHookContext(HOOK_EVENT, lines.join("\n"));
|
|
4131
4175
|
}
|
|
4132
4176
|
function outputSimilarPattern(match) {
|
|
4133
4177
|
const lines = [];
|
|
@@ -4137,7 +4181,7 @@ function outputSimilarPattern(match) {
|
|
|
4137
4181
|
lines.push(` Check if existing rules cover this case: ${match.rules.join(", ")}`);
|
|
4138
4182
|
}
|
|
4139
4183
|
lines.push(" If genuinely new: create incident + prevention rule + enforcement.");
|
|
4140
|
-
|
|
4184
|
+
writeHookContext(HOOK_EVENT, lines.join("\n"));
|
|
4141
4185
|
}
|
|
4142
4186
|
function outputNewPattern(fileName, match) {
|
|
4143
4187
|
const lines = [];
|
|
@@ -4151,7 +4195,7 @@ function outputNewPattern(fileName, match) {
|
|
|
4151
4195
|
lines.push(" 1. INCIDENT REPORT");
|
|
4152
4196
|
lines.push(" 2. PREVENTION RULE (if new failure pattern)");
|
|
4153
4197
|
lines.push(" 3. ENFORCEMENT (hook or static check)");
|
|
4154
|
-
|
|
4198
|
+
writeHookContext(HOOK_EVENT, lines.join("\n"));
|
|
4155
4199
|
}
|
|
4156
4200
|
function readStdin() {
|
|
4157
4201
|
return new Promise((resolve5) => {
|
|
@@ -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/cost-tracker.ts
|
|
4074
4087
|
init_hook_failure_signal();
|
|
4088
|
+
init_timestamps();
|
|
4075
4089
|
var CHARS_PER_TOKEN = 4;
|
|
4076
4090
|
function estimateTokens(text) {
|
|
4077
4091
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
@@ -4087,9 +4101,9 @@ async function main() {
|
|
|
4087
4101
|
const db = getMemoryDb();
|
|
4088
4102
|
try {
|
|
4089
4103
|
db.prepare(`
|
|
4090
|
-
INSERT INTO tool_cost_events (session_id, tool_name, estimated_input_tokens, estimated_output_tokens, model)
|
|
4091
|
-
VALUES (?, ?, ?, ?, ?)
|
|
4092
|
-
`).run(session_id, tool_name, estimatedInputTokens, estimatedOutputTokens, "");
|
|
4104
|
+
INSERT INTO tool_cost_events (session_id, tool_name, estimated_input_tokens, estimated_output_tokens, model, created_at)
|
|
4105
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
4106
|
+
`).run(session_id, tool_name, estimatedInputTokens, estimatedOutputTokens, "", nowIso());
|
|
4093
4107
|
} finally {
|
|
4094
4108
|
db.close();
|
|
4095
4109
|
}
|
|
@@ -1345,6 +1345,16 @@ var init_memory_vector = __esm({
|
|
|
1345
1345
|
}
|
|
1346
1346
|
});
|
|
1347
1347
|
|
|
1348
|
+
// src/lib/timestamps.ts
|
|
1349
|
+
function nowIso() {
|
|
1350
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1351
|
+
}
|
|
1352
|
+
var init_timestamps = __esm({
|
|
1353
|
+
"src/lib/timestamps.ts"() {
|
|
1354
|
+
"use strict";
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1348
1358
|
// src/db-backup.ts
|
|
1349
1359
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1350
1360
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3605,8 +3615,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3605
3615
|
}
|
|
3606
3616
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3607
3617
|
const result = db.prepare(`
|
|
3608
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3609
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3618
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3619
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3610
3620
|
`).run(
|
|
3611
3621
|
sessionId,
|
|
3612
3622
|
turnNumber,
|
|
@@ -3615,14 +3625,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3615
3625
|
toolCallsJson,
|
|
3616
3626
|
toolCallCount,
|
|
3617
3627
|
promptTokens,
|
|
3618
|
-
responseTokens
|
|
3628
|
+
responseTokens,
|
|
3629
|
+
nowIso()
|
|
3619
3630
|
);
|
|
3620
3631
|
return Number(result.lastInsertRowid);
|
|
3621
3632
|
}
|
|
3622
3633
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3623
3634
|
db.prepare(`
|
|
3624
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3625
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3635
|
+
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)
|
|
3636
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3626
3637
|
`).run(
|
|
3627
3638
|
sessionId,
|
|
3628
3639
|
turnNumber,
|
|
@@ -3631,7 +3642,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3631
3642
|
inputSize,
|
|
3632
3643
|
outputSize,
|
|
3633
3644
|
success ? 1 : 0,
|
|
3634
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
3645
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
3646
|
+
nowIso()
|
|
3635
3647
|
);
|
|
3636
3648
|
}
|
|
3637
3649
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -3890,6 +3902,7 @@ var init_memory_db = __esm({
|
|
|
3890
3902
|
init_db_driver();
|
|
3891
3903
|
init_config();
|
|
3892
3904
|
init_memory_vector();
|
|
3905
|
+
init_timestamps();
|
|
3893
3906
|
init_db_backup();
|
|
3894
3907
|
init_rule_delivery();
|
|
3895
3908
|
init_rule_candidate_store();
|
|
@@ -3986,12 +3999,43 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
3986
3999
|
import { join as join6 } from "path";
|
|
3987
4000
|
|
|
3988
4001
|
// src/hooks/lib/write-hook-message.ts
|
|
3989
|
-
|
|
3990
|
-
|
|
4002
|
+
var HOOK_EVENTS = [
|
|
4003
|
+
"UserPromptSubmit",
|
|
4004
|
+
"UserPromptExpansion",
|
|
4005
|
+
"SessionStart",
|
|
4006
|
+
"PreToolUse",
|
|
4007
|
+
"PostToolUse",
|
|
4008
|
+
"Stop",
|
|
4009
|
+
"SubagentStop",
|
|
4010
|
+
"PreCompact"
|
|
4011
|
+
];
|
|
4012
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4013
|
+
function isHookEvent(value) {
|
|
4014
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4015
|
+
}
|
|
4016
|
+
function writeHookContext(hookEventName, message) {
|
|
4017
|
+
if (!isHookEvent(hookEventName)) {
|
|
4018
|
+
process.stderr.write(
|
|
4019
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4020
|
+
`
|
|
4021
|
+
);
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4025
|
+
let additionalContext = message;
|
|
4026
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4027
|
+
const marker = `
|
|
4028
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4029
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4030
|
+
}
|
|
4031
|
+
process.stdout.write(
|
|
4032
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4033
|
+
);
|
|
3991
4034
|
}
|
|
3992
4035
|
|
|
3993
4036
|
// src/hooks/fix-detector.ts
|
|
3994
4037
|
init_hook_failure_signal();
|
|
4038
|
+
var HOOK_EVENT = "PostToolUse";
|
|
3995
4039
|
var FIX_HEURISTICS = [
|
|
3996
4040
|
{
|
|
3997
4041
|
name: "removed_broken_code",
|
|
@@ -4166,7 +4210,8 @@ async function main() {
|
|
|
4166
4210
|
appendFileSync3(flagPath, JSON.stringify(signal) + "\n");
|
|
4167
4211
|
const lines = readFileSync6(flagPath, "utf-8").split("\n").filter(Boolean);
|
|
4168
4212
|
if (lines.length === 1) {
|
|
4169
|
-
|
|
4213
|
+
writeHookContext(
|
|
4214
|
+
HOOK_EVENT,
|
|
4170
4215
|
`[Massu Auto-Learning] Bug fix detected in ${filePath} (signals: ${detected.join(", ")}). The auto-learning pipeline will prompt you at session end to create an incident report, derive a prevention rule, and add enforcement.`
|
|
4171
4216
|
);
|
|
4172
4217
|
}
|
|
@@ -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();
|
|
@@ -3979,12 +3992,43 @@ import { existsSync as existsSync9, readFileSync as readFileSync6, readdirSync a
|
|
|
3979
3992
|
import { basename as basename3, resolve as resolve5 } from "path";
|
|
3980
3993
|
|
|
3981
3994
|
// src/hooks/lib/write-hook-message.ts
|
|
3982
|
-
|
|
3983
|
-
|
|
3995
|
+
var HOOK_EVENTS = [
|
|
3996
|
+
"UserPromptSubmit",
|
|
3997
|
+
"UserPromptExpansion",
|
|
3998
|
+
"SessionStart",
|
|
3999
|
+
"PreToolUse",
|
|
4000
|
+
"PostToolUse",
|
|
4001
|
+
"Stop",
|
|
4002
|
+
"SubagentStop",
|
|
4003
|
+
"PreCompact"
|
|
4004
|
+
];
|
|
4005
|
+
var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
|
|
4006
|
+
function isHookEvent(value) {
|
|
4007
|
+
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
4008
|
+
}
|
|
4009
|
+
function writeHookContext(hookEventName, message) {
|
|
4010
|
+
if (!isHookEvent(hookEventName)) {
|
|
4011
|
+
process.stderr.write(
|
|
4012
|
+
`[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
|
|
4013
|
+
`
|
|
4014
|
+
);
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
if (typeof message !== "string" || message.length === 0) return;
|
|
4018
|
+
let additionalContext = message;
|
|
4019
|
+
if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
|
|
4020
|
+
const marker = `
|
|
4021
|
+
[massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
|
|
4022
|
+
additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
|
|
4023
|
+
}
|
|
4024
|
+
process.stdout.write(
|
|
4025
|
+
JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
|
|
4026
|
+
);
|
|
3984
4027
|
}
|
|
3985
4028
|
|
|
3986
4029
|
// src/hooks/incident-pipeline.ts
|
|
3987
4030
|
init_hook_failure_signal();
|
|
4031
|
+
var HOOK_EVENT = "PostToolUse";
|
|
3988
4032
|
async function main() {
|
|
3989
4033
|
try {
|
|
3990
4034
|
const input = await readStdin();
|
|
@@ -4067,7 +4111,7 @@ async function main() {
|
|
|
4067
4111
|
lines.push(" This step is MANDATORY per the auto-learning pipeline.");
|
|
4068
4112
|
lines.push("============================================================================");
|
|
4069
4113
|
lines.push("");
|
|
4070
|
-
|
|
4114
|
+
writeHookContext(HOOK_EVENT, lines.join("\n"));
|
|
4071
4115
|
try {
|
|
4072
4116
|
const taxonomyConfig = config.autoLearning?.failureClassification;
|
|
4073
4117
|
if (taxonomyConfig?.enabled !== false) {
|