@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/intent-suggester.ts
3981
4024
  init_hook_failure_signal();
4025
+ var HOOK_EVENT = "UserPromptSubmit";
3982
4026
  var COMMAND_MAPPINGS = [
3983
4027
  {
3984
4028
  keywords: ["test", "failing"],
@@ -4056,7 +4100,7 @@ async function main() {
4056
4100
  process.exit(0);
4057
4101
  return;
4058
4102
  }
4059
- writeHookMessage(`Tip: Use ${match.command} to ${match.description}.`);
4103
+ writeHookContext(HOOK_EVENT, `Tip: Use ${match.command} to ${match.description}.`);
4060
4104
  } catch (err) {
4061
4105
  recordHookFailure("intent-suggester", err);
4062
4106
  }
@@ -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";
@@ -3706,8 +3716,8 @@ function linkSessionToTask(db, sessionId, taskId) {
3706
3716
  }
3707
3717
  function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
3708
3718
  const result = db.prepare(`
3709
- INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
3710
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
3719
+ INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
3720
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3711
3721
  `).run(
3712
3722
  sessionId,
3713
3723
  turnNumber,
@@ -3716,14 +3726,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
3716
3726
  toolCallsJson,
3717
3727
  toolCallCount,
3718
3728
  promptTokens,
3719
- responseTokens
3729
+ responseTokens,
3730
+ nowIso()
3720
3731
  );
3721
3732
  return Number(result.lastInsertRowid);
3722
3733
  }
3723
3734
  function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
3724
3735
  db.prepare(`
3725
- INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
3726
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
3736
+ 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)
3737
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3727
3738
  `).run(
3728
3739
  sessionId,
3729
3740
  turnNumber,
@@ -3732,7 +3743,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
3732
3743
  inputSize,
3733
3744
  outputSize,
3734
3745
  success ? 1 : 0,
3735
- filesInvolved ? JSON.stringify(filesInvolved) : null
3746
+ filesInvolved ? JSON.stringify(filesInvolved) : null,
3747
+ nowIso()
3736
3748
  );
3737
3749
  }
3738
3750
  function getLastProcessedLine(db, sessionId) {
@@ -3991,6 +4003,7 @@ var init_memory_db = __esm({
3991
4003
  init_db_driver();
3992
4004
  init_config();
3993
4005
  init_memory_vector();
4006
+ init_timestamps();
3994
4007
  init_db_backup();
3995
4008
  init_rule_delivery();
3996
4009
  init_rule_candidate_store();
@@ -4520,14 +4533,45 @@ function pendingPointer(db) {
4520
4533
  init_memory_embedder();
4521
4534
 
4522
4535
  // src/hooks/lib/write-hook-message.ts
4523
- function writeHookMessage(message) {
4524
- process.stdout.write(JSON.stringify({ message }) + "\n");
4536
+ var HOOK_EVENTS = [
4537
+ "UserPromptSubmit",
4538
+ "UserPromptExpansion",
4539
+ "SessionStart",
4540
+ "PreToolUse",
4541
+ "PostToolUse",
4542
+ "Stop",
4543
+ "SubagentStop",
4544
+ "PreCompact"
4545
+ ];
4546
+ var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
4547
+ function isHookEvent(value) {
4548
+ return typeof value === "string" && HOOK_EVENTS.includes(value);
4549
+ }
4550
+ function writeHookContext(hookEventName, message) {
4551
+ if (!isHookEvent(hookEventName)) {
4552
+ process.stderr.write(
4553
+ `[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
4554
+ `
4555
+ );
4556
+ return;
4557
+ }
4558
+ if (typeof message !== "string" || message.length === 0) return;
4559
+ let additionalContext = message;
4560
+ if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
4561
+ const marker = `
4562
+ [massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
4563
+ additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
4564
+ }
4565
+ process.stdout.write(
4566
+ JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
4567
+ );
4525
4568
  }
4526
4569
 
4527
4570
  // src/hooks/memory-recall.ts
4528
4571
  init_db_driver();
4529
4572
  init_hook_failure_signal();
4530
4573
  import { existsSync as existsSync9 } from "fs";
4574
+ var HOOK_EVENT = "UserPromptSubmit";
4531
4575
  var DEFAULTS = {
4532
4576
  enabled: true,
4533
4577
  maxTokens: 1200,
@@ -4581,7 +4625,7 @@ async function main() {
4581
4625
  cfg.timeoutMs
4582
4626
  );
4583
4627
  if (block && block.trim()) {
4584
- writeHookMessage(block);
4628
+ writeHookContext(HOOK_EVENT, block);
4585
4629
  }
4586
4630
  } catch {
4587
4631
  } finally {
@@ -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();
@@ -4013,12 +4026,43 @@ function isInMiddlewareTree(dataDb, file) {
4013
4026
  init_config();
4014
4027
 
4015
4028
  // src/hooks/lib/write-hook-message.ts
4016
- function writeHookMessage(message) {
4017
- process.stdout.write(JSON.stringify({ message }) + "\n");
4029
+ var HOOK_EVENTS = [
4030
+ "UserPromptSubmit",
4031
+ "UserPromptExpansion",
4032
+ "SessionStart",
4033
+ "PreToolUse",
4034
+ "PostToolUse",
4035
+ "Stop",
4036
+ "SubagentStop",
4037
+ "PreCompact"
4038
+ ];
4039
+ var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
4040
+ function isHookEvent(value) {
4041
+ return typeof value === "string" && HOOK_EVENTS.includes(value);
4042
+ }
4043
+ function writeHookContext(hookEventName, message) {
4044
+ if (!isHookEvent(hookEventName)) {
4045
+ process.stderr.write(
4046
+ `[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
4047
+ `
4048
+ );
4049
+ return;
4050
+ }
4051
+ if (typeof message !== "string" || message.length === 0) return;
4052
+ let additionalContext = message;
4053
+ if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
4054
+ const marker = `
4055
+ [massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
4056
+ additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
4057
+ }
4058
+ process.stdout.write(
4059
+ JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
4060
+ );
4018
4061
  }
4019
4062
 
4020
4063
  // src/hooks/post-edit-context.ts
4021
4064
  init_hook_failure_signal();
4065
+ var HOOK_EVENT = "PostToolUse";
4022
4066
  async function main() {
4023
4067
  try {
4024
4068
  const input = await readStdin();
@@ -4055,7 +4099,7 @@ async function main() {
4055
4099
  } catch (_e) {
4056
4100
  }
4057
4101
  if (warnings.length > 0) {
4058
- writeHookMessage(`[Massu] ${warnings.join(" | ")}`);
4102
+ writeHookContext(HOOK_EVENT, `[Massu] ${warnings.join(" | ")}`);
4059
4103
  }
4060
4104
  } catch (err) {
4061
4105
  recordHookFailure("post-edit-context", err);
@@ -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";
@@ -3676,8 +3686,8 @@ function linkSessionToTask(db, sessionId, taskId) {
3676
3686
  }
3677
3687
  function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
3678
3688
  const result = db.prepare(`
3679
- INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
3680
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
3689
+ INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
3690
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3681
3691
  `).run(
3682
3692
  sessionId,
3683
3693
  turnNumber,
@@ -3686,14 +3696,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
3686
3696
  toolCallsJson,
3687
3697
  toolCallCount,
3688
3698
  promptTokens,
3689
- responseTokens
3699
+ responseTokens,
3700
+ nowIso()
3690
3701
  );
3691
3702
  return Number(result.lastInsertRowid);
3692
3703
  }
3693
3704
  function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
3694
3705
  db.prepare(`
3695
- INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
3696
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
3706
+ 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)
3707
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
3697
3708
  `).run(
3698
3709
  sessionId,
3699
3710
  turnNumber,
@@ -3702,7 +3713,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
3702
3713
  inputSize,
3703
3714
  outputSize,
3704
3715
  success ? 1 : 0,
3705
- filesInvolved ? JSON.stringify(filesInvolved) : null
3716
+ filesInvolved ? JSON.stringify(filesInvolved) : null,
3717
+ nowIso()
3706
3718
  );
3707
3719
  }
3708
3720
  function getLastProcessedLine(db, sessionId) {
@@ -3961,6 +3973,7 @@ var init_memory_db = __esm({
3961
3973
  init_db_driver();
3962
3974
  init_config();
3963
3975
  init_memory_vector();
3976
+ init_timestamps();
3964
3977
  init_db_backup();
3965
3978
  init_rule_delivery();
3966
3979
  init_rule_candidate_store();
@@ -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();
@@ -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,12 +4085,43 @@ function getFeatureImpact(db, filePaths) {
4072
4085
  init_config();
4073
4086
 
4074
4087
  // src/hooks/lib/write-hook-message.ts
4075
- function writeHookMessage(message) {
4076
- process.stdout.write(JSON.stringify({ message }) + "\n");
4088
+ var HOOK_EVENTS = [
4089
+ "UserPromptSubmit",
4090
+ "UserPromptExpansion",
4091
+ "SessionStart",
4092
+ "PreToolUse",
4093
+ "PostToolUse",
4094
+ "Stop",
4095
+ "SubagentStop",
4096
+ "PreCompact"
4097
+ ];
4098
+ var ADDITIONAL_CONTEXT_MAX_CHARS = 1e4;
4099
+ function isHookEvent(value) {
4100
+ return typeof value === "string" && HOOK_EVENTS.includes(value);
4101
+ }
4102
+ function writeHookContext(hookEventName, message) {
4103
+ if (!isHookEvent(hookEventName)) {
4104
+ process.stderr.write(
4105
+ `[massu] writeHookContext: refusing to emit \u2014 unrecognised hook event ${JSON.stringify(hookEventName)}. Known: ${HOOK_EVENTS.join(", ")}.
4106
+ `
4107
+ );
4108
+ return;
4109
+ }
4110
+ if (typeof message !== "string" || message.length === 0) return;
4111
+ let additionalContext = message;
4112
+ if (additionalContext.length > ADDITIONAL_CONTEXT_MAX_CHARS) {
4113
+ const marker = `
4114
+ [massu] \u2026truncated at ${ADDITIONAL_CONTEXT_MAX_CHARS} chars (spec cap).`;
4115
+ additionalContext = additionalContext.slice(0, ADDITIONAL_CONTEXT_MAX_CHARS - marker.length) + marker;
4116
+ }
4117
+ process.stdout.write(
4118
+ JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) + "\n"
4119
+ );
4077
4120
  }
4078
4121
 
4079
4122
  // src/hooks/pre-delete-check.ts
4080
4123
  init_hook_failure_signal();
4124
+ var HOOK_EVENT = "PreToolUse";
4081
4125
  var PROJECT_ROOT2 = getProjectRoot();
4082
4126
  var KNOWLEDGE_PROTECTED_FILES = [
4083
4127
  "knowledge-db.ts",
@@ -4221,7 +4265,7 @@ async function main() {
4221
4265
  const input = await readStdin();
4222
4266
  const hookInput = JSON.parse(input);
4223
4267
  const messages = runPreDeleteChecks(hookInput);
4224
- for (const msg of messages) writeHookMessage(msg);
4268
+ for (const msg of messages) writeHookContext(HOOK_EVENT, msg);
4225
4269
  } catch (err) {
4226
4270
  recordHookFailure("pre-delete-check", err);
4227
4271
  process.stderr.write("MASSU SECURITY GATE \u2014 BLOCKED (gate could not evaluate this call)\n");