@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.
@@ -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
- function writeHookMessage(message) {
3977
- process.stdout.write(JSON.stringify({ message }) + "\n");
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) writeHookMessage(msg);
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) writeHookMessage(msg);
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
- writeHookMessage(f.message);
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
- function writeHookMessage(message) {
3982
- process.stdout.write(JSON.stringify({ message }) + "\n");
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
- writeHookMessage(lines.join("\n"));
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
- function writeHookMessage(message) {
3977
- process.stdout.write(JSON.stringify({ message }) + "\n");
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) writeHookMessage(msg);
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();