context-mode 1.0.161 → 1.0.163

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.
Files changed (153) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  5. package/.openclaw-plugin/package.json +1 -1
  6. package/README.md +142 -28
  7. package/bin/statusline.mjs +24 -4
  8. package/build/adapters/antigravity/index.d.ts +1 -1
  9. package/build/adapters/antigravity-cli/index.d.ts +51 -0
  10. package/build/adapters/antigravity-cli/index.js +341 -0
  11. package/build/adapters/claude-code/hooks.d.ts +1 -0
  12. package/build/adapters/claude-code/hooks.js +3 -0
  13. package/build/adapters/claude-code/index.js +24 -5
  14. package/build/adapters/client-map.js +5 -0
  15. package/build/adapters/codex/hooks.d.ts +5 -1
  16. package/build/adapters/codex/hooks.js +5 -1
  17. package/build/adapters/codex/index.d.ts +9 -1
  18. package/build/adapters/codex/index.js +87 -5
  19. package/build/adapters/copilot-cli/hooks.d.ts +33 -0
  20. package/build/adapters/copilot-cli/hooks.js +64 -0
  21. package/build/adapters/copilot-cli/index.d.ts +48 -0
  22. package/build/adapters/copilot-cli/index.js +341 -0
  23. package/build/adapters/detect.d.ts +1 -1
  24. package/build/adapters/detect.js +71 -3
  25. package/build/adapters/openclaw/mcp-tools.js +1 -1
  26. package/build/adapters/opencode/index.js +31 -17
  27. package/build/adapters/opencode/zod3tov4.js +27 -6
  28. package/build/adapters/pi/extension.d.ts +2 -12
  29. package/build/adapters/pi/extension.js +114 -96
  30. package/build/adapters/types.d.ts +5 -4
  31. package/build/adapters/types.js +4 -3
  32. package/build/cache-heal.d.ts +48 -0
  33. package/build/cache-heal.js +150 -0
  34. package/build/cli.js +37 -97
  35. package/build/executor.d.ts +25 -0
  36. package/build/executor.js +143 -22
  37. package/build/opencode-plugin.js +5 -2
  38. package/build/routing-block.d.ts +8 -0
  39. package/build/routing-block.js +86 -0
  40. package/build/runtime.d.ts +0 -36
  41. package/build/runtime.js +107 -27
  42. package/build/search/flood-guard.d.ts +57 -0
  43. package/build/search/flood-guard.js +80 -0
  44. package/build/security.d.ts +8 -3
  45. package/build/security.js +155 -29
  46. package/build/server.d.ts +14 -0
  47. package/build/server.js +368 -350
  48. package/build/session/analytics.d.ts +8 -8
  49. package/build/session/analytics.js +18 -13
  50. package/build/session/db.d.ts +1 -0
  51. package/build/session/db.js +37 -4
  52. package/build/session/extract.d.ts +46 -0
  53. package/build/session/extract.js +764 -13
  54. package/build/session/project-attribution.js +14 -0
  55. package/build/store.d.ts +1 -1
  56. package/build/store.js +139 -25
  57. package/build/tool-naming.d.ts +4 -0
  58. package/build/tool-naming.js +24 -0
  59. package/build/util/jsonc.d.ts +14 -0
  60. package/build/util/jsonc.js +104 -0
  61. package/cli.bundle.mjs +260 -254
  62. package/configs/antigravity/GEMINI.md +2 -2
  63. package/configs/antigravity-cli/hooks/hooks.json +37 -0
  64. package/configs/antigravity-cli/hooks.json +37 -0
  65. package/configs/antigravity-cli/mcp_config.json +10 -0
  66. package/configs/antigravity-cli/plugin.json +14 -0
  67. package/configs/antigravity-cli/rules/context-mode.md +77 -0
  68. package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
  69. package/configs/claude-code/CLAUDE.md +2 -2
  70. package/configs/codex/AGENTS.md +2 -2
  71. package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
  72. package/configs/copilot-cli/.mcp.json +12 -0
  73. package/configs/copilot-cli/README.md +47 -0
  74. package/configs/copilot-cli/hooks.json +41 -0
  75. package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
  76. package/configs/gemini-cli/GEMINI.md +2 -2
  77. package/configs/jetbrains-copilot/copilot-instructions.md +2 -2
  78. package/configs/kilo/AGENTS.md +2 -2
  79. package/configs/kiro/KIRO.md +2 -2
  80. package/configs/omp/SYSTEM.md +2 -2
  81. package/configs/openclaw/AGENTS.md +2 -2
  82. package/configs/opencode/AGENTS.md +2 -2
  83. package/configs/qwen-code/QWEN.md +2 -2
  84. package/configs/vscode-copilot/copilot-instructions.md +2 -2
  85. package/configs/zed/AGENTS.md +2 -2
  86. package/hooks/antigravity-cli/payload.mjs +98 -0
  87. package/hooks/antigravity-cli/posttooluse.mjs +138 -0
  88. package/hooks/antigravity-cli/pretooluse.mjs +78 -0
  89. package/hooks/antigravity-cli/stop.mjs +58 -0
  90. package/hooks/codex/pretooluse.mjs +14 -4
  91. package/hooks/codex/stop.mjs +12 -4
  92. package/hooks/copilot-cli/posttooluse.mjs +79 -0
  93. package/hooks/copilot-cli/precompact.mjs +66 -0
  94. package/hooks/copilot-cli/pretooluse.mjs +41 -0
  95. package/hooks/copilot-cli/sessionstart.mjs +121 -0
  96. package/hooks/copilot-cli/stop.mjs +59 -0
  97. package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
  98. package/hooks/core/codex-caps.mjs +112 -0
  99. package/hooks/core/formatters.mjs +158 -7
  100. package/hooks/core/mcp-ready.mjs +37 -8
  101. package/hooks/core/routing.mjs +94 -8
  102. package/hooks/core/tool-naming.mjs +3 -0
  103. package/hooks/hooks.json +12 -1
  104. package/hooks/pretooluse.mjs +6 -2
  105. package/hooks/routing-block.mjs +2 -2
  106. package/hooks/security.bundle.mjs +2 -1
  107. package/hooks/session-db.bundle.mjs +11 -7
  108. package/hooks/session-directive.mjs +88 -20
  109. package/hooks/session-extract.bundle.mjs +2 -2
  110. package/hooks/session-helpers.mjs +21 -0
  111. package/hooks/session-loaders.mjs +8 -5
  112. package/hooks/sessionstart.mjs +53 -7
  113. package/hooks/stop.mjs +49 -0
  114. package/hooks/userpromptsubmit.mjs +9 -2
  115. package/openclaw.plugin.json +1 -1
  116. package/package.json +4 -10
  117. package/scripts/install-antigravity-cli-plugin.mjs +141 -0
  118. package/server.bundle.mjs +214 -205
  119. package/skills/ctx-insight/SKILL.md +12 -17
  120. package/build/util/db-lock.d.ts +0 -65
  121. package/build/util/db-lock.js +0 -166
  122. package/insight/index.html +0 -13
  123. package/insight/package.json +0 -55
  124. package/insight/server.mjs +0 -1265
  125. package/insight/src/components/analytics.tsx +0 -112
  126. package/insight/src/components/ui/badge.tsx +0 -52
  127. package/insight/src/components/ui/button.tsx +0 -58
  128. package/insight/src/components/ui/card.tsx +0 -103
  129. package/insight/src/components/ui/chart.tsx +0 -371
  130. package/insight/src/components/ui/collapsible.tsx +0 -19
  131. package/insight/src/components/ui/input.tsx +0 -20
  132. package/insight/src/components/ui/progress.tsx +0 -83
  133. package/insight/src/components/ui/scroll-area.tsx +0 -55
  134. package/insight/src/components/ui/separator.tsx +0 -23
  135. package/insight/src/components/ui/table.tsx +0 -114
  136. package/insight/src/components/ui/tabs.tsx +0 -82
  137. package/insight/src/components/ui/tooltip.tsx +0 -64
  138. package/insight/src/lib/api.ts +0 -144
  139. package/insight/src/lib/utils.ts +0 -6
  140. package/insight/src/main.tsx +0 -22
  141. package/insight/src/routeTree.gen.ts +0 -189
  142. package/insight/src/router.tsx +0 -19
  143. package/insight/src/routes/__root.tsx +0 -55
  144. package/insight/src/routes/enterprise.tsx +0 -316
  145. package/insight/src/routes/index.tsx +0 -1482
  146. package/insight/src/routes/knowledge.tsx +0 -221
  147. package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +0 -137
  148. package/insight/src/routes/search.tsx +0 -97
  149. package/insight/src/routes/sessions.tsx +0 -179
  150. package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +0 -181
  151. package/insight/src/styles.css +0 -104
  152. package/insight/tsconfig.json +0 -29
  153. package/insight/vite.config.ts +0 -19
@@ -7,10 +7,54 @@
7
7
 
8
8
  import { writeFileSync } from "node:fs";
9
9
 
10
+ // ── Leg-boundary helpers (#780) ──
11
+ // The current Claude Code session_id persists across `--continue` legs, so the
12
+ // events array can carry rows written in a PRIOR leg. The last `session_start`
13
+ // lifecycle event (emitted by the prior leg's SessionStart, before this leg's
14
+ // directive is built — sessionstart.mjs:206/258, 277/293) is the boundary that
15
+ // opened the current leg. Rows with created_at < boundary are prior-leg.
16
+ function computeLegBoundary(events) {
17
+ let boundary = null;
18
+ for (const ev of events) {
19
+ if (ev.category === "session_start" && ev.created_at) boundary = ev.created_at;
20
+ }
21
+ return boundary;
22
+ }
23
+
24
+ // True when an event was written in a leg strictly before the current one.
25
+ // No boundary (first leg) → nothing is prior-leg, so everything stays current.
26
+ function isPriorLeg(ev, boundary) {
27
+ return boundary != null && ev.created_at != null && ev.created_at < boundary;
28
+ }
29
+
30
+ // ── Data References sizing (#840) ──
31
+ // Inlining every captured tool-output verbatim defeats context-mode's own
32
+ // raw-bytes-stay-out principle. Inline only a small recent window; reference
33
+ // large blobs with a one-line pointer. The full payloads stay queryable in
34
+ // FTS5 via ctx_search(source: "session-events").
35
+ const DATA_REF_INLINE_MAX = 8; // most-recent captures rendered inline
36
+ const DATA_REF_ENTRY_MAX = 150; // per-entry char cap before it is referenced
37
+
38
+ function renderDataReferences(entries, push, searchHint) {
39
+ const recent = entries.slice(-DATA_REF_INLINE_MAX);
40
+ for (const ev of recent) {
41
+ const raw = ev.data ?? "";
42
+ const text = raw.length > DATA_REF_ENTRY_MAX
43
+ ? `${raw.substring(0, DATA_REF_ENTRY_MAX - 3)}… (${raw.length} bytes — query ${searchHint})`
44
+ : raw;
45
+ push(`- ${text}`);
46
+ }
47
+ const older = entries.length - recent.length;
48
+ if (older > 0) {
49
+ push(`- … ${older} older captures kept in the sandbox — query ${searchHint}.`);
50
+ }
51
+ }
52
+
10
53
  // ── Group events by category and extract metadata ──
11
54
  export function groupEvents(events) {
12
55
  const grouped = {};
13
56
  let lastPrompt = "";
57
+ const legBoundary = computeLegBoundary(events);
14
58
  for (const ev of events) {
15
59
  if (ev.category === "prompt") {
16
60
  lastPrompt = ev.data;
@@ -25,13 +69,13 @@ export function groupEvents(events) {
25
69
  const base = path?.split(/[/\\]/).pop()?.trim();
26
70
  if (base && !base.includes("*")) fileNames.add(base);
27
71
  }
28
- return { grouped, lastPrompt, fileNames };
72
+ return { grouped, lastPrompt, fileNames, legBoundary };
29
73
  }
30
74
 
31
75
  // ── Write session events as markdown for FTS5 auto-indexing ──
32
76
  // Structured with H2 headings per category — optimal for FTS5 chunking.
33
77
  export function writeSessionEventsFile(events, eventsPath) {
34
- const { grouped, lastPrompt, fileNames } = groupEvents(events);
78
+ const { grouped, lastPrompt, fileNames, legBoundary } = groupEvents(events);
35
79
 
36
80
  const lines = [];
37
81
  lines.push("# Session Resume");
@@ -149,10 +193,23 @@ export function writeSessionEventsFile(events, eventsPath) {
149
193
  }
150
194
 
151
195
  if (grouped.subagent?.length > 0) {
152
- lines.push("## Subagent Tasks");
153
- lines.push("");
154
- for (const ev of grouped.subagent) lines.push(`- ${ev.data}`);
155
- lines.push("");
196
+ // #780 — only current-leg subagent events are "current". Prior-leg ones
197
+ // are reframed as history so stale [completed]/[launched] labels are not
198
+ // re-injected as if the agent ran in this conversation.
199
+ const currentSub = grouped.subagent.filter(ev => !isPriorLeg(ev, legBoundary));
200
+ const priorSub = grouped.subagent.filter(ev => isPriorLeg(ev, legBoundary));
201
+ if (currentSub.length > 0) {
202
+ lines.push("## Subagent Tasks");
203
+ lines.push("");
204
+ for (const ev of currentSub) lines.push(`- ${ev.data}`);
205
+ lines.push("");
206
+ }
207
+ if (priorSub.length > 0) {
208
+ lines.push("## Subagent Tasks (earlier session — not current)");
209
+ lines.push("");
210
+ for (const ev of priorSub) lines.push(`- ${ev.data}`);
211
+ lines.push("");
212
+ }
156
213
  }
157
214
 
158
215
  if (grouped.skill?.length > 0) {
@@ -180,7 +237,11 @@ export function writeSessionEventsFile(events, eventsPath) {
180
237
  if (grouped.data?.length > 0) {
181
238
  lines.push("## Data References");
182
239
  lines.push("");
183
- for (const ev of grouped.data) lines.push(`- ${ev.data}`);
240
+ renderDataReferences(
241
+ grouped.data,
242
+ (l) => lines.push(l),
243
+ 'ctx_search(source: "session-events")',
244
+ );
184
245
  lines.push("");
185
246
  }
186
247
 
@@ -212,8 +273,10 @@ export function writeSessionEventsFile(events, eventsPath) {
212
273
 
213
274
  // ── Build session guide — actionable narrative for LLM to continue from ──
214
275
  export function buildSessionDirective(source, eventMeta, toolNamer) {
215
- const { grouped, lastPrompt, fileNames } = eventMeta;
276
+ const { grouped, lastPrompt, fileNames, legBoundary } = eventMeta;
216
277
  const isCompact = source === "compact";
278
+ const searchTool = toolNamer ? toolNamer("ctx_search") : "ctx_search";
279
+ const dataSearchHint = `${searchTool}(source: "session-events")`;
217
280
 
218
281
  let block = `\n<session_knowledge source="${isCompact ? "compact" : "continue"}">`;
219
282
  block += `\n<session_guide>`;
@@ -333,14 +396,23 @@ export function buildSessionDirective(source, eventMeta, toolNamer) {
333
396
  block += `\n`;
334
397
  }
335
398
 
336
- // 9. Subagent tasks
399
+ // 9. Subagent tasks — #780: prior-leg events are NOT current work. Render
400
+ // only current-leg subagents under "Subagent Tasks"; reframe prior-leg
401
+ // ones so stale [completed]/[launched] labels don't read as this leg's.
337
402
  if (grouped.subagent?.length > 0) {
338
- block += `\n## Subagent Tasks`;
339
- for (const ev of grouped.subagent) {
340
- const text = ev.data.length > 120 ? ev.data.substring(0, 117) + "..." : ev.data;
341
- block += `\n- ${text}`;
403
+ const fmt = (ev) => ev.data.length > 120 ? ev.data.substring(0, 117) + "..." : ev.data;
404
+ const currentSub = grouped.subagent.filter(ev => !isPriorLeg(ev, legBoundary));
405
+ const priorSub = grouped.subagent.filter(ev => isPriorLeg(ev, legBoundary));
406
+ if (currentSub.length > 0) {
407
+ block += `\n## Subagent Tasks`;
408
+ for (const ev of currentSub) block += `\n- ${fmt(ev)}`;
409
+ block += `\n`;
410
+ }
411
+ if (priorSub.length > 0) {
412
+ block += `\n## Subagent Tasks (earlier session — not current)`;
413
+ for (const ev of priorSub) block += `\n- ${fmt(ev)}`;
414
+ block += `\n`;
342
415
  }
343
- block += `\n`;
344
416
  }
345
417
 
346
418
  // 10. Skills invoked
@@ -363,13 +435,10 @@ export function buildSessionDirective(source, eventMeta, toolNamer) {
363
435
  block += `\n`;
364
436
  }
365
437
 
366
- // 12. Data references
438
+ // 12. Data references — #840: reference (don't inline) large tool-outputs.
367
439
  if (grouped.data?.length > 0) {
368
440
  block += `\n## Data References`;
369
- for (const ev of grouped.data) {
370
- const text = ev.data.length > 150 ? ev.data.substring(0, 147) + "..." : ev.data;
371
- block += `\n- ${text}`;
372
- }
441
+ renderDataReferences(grouped.data, (l) => { block += `\n${l}`; }, dataSearchHint);
373
442
  block += `\n`;
374
443
  }
375
444
 
@@ -417,7 +486,6 @@ export function buildSessionDirective(source, eventMeta, toolNamer) {
417
486
  // Search on demand — detailed data lives in FTS5
418
487
  block += `\n<session_search>`;
419
488
  block += `\nDetailed session data is indexed in context-mode FTS5 (source: "session-events").`;
420
- const searchTool = toolNamer ? toolNamer("ctx_search") : "ctx_search";
421
489
  block += `\nUse ${searchTool}(queries: [...], source: "session-events") when you need specifics.`;
422
490
  block += `\nDo NOT call ctx_index() — data is already indexed.`;
423
491
  block += `\n</session_search>`;
@@ -1,2 +1,2 @@
1
- function i(t){return t==null?"":String(t)}function E(t){return t==null?"":typeof t=="string"?t:JSON.stringify(t)}function d(t){let e=String(t.tool_response??""),n=String(t.tool_input?.command??"");if(e.startsWith("context-mode:")||n.startsWith('echo "context-mode:')||n.startsWith("echo 'context-mode:"))return!1;let r=t.tool_output?.isError===!0||t.tool_output?.is_error===!0;return t.tool_name==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(e)||r}function k(t){if(!t)return[];let e=[];for(let r of t.split(/\r?\n/)){if(r.startsWith("*** Add File: ")){e.push({path:r.slice(14).trim(),type:"file_write"});continue}if(r.startsWith("*** Update File: ")){e.push({path:r.slice(17).trim(),type:"file_edit"});continue}if(r.startsWith("*** Delete File: ")){e.push({path:r.slice(17).trim(),type:"file_edit"});continue}r.startsWith("*** Move to: ")&&e.push({path:r.slice(13).trim(),type:"file_edit"})}let n=new Set;return e.filter(r=>{if(!r.path)return!1;let o=`${r.type}:${r.path}`;return n.has(o)?!1:(n.add(o),!0)})}function S(t){return/(?:^|[/\\])\.claude[/\\]plans[/\\]/.test(t)}function A(t){let{tool_name:e,tool_input:n,tool_response:r}=t,o=[];if(e==="Read"){let s=String(n.file_path??"");return(/(?:CLAUDE|AGENTS(?:\.override)?|GEMINI|QWEN|KIRO)\.md$/i.test(s)||/\/copilot-instructions\.md$/i.test(s)||/\/context-mode\.mdc$/i.test(s)||/\.claude[\\/]/i.test(s)||/[\\/]memor(?:y|ies)[\\/][^\\/]+\.md$/i.test(s))&&(o.push({type:"rule",category:"rule",data:i(s),priority:1}),r&&r.length>0&&o.push({type:"rule_content",category:"rule",data:i(r),priority:1})),o.push({type:"file_read",category:"file",data:i(s),priority:1}),o}if(e==="Edit"){let s=String(n.file_path??"");return o.push({type:"file_edit",category:"file",data:i(s),priority:1}),o}if(e==="NotebookEdit"){let s=String(n.notebook_path??"");return o.push({type:"file_edit",category:"file",data:i(s),priority:1}),o}if(e==="Write"){let s=String(n.file_path??"");return o.push({type:"file_write",category:"file",data:i(s),priority:1}),o}if(e==="apply_patch"){if(d(t))return[];let s=k(String(n.command??n.patch??""));for(let a of s)o.push({type:a.type,category:"file",data:i(a.path),priority:1});return o}if(e==="Glob"){let s=String(n.pattern??"");return o.push({type:"file_glob",category:"file",data:i(s),priority:3}),o}if(e==="Grep"){let s=String(n.pattern??""),a=String(n.path??"");return o.push({type:"file_search",category:"file",data:i(`${s} in ${a}`),priority:3}),o}return o}function R(t){if(t.tool_name!=="Bash")return[];let n=String(t.tool_input.command??"").match(/\bcd\s+("([^"]+)"|'([^']+)'|(\S+))/);if(!n)return[];let r=n[2]??n[3]??n[4]??"";return[{type:"cwd",category:"cwd",data:i(r),priority:2}]}function T(t){let{tool_response:e}=t,n=String(e??"");return d(t)?[{type:"error_tool",category:"error",data:i(n),priority:2}]:[]}var x=[{pattern:/\bgit\s+checkout\b/,operation:"branch"},{pattern:/\bgit\s+commit\b/,operation:"commit"},{pattern:/\bgit\s+merge\s+\S+/,operation:"merge"},{pattern:/\bgit\s+rebase\b/,operation:"rebase"},{pattern:/\bgit\s+stash\b/,operation:"stash"},{pattern:/\bgit\s+push\b/,operation:"push"},{pattern:/\bgit\s+pull\b/,operation:"pull"},{pattern:/\bgit\s+log\b/,operation:"log"},{pattern:/\bgit\s+diff\b/,operation:"diff"},{pattern:/\bgit\s+status\b/,operation:"status"},{pattern:/\bgit\s+branch\b/,operation:"branch"},{pattern:/\bgit\s+reset\b/,operation:"reset"},{pattern:/\bgit\s+add\b/,operation:"add"},{pattern:/\bgit\s+cherry-pick\b/,operation:"cherry-pick"},{pattern:/\bgit\s+tag\b/,operation:"tag"},{pattern:/\bgit\s+fetch\b/,operation:"fetch"},{pattern:/\bgit\s+clone\b/,operation:"clone"},{pattern:/\bgit\s+worktree\b/,operation:"worktree"}];function w(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??""),n=x.find(r=>r.pattern.test(e));return n?[{type:"git",category:"git",data:i(n.operation),priority:2}]:[]}function I(t){return new Set(["TodoWrite","TaskCreate","TaskUpdate"]).has(t.tool_name)?[{type:t.tool_name==="TaskUpdate"?"task_update":t.tool_name==="TaskCreate"?"task_create":"task",category:"task",data:i(JSON.stringify(t.tool_input)),priority:1}]:[]}function N(t){if(t.tool_name==="EnterPlanMode")return[{type:"plan_enter",category:"plan",data:"entered plan mode",priority:2}];if(t.tool_name==="ExitPlanMode"){let e=[],n=t.tool_input.allowedPrompts,r=Array.isArray(n)&&n.length>0?`exited plan mode (allowed: ${E(n.map(s=>typeof s=="object"&&s!==null&&"prompt"in s?String(s.prompt):String(s)).join(", "))})`:"exited plan mode";e.push({type:"plan_exit",category:"plan",data:i(r),priority:2});let o=String(t.tool_response??"").toLowerCase();return o.includes("approved")||o.includes("approve")?e.push({type:"plan_approved",category:"plan",data:"plan approved by user",priority:1}):(o.includes("rejected")||o.includes("decline")||o.includes("denied"))&&e.push({type:"plan_rejected",category:"plan",data:i(`plan rejected: ${t.tool_response??""}`),priority:2}),e}if(t.tool_name==="Write"||t.tool_name==="Edit"){let e=String(t.tool_input.file_path??"");if(S(e))return[{type:"plan_file_write",category:"plan",data:i(`plan file: ${e.split(/[/\\]/).pop()??e}`),priority:2}]}return t.tool_name==="apply_patch"?d(t)?[]:k(String(t.tool_input.command??t.tool_input.patch??"")).filter(n=>S(n.path)).map(n=>({type:"plan_file_write",category:"plan",data:i(`plan file: ${n.path.split(/[/\\]/).pop()??n.path}`),priority:2})):[]}var C=[/\bsource\s+\S*activate\b/,/\bexport\s+\w+=/,/\bnvm\s+use\b/,/\bpyenv\s+(shell|local|global)\b/,/\bconda\s+activate\b/,/\brbenv\s+(shell|local|global)\b/,/\bnpm\s+install\b/,/\bnpm\s+ci\b/,/\bpip\s+install\b/,/\bbun\s+install\b/,/\byarn\s+(add|install)\b/,/\bpnpm\s+(add|install)\b/,/\bcargo\s+(install|add)\b/,/\bgo\s+(install|get)\b/,/\brustup\b/,/\basdf\b/,/\bvolta\b/,/\bdeno\s+install\b/];function L(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??"");if(!C.some(o=>o.test(e)))return[];let r=e.replace(/\bexport\s+(\w+)=\S*/g,"export $1=***");return[{type:"env",category:"env",data:i(r),priority:2}]}function $(t){if(t.tool_name!=="Skill")return[];let e=String(t.tool_input.skill??"");return[{type:"skill",category:"skill",data:i(e),priority:2}]}function H(t){if(!t.tool_response?.includes("Error")&&!t.tool_output?.isError)return[];let e=String(t.tool_response||""),n=[/not supported/i,/cannot/i,/does not support/i,/FAIL/i,/refused/i,/permission denied/i,/incompatible/i];for(let r of n){let o=e.match(r);if(o){let s=e.toLowerCase().indexOf(o[0].toLowerCase()),a=e.slice(Math.max(0,s-50),Math.min(e.length,s+200)).trim();return[{type:"constraint_discovered",category:"constraint",data:i(a),priority:2}]}}return[]}function O(t){if(t.tool_name!=="Agent")return[];let e=i(String(t.tool_input.prompt??t.tool_input.description??"")),n=t.tool_response?i(String(t.tool_response)):"",r=n.length>0;return[{type:r?"subagent_completed":"subagent_launched",category:"subagent",data:i(r?`[completed] ${e} \u2192 ${n}`:`[launched] ${e}`),priority:r?2:3}]}function P(t){let{tool_name:e,tool_input:n,tool_response:r}=t;if(!e.startsWith("mcp__"))return[];let o=e.split("__"),s=o[o.length-1]||e,a=Object.values(n).find(g=>typeof g=="string"),u=a?`: ${i(String(a))}`:"",c=r&&r.length>0?`
2
- response: ${i(r)}`:"";return[{type:"mcp",category:"mcp",data:i(`${s}${u}${c}`),priority:3}]}var M=2048;function B(t,e){if(Buffer.byteLength(t,"utf8")<=e)return{value:t,truncated:!1};let n=Buffer.from(t,"utf8"),r=e;for(;r>0&&(n[r]&192)===128;)r--;return{value:n.subarray(0,r).toString("utf8"),truncated:!0}}var W=/(authorization|auth_token|access_token|refresh_token|bearer|token|secret|password|passwd|pwd|api[-_]?key|apikey|cookie|set-cookie|signature|private[-_]?key|client[-_]?secret|x[-_]?api[-_]?key)/i,j="[REDACTED]";function y(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))return"[CIRCULAR]";e.add(t);let n;if(Array.isArray(t))n=t.map(r=>y(r,e));else{let r={};for(let[o,s]of Object.entries(t))W.test(o)?r[o]=j:r[o]=y(s,e);n=r}return e.delete(t),n}function D(t){let{tool_name:e,tool_input:n}=t;if(!e.startsWith("mcp__"))return[];let r=y(n??{}),o;try{o=JSON.stringify(r)}catch{o="{}"}let{value:s,truncated:a}=B(o,M),u=a?`{"tool_name":${JSON.stringify(e)},"params_raw":${JSON.stringify(s)},"truncated":true}`:`{"tool_name":${JSON.stringify(e)},"params":${s}}`;return[{type:"mcp_tool_call",category:"mcp_tool_call",data:i(u),priority:4}]}function F(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,n=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",r=String(t.tool_response??""),o="";try{let c=JSON.parse(r)?.answers;if(c&&typeof c=="object"){let g=f=>typeof f=="string"?f:Array.isArray(f)?f.filter(h=>typeof h=="string").join(" | "):"",m=n?g(c[n]):"";m?o=m:o=Object.values(c).map(g).filter(h=>h.length>0).join(" | ")}}catch{}let s=i(o),a=n?`Q: ${i(n)} \u2192 A: ${s}`:`answer: ${s}`;return[{type:"decision_question",category:"decision",data:i(a),priority:2}]}function U(t){if(t.tool_name!=="Agent")return[];if(!t.tool_response||t.tool_response.length===0)return[];let e=t.tool_response.length>500?t.tool_response.slice(0,500):t.tool_response;return[{type:"agent_finding",category:"agent-finding",data:i(e),priority:2}]}function G(t){let e=[E(t.tool_input),i(t.tool_response)].join(" ");if(e.length===0)return[];let n=new Set,r=e.match(/https?:\/\/[^\s)]+/g);if(r)for(let c of r)c=c.replace(/["'})\],;.]+$/,""),/localhost|127\.0\.0\.1/i.test(c)||n.add(c);let o=e.match(/(?<!\w)#(\d+)/g);if(o)for(let c of o)n.add(c);if(n.size===0)return[];let s,a=i(t.tool_response).match(/Fetched and indexed[^\(]*\(([\d.]+)\s*KB\)/i);if(a){let c=Number(a[1]);Number.isFinite(c)&&c>0&&(s=Math.round(c*1024))}let u={type:"external_ref",category:"external-ref",data:i(Array.from(n).join(", ")),priority:3};return s!==void 0&&(u.bytes_avoided=s),[u]}function K(t){if(t.tool_name!=="EnterWorktree")return[];let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:i(`entered worktree: ${e}`),priority:2}]}var v=/[,;,;、،]/u,J=15,q=500;function z(t){if(_.test(t)||!b.test(t)||!v.test(t))return!1;let e=[...t].length;return e>=J&&e<=q}function Q(t){let e=t.trim();return z(e)?[{type:"decision",category:"decision",data:i(t),priority:2}]:[]}var V=8,X=120,Y=new RegExp("\\p{L}+\\s+\\p{L}+","u"),Z=new RegExp("\\p{L}{6,}","u");function tt(t){let e=t.split(/[.!\n。!]/u)[0].trim();if(_.test(e)||v.test(e)||!b.test(e))return!1;let n=[...e].length;return n<V||n>X?!1:Y.test(e)||Z.test(e)}function et(t){let e=t.trim();return tt(e)?[{type:"role",category:"role",data:i(t),priority:3}]:[]}var _=/[??؟¿]/u,b=new RegExp("\\p{L}","u"),nt=60;function rt(t){if(_.test(t)||!b.test(t))return!1;let e=[...t].length;return e>0&&e<nt}function ot(t){let e=t.trim();if(!e)return[];let n;return _.test(e)?n="investigate":rt(e)&&(n="implement"),n?[{type:"intent",category:"intent",data:i(n),priority:4}]:[]}var st=/^(?:\/goal\s+|(?:goal|objective)\s*:\s*)(.+)$/is;function it(t){let e=t.trim();if(!e)return[];let n=e.match(st);if(!n)return[];let r=n[1].trim();return r?[{type:"goal",category:"goal",data:i(r),priority:4}]:[]}var at=/(?:\bError\s*:|\bException\s*:|\bTraceback\b|\bat\s+\S+\s*\([^)]*:\d+:\d+\))/u,ct=/[✓✔✅☑🎉]/u,lt=/^\s*(?:fixed|resolved)\s*:/iu;function pt(t){let e=[];return ct.test(t)||lt.test(t)?(e.push({type:"blocker_resolved",category:"blocked-on",data:i(t),priority:2}),e):(at.test(t)&&e.push({type:"blocker",category:"blocked-on",data:i(t),priority:2}),e)}function ut(t){return t.length<=1024?[]:[{type:"data",category:"data",data:i(t),priority:4}]}var l=null;function ft(t){let{tool_name:e,tool_response:n}=t,r=String(n??"");if(d(t))return l={tool:e,error:r.slice(0,200),callsSince:0},[];if(!l)return[];if(l.callsSince++,l.callsSince>10)return l=null,[];if(!!d(t))return[];let s=e===l.tool,a=l.tool==="Read"&&(e==="Edit"||e==="Write"||e==="apply_patch");if(s||a){let u={type:"error_resolved",category:"error-resolution",data:i(`Error in ${l.tool}: ${l.error} \u2192 Fixed`),priority:2};return l=null,[u]}return[]}function yt(){l=null}var p=[];function dt(t){return`${t.length}:${t.slice(0,20)}`}function gt(t){let{tool_name:e,tool_input:n}=t,r=dt(JSON.stringify(n).slice(0,200));if(p.push({tool:e,inputHash:r}),p.length>50&&p.splice(0,p.length-50),p.length<3)return[];let o=0;for(let s=p.length-1;s>=0&&(p[s].tool===e&&p[s].inputHash===r);s--)o++;return o>=3?(p.splice(p.length-o),[{type:"retry_detected",category:"iteration-loop",data:i(`${e} called ${o} times with similar input`),priority:2}]):[]}function bt(){p.length=0}var _t={run_shell_command:"Bash",read_file:"Read",read_many_files:"Read",grep_search:"Grep",search_file_content:"Grep",web_fetch:"WebFetch",write_file:"Write",edit:"Edit",glob:"Glob",todo_write:"TodoWrite",ask_user_question:"AskUserQuestion",list_directory:"LS",save_memory:"Memory",skill:"Skill",exit_plan_mode:"ExitPlanMode",agent:"Agent",bash:"Bash",view:"Read",grep:"Grep",fetch:"WebFetch",shell:"Bash",shell_command:"Bash",exec_command:"Bash","container.exec":"Bash",local_shell:"Bash",grep_files:"Grep"};function ht(t){let e=_t[t.tool_name];return!e||e===t.tool_name?t:{...t,tool_name:e}}function mt(t){try{let e=ht(t),n=[];return n.push(...A(e)),n.push(...R(e)),n.push(...T(e)),n.push(...w(e)),n.push(...L(e)),n.push(...I(e)),n.push(...N(e)),n.push(...$(e)),n.push(...O(e)),n.push(...P(e)),n.push(...D(e)),n.push(...F(e)),n.push(...H(e)),n.push(...K(e)),n.push(...U(e)),n.push(...G(e)),n.push(...ft(e)),n.push(...gt(e)),n}catch{return[]}}function St(t){try{let e=[];return e.push(...Q(t)),e.push(...et(t)),e.push(...ot(t)),e.push(...it(t)),e.push(...pt(t)),e.push(...ut(t)),e}catch{return[]}}export{mt as extractEvents,St as extractUserEvents,yt as resetErrorResolutionState,bt as resetIterationLoopState};
1
+ function i(t){return t==null?"":String(t)}function v(t){return t==null?"":typeof t=="string"?t:JSON.stringify(t)}function _(t){let e=String(t.tool_response??""),n=String(t.tool_input?.command??"");if(e.startsWith("context-mode:")||n.startsWith('echo "context-mode:')||n.startsWith("echo 'context-mode:"))return!1;let r=t.tool_output?.isError===!0||t.tool_output?.is_error===!0;return t.tool_name==="Bash"&&/exit code [1-9]|error:|Error:|FAIL|failed/i.test(e)||r}function R(t){if(!t)return[];let e=[];for(let r of t.split(/\r?\n/)){if(r.startsWith("*** Add File: ")){e.push({path:r.slice(14).trim(),type:"file_write"});continue}if(r.startsWith("*** Update File: ")){e.push({path:r.slice(17).trim(),type:"file_edit"});continue}if(r.startsWith("*** Delete File: ")){e.push({path:r.slice(17).trim(),type:"file_edit"});continue}r.startsWith("*** Move to: ")&&e.push({path:r.slice(13).trim(),type:"file_edit"})}let n=new Set;return e.filter(r=>{if(!r.path)return!1;let o=`${r.type}:${r.path}`;return n.has(o)?!1:(n.add(o),!0)})}function S(t){return/(?:^|[/\\])\.claude[/\\]plans[/\\]/.test(t)}function $(t){let{tool_name:e,tool_input:n,tool_response:r}=t,o=[];if(e==="Read"){let s=String(n.file_path??"");return(/(?:CLAUDE|AGENTS(?:\.override)?|GEMINI|QWEN|KIRO)\.md$/i.test(s)||/\/copilot-instructions\.md$/i.test(s)||/\/context-mode\.mdc$/i.test(s)||/\.claude[\\/]/i.test(s)||/[\\/]memor(?:y|ies)[\\/][^\\/]+\.md$/i.test(s))&&(o.push({type:"rule",category:"rule",data:i(s),priority:1}),r&&r.length>0&&o.push({type:"rule_content",category:"rule",data:i(r),priority:1})),o.push({type:"file_read",category:"file",data:i(s),priority:1}),o}if(e==="Edit"){let s=String(n.file_path??"");return o.push({type:"file_edit",category:"file",data:i(s),priority:1}),o}if(e==="NotebookEdit"){let s=String(n.notebook_path??"");return o.push({type:"file_edit",category:"file",data:i(s),priority:1}),o}if(e==="Write"){let s=String(n.file_path??"");return o.push({type:"file_write",category:"file",data:i(s),priority:1}),o}if(e==="apply_patch"){if(_(t))return[];let s=R(String(n.command??n.patch??""));for(let a of s)o.push({type:a.type,category:"file",data:i(a.path),priority:1});return o}if(e==="Glob"){let s=String(n.pattern??"");return o.push({type:"file_glob",category:"file",data:i(s),priority:3}),o}if(e==="Grep"){let s=String(n.pattern??""),a=String(n.path??"");return o.push({type:"file_search",category:"file",data:i(`${s} in ${a}`),priority:3}),o}return o}function I(t){if(t.tool_name!=="Bash")return[];let n=String(t.tool_input.command??"").match(/\bcd\s+("([^"]+)"|'([^']+)'|(\S+))/);if(!n)return[];let r=n[2]??n[3]??n[4]??"";return[{type:"cwd",category:"cwd",data:i(r),priority:2}]}function C(t){let{tool_response:e}=t,n=String(e??"");return _(t)?[{type:"error_tool",category:"error",data:i(n),priority:2}]:[]}var E=[{pattern:/\bgit\s+checkout\b/,operation:"branch"},{pattern:/\bgit\s+commit\b/,operation:"commit"},{pattern:/\bgit\s+merge\s+\S+/,operation:"merge"},{pattern:/\bgit\s+rebase\b/,operation:"rebase"},{pattern:/\bgit\s+stash\b/,operation:"stash"},{pattern:/\bgit\s+push\b/,operation:"push"},{pattern:/\bgit\s+pull\b/,operation:"pull"},{pattern:/\bgit\s+log\b/,operation:"log"},{pattern:/\bgit\s+diff\b/,operation:"diff"},{pattern:/\bgit\s+status\b/,operation:"status"},{pattern:/\bgit\s+branch\b/,operation:"branch"},{pattern:/\bgit\s+reset\b/,operation:"reset"},{pattern:/\bgit\s+add\b/,operation:"add"},{pattern:/\bgit\s+cherry-pick\b/,operation:"cherry-pick"},{pattern:/\bgit\s+tag\b/,operation:"tag"},{pattern:/\bgit\s+fetch\b/,operation:"fetch"},{pattern:/\bgit\s+clone\b/,operation:"clone"},{pattern:/\bgit\s+worktree\b/,operation:"worktree"}];function H(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??""),n=P(e),r;if(n&&n.operation&&(r=E.find(s=>s.operation===n.operation)),r||(r=E.find(s=>s.pattern.test(e))),!r)return[];let o=[];if(n?.scopedDir&&o.push({type:"cwd",category:"cwd",data:i(n.scopedDir),priority:2}),r.operation==="commit"){let s=N(e);if(s)return o.push({type:"git_commit",category:"git",data:i(s),priority:2}),o}return o.push({type:"git",category:"git",data:i(r.operation),priority:2}),o}function O(t){return typeof t!="string"||t.length===0?t:t==="~"?w():t.startsWith("~/")?w()+t.slice(1):t}function w(){try{return process.env.HOME||process.env.USERPROFILE||(process.env.HOMEDRIVE&&process.env.HOMEPATH?process.env.HOMEDRIVE+process.env.HOMEPATH:"")||"~"}catch{return"~"}}function P(t){let e=x(t),n=0;for(;n<e.length&&M(e[n]);)n++;for(;n<e.length&&e[n]!=="git"&&!e[n].endsWith("/git")&&L(e[n]);)n++;if(n>=e.length||e[n]!=="git"&&!e[n].endsWith("/git"))return null;n++;let r=null,o=null;for(;n<e.length;){let s=e[n];if(s==="-C"||s==="--directory"){r=e[n+1]??null,n+=2;continue}if(s.startsWith("--directory=")){r=s.slice(12),n++;continue}if(s.length>0&&s[0]==="-"){n++;continue}o=s;break}return r&&(r=O(r)),{scopedDir:r,operation:o}}function M(t){if(t.length===0)return!1;let e=!1;for(let n=0;n<t.length;n++){let r=t.charCodeAt(n);if(n===0){if(!(r>=65&&r<=90||r===95))return!1}else if(r===61){e=!0;break}else if(!(r>=65&&r<=90||r>=48&&r<=57||r===95))return!1}return e}function L(t){switch(t){case"sudo":case"doas":case"env":case"exec":case"time":return!0;default:return!1}}function x(t){let e=[],n=t.length,r=0;for(;r<n;){for(;r<n&&(t[r]===" "||t[r]===" ");)r++;if(r>=n)break;let o="";for(;r<n&&t[r]!==" "&&t[r]!==" ";){let s=t[r];if(s==='"'||s==="'"){let a=s;for(r++;r<n&&t[r]!==a;)t[r]==="\\"&&r+1<n?(o+=t[r+1],r+=2):(o+=t[r],r++);r<n&&r++}else s==="\\"&&r+1<n?(o+=t[r+1],r+=2):(o+=s,r++)}e.push(o)}return e}function N(t){let e=x(t),n="--message=";for(let r=0;r<e.length;r++){let o=e[r];if(o.length>n.length&&o.startsWith(n)){let s=o.slice(n.length);return s.length>0?s:null}if(o==="--message"){let s=e[r+1];return s&&s.length>0?s:null}if(o.length>=2&&o[0]==="-"&&o[1]!=="-"&&o[o.length-1]==="m"&&j(o,1)){let s=e[r+1];return s&&s.length>0?s:null}}return null}function j(t,e){if(e>=t.length)return!1;for(let n=e;n<t.length;n++){let r=t.charCodeAt(n);if(r<97||r>122)return!1}return!0}function W(t){return new Set(["TodoWrite","TaskCreate","TaskUpdate"]).has(t.tool_name)?[{type:t.tool_name==="TaskUpdate"?"task_update":t.tool_name==="TaskCreate"?"task_create":"task",category:"task",data:i(JSON.stringify(t.tool_input)),priority:1}]:[]}function D(t){let e=2166136261;for(let n=0;n<t.length;n++)e^=t.charCodeAt(n),e=Math.imul(e,16777619);return(e>>>0).toString(16).padStart(8,"0")}function B(t){let e=t.tool_input.plan;if(typeof e=="string"&&e.length>0)return e;let n=t.tool_response;if(typeof n=="string"&&n.length>0)try{let r=JSON.parse(n);if(r&&typeof r=="object"&&typeof r.plan=="string")return r.plan}catch{}return null}function F(t){if(t.tool_name==="EnterPlanMode")return[{type:"plan_enter",category:"plan",data:"entered plan mode",priority:2}];if(t.tool_name==="ExitPlanMode"){let e=[],n=t.tool_input.allowedPrompts,r=Array.isArray(n)&&n.length>0?`exited plan mode (allowed: ${v(n.map(a=>typeof a=="object"&&a!==null&&"prompt"in a?String(a.prompt):String(a)).join(", "))})`:"exited plan mode",o=B(t);typeof o=="string"&&o.length>0&&(r+=` plan_bytes:${o.length} plan_hash:${D(o)}`),e.push({type:"plan_exit",category:"plan",data:i(r),priority:2});let s=String(t.tool_response??"").toLowerCase();return s.includes("approved")||s.includes("approve")?e.push({type:"plan_approved",category:"plan",data:"plan approved by user",priority:1}):(s.includes("rejected")||s.includes("decline")||s.includes("denied"))&&e.push({type:"plan_rejected",category:"plan",data:i(`plan rejected: ${t.tool_response??""}`),priority:2}),e}if(t.tool_name==="Write"||t.tool_name==="Edit"){let e=String(t.tool_input.file_path??"");if(S(e))return[{type:"plan_file_write",category:"plan",data:i(`plan file: ${e.split(/[/\\]/).pop()??e}`),priority:2}]}return t.tool_name==="apply_patch"?_(t)?[]:R(String(t.tool_input.command??t.tool_input.patch??"")).filter(n=>S(n.path)).map(n=>({type:"plan_file_write",category:"plan",data:i(`plan file: ${n.path.split(/[/\\]/).pop()??n.path}`),priority:2})):[]}var U=[/\bsource\s+\S*activate\b/,/\bexport\s+\w+=/,/\bnvm\s+use\b/,/\bpyenv\s+(shell|local|global)\b/,/\bconda\s+activate\b/,/\brbenv\s+(shell|local|global)\b/,/\bnpm\s+install\b/,/\bnpm\s+ci\b/,/\bpip\s+install\b/,/\bbun\s+install\b/,/\byarn\s+(add|install)\b/,/\bpnpm\s+(add|install)\b/,/\bcargo\s+(install|add)\b/,/\bgo\s+(install|get)\b/,/\brustup\b/,/\basdf\b/,/\bvolta\b/,/\bdeno\s+install\b/];function G(t){if(t.tool_name!=="Bash")return[];let e=String(t.tool_input.command??"");if(!U.some(o=>o.test(e)))return[];let r=e.replace(/\bexport\s+(\w+)=\S*/g,"export $1=***");return[{type:"env",category:"env",data:i(r),priority:2}]}function K(t){if(t.tool_name!=="Skill")return[];let e=String(t.tool_input.skill??"");return[{type:"skill",category:"skill",data:i(e),priority:2}]}function J(t){if(!t.tool_response?.includes("Error")&&!t.tool_output?.isError)return[];let e=String(t.tool_response||""),n=[/not supported/i,/cannot/i,/does not support/i,/FAIL/i,/refused/i,/permission denied/i,/incompatible/i];for(let r of n){let o=e.match(r);if(o){let s=e.toLowerCase().indexOf(o[0].toLowerCase()),a=e.slice(Math.max(0,s-50),Math.min(e.length,s+200)).trim();return[{type:"constraint_discovered",category:"constraint",data:i(a),priority:2}]}}return[]}function q(t){if(t.tool_name!=="Agent")return[];let e=i(String(t.tool_input.prompt??t.tool_input.description??"")),n=t.tool_response?i(String(t.tool_response)):"",r=n.length>0;return[{type:r?"subagent_completed":"subagent_launched",category:"subagent",data:i(r?`[completed] ${e} \u2192 ${n}`:`[launched] ${e}`),priority:r?2:3}]}function z(t){let{tool_name:e,tool_input:n,tool_response:r}=t;if(!e.startsWith("mcp__"))return[];let o=e.split("__"),s=o[o.length-1]||e,a=Object.values(n).find(l=>typeof l=="string"),p=a?`: ${i(String(a))}`:"",c=r&&r.length>0?`
2
+ response: ${i(r)}`:"";return[{type:"mcp",category:"mcp",data:i(`${s}${p}${c}`),priority:3}]}var V=2048;function Q(t,e){if(Buffer.byteLength(t,"utf8")<=e)return{value:t,truncated:!1};let n=Buffer.from(t,"utf8"),r=e;for(;r>0&&(n[r]&192)===128;)r--;return{value:n.subarray(0,r).toString("utf8"),truncated:!0}}var X=/(authorization|auth_token|access_token|refresh_token|bearer|token|secret|password|passwd|pwd|api[-_]?key|apikey|cookie|set-cookie|signature|private[-_]?key|client[-_]?secret|x[-_]?api[-_]?key)/i,Y="[REDACTED]";function b(t,e=new WeakSet){if(t==null||typeof t!="object")return t;if(e.has(t))return"[CIRCULAR]";e.add(t);let n;if(Array.isArray(t))n=t.map(r=>b(r,e));else{let r={};for(let[o,s]of Object.entries(t))X.test(o)?r[o]=Y:r[o]=b(s,e);n=r}return e.delete(t),n}function Z(t){let{tool_name:e,tool_input:n}=t;if(!e.startsWith("mcp__"))return[];let r=b(n??{}),o;try{o=JSON.stringify(r)}catch{o="{}"}let{value:s,truncated:a}=Q(o,V),p=a?`{"tool_name":${JSON.stringify(e)},"params_raw":${JSON.stringify(s)},"truncated":true}`:`{"tool_name":${JSON.stringify(e)},"params":${s}}`;return[{type:"mcp_tool_call",category:"mcp_tool_call",data:i(p),priority:4}]}function tt(t){if(t.tool_name!=="AskUserQuestion")return[];let e=t.tool_input.questions,n=Array.isArray(e)&&e.length>0?String(e[0].question??""):"",r=String(t.tool_response??""),o="";try{let c=JSON.parse(r)?.answers;if(c&&typeof c=="object"){let l=g=>typeof g=="string"?g:Array.isArray(g)?g.filter(h=>typeof h=="string").join(" | "):"",u=n?l(c[n]):"";u?o=u:o=Object.values(c).map(l).filter(h=>h.length>0).join(" | ")}}catch{}let s=i(o),a=n?`Q: ${i(n)} \u2192 A: ${s}`:`answer: ${s}`;return[{type:"decision_question",category:"decision",data:i(a),priority:2}]}function et(t){if(t.tool_name!=="Agent")return[];if(!t.tool_response||t.tool_response.length===0)return[];let e=t.tool_response.length>500?t.tool_response.slice(0,500):t.tool_response;return[{type:"agent_finding",category:"agent-finding",data:i(e),priority:2}]}function nt(t){let e=[v(t.tool_input),i(t.tool_response)].join(" ");if(e.length===0)return[];let n=new Set,r=e.match(/https?:\/\/[^\s)]+/g);if(r)for(let c of r)c=c.replace(/["'})\],;.]+$/,""),/localhost|127\.0\.0\.1/i.test(c)||n.add(c);let o=e.match(/(?<!\w)#(\d+)/g);if(o)for(let c of o)n.add(c);if(n.size===0)return[];let s,a=i(t.tool_response).match(/Fetched and indexed[^\(]*\(([\d.]+)\s*KB\)/i);if(a){let c=Number(a[1]);Number.isFinite(c)&&c>0&&(s=Math.round(c*1024))}let p={type:"external_ref",category:"external-ref",data:i(Array.from(n).join(", ")),priority:3};return s!==void 0&&(p.bytes_avoided=s),[p]}function rt(t){if(t.tool_name==="EnterWorktree"){let e=String(t.tool_input.name??"unnamed");return[{type:"worktree",category:"env",data:i(`entered worktree: ${e}`),priority:2}]}if(t.tool_name==="ExitWorktree"){let e=!!t.tool_input.discard_changes;return[{type:"worktree_exit",category:"env",data:i(`exited worktree (discard_changes:${e})`),priority:2}]}return[]}function ot(t){if(typeof t!="string"||t.length===0)return null;let e=t.indexOf("://");if(e<0)return null;let n=e+3;if(n>=t.length)return null;let r=t.length;for(let s=n;s<t.length;s++){let a=t.charCodeAt(s);if(a===47||a===63||a===35){r=s;break}}let o=t.slice(n,r);return o.length>0?o:null}function st(t){if(t.tool_name!=="WebFetch")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=[];if(typeof r.code=="number"&&o.push(`code:${r.code}`),typeof r.bytes=="number"&&o.push(`bytes:${r.bytes}`),typeof r.durationMs=="number"&&o.push(`durMs:${r.durationMs}`),typeof r.url=="string"){let s=ot(r.url);s&&o.push(`host:${s}`)}return o.length===0?[]:[{type:"webfetch_metadata",category:"data",data:i(o.join(" ")),priority:3}]}function it(t){if(t.tool_name!=="Bash")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n;if(!(typeof r.interrupted=="boolean"||typeof r.stderr=="string"||typeof r.returnCodeInterpretation=="string"))return[];let s=[];return typeof r.interrupted=="boolean"&&s.push(`interrupted:${r.interrupted}`),typeof r.returnCodeInterpretation=="string"&&s.push(`rcInterp:${r.returnCodeInterpretation.slice(0,80)}`),typeof r.stderr=="string"&&s.push(`stderrBytes:${r.stderr.length}`),[{type:"bash_outcome",category:"data",data:i(s.join(" ")),priority:3}]}function at(t){if(t.tool_name!=="Read")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=r.type;if(o!=="text"&&o!=="image")return[];let s=[`type:${o}`];if(o==="text")typeof r.numLines=="number"&&s.push(`lines:${r.numLines}`),typeof r.totalLines=="number"&&s.push(`totalLines:${r.totalLines}`),typeof r.startLine=="number"&&s.push(`start:${r.startLine}`);else{typeof r.originalSize=="number"&&s.push(`origSize:${r.originalSize}`);let a=r.dimensions;if(a&&typeof a=="object"){let p=a;typeof p.width=="number"&&typeof p.height=="number"&&s.push(`dims:${p.width}x${p.height}`)}}return[{type:"file_read_metadata",category:"data",data:i(s.join(" ")),priority:3}]}var y={"claude-opus-4-8":{input:5,output:25,cache_write:6.25,cache_read:.5},"claude-opus-4-7":{input:5,output:25,cache_write:6.25,cache_read:.5},"claude-sonnet-4-6":{input:3,output:15,cache_write:3.75,cache_read:.3},"claude-haiku-4-5":{input:1,output:5,cache_write:1.25,cache_read:.1},default:{input:3,output:15,cache_write:3.75,cache_read:.3}};function ct(t,e){let n=[t.tool_input?.model,t.model,e.model],r=Object.keys(y).filter(o=>o!=="default");for(let o of n)if(!(typeof o!="string"||o.length===0)){if(o in y)return o;for(let s of r)if(o.startsWith(s))return s}return"default"}function pt(t,e,n,r,o){let s=y[t]??y.default;return(e*s.input+n*s.output+r*s.cache_write+o*s.cache_read)/1e6}function lt(t){if(t.tool_name!=="Task")return[];let e=t.tool_response;if(typeof e!="string"||e.length===0)return[];let n;try{n=JSON.parse(e)}catch{return[]}if(!n||typeof n!="object")return[];let r=n,o=r.usage&&typeof r.usage=="object"?r.usage:{};if(!(typeof r.totalTokens=="number"||typeof r.totalDurationMs=="number"||typeof o.input_tokens=="number"||typeof o.output_tokens=="number"||typeof o.service_tier=="string"))return[];let a=[];typeof r.totalTokens=="number"&&a.push(`totalTokens:${r.totalTokens}`),typeof r.totalDurationMs=="number"&&a.push(`totalDurMs:${r.totalDurationMs}`),typeof o.input_tokens=="number"&&a.push(`tokens_in:${o.input_tokens}`),typeof o.output_tokens=="number"&&a.push(`tokens_out:${o.output_tokens}`),typeof o.cache_creation_input_tokens=="number"&&a.push(`cache_create:${o.cache_creation_input_tokens}`),typeof o.cache_read_input_tokens=="number"&&a.push(`cache_read:${o.cache_read_input_tokens}`),typeof o.service_tier=="string"&&a.push(`tier:${o.service_tier.slice(0,32)}`);let p=typeof o.input_tokens=="number"?o.input_tokens:0,c=typeof o.output_tokens=="number"?o.output_tokens:0,l=typeof o.cache_creation_input_tokens=="number"?o.cache_creation_input_tokens:0,u=typeof o.cache_read_input_tokens=="number"?o.cache_read_input_tokens:0;if(p>0||c>0||l>0||u>0){let h=ct(t,r),A=pt(h,p,c,l,u);a.push(`cost_usd:${A.toFixed(6).replace(/0+$/,"").replace(/\.$/,".0")}`)}return[{type:"agent_usage",category:"cost",data:i(a.join(" ")),priority:2}]}var T=/[,;,;、،]/u,ut=15,ft=500;function dt(t){if(m.test(t)||!k.test(t)||!T.test(t))return!1;let e=[...t].length;return e>=ut&&e<=ft}function gt(t){let e=t.trim();return dt(e)?[{type:"decision",category:"decision",data:i(t),priority:2}]:[]}var ht=8,_t=120,yt=new RegExp("\\p{L}+\\s+\\p{L}+","u"),mt=new RegExp("\\p{L}{6,}","u");function bt(t){let e=t.split(/[.!\n。!]/u)[0].trim();if(m.test(e)||T.test(e)||!k.test(e))return!1;let n=[...e].length;return n<ht||n>_t?!1:yt.test(e)||mt.test(e)}function kt(t){let e=t.trim();return bt(e)?[{type:"role",category:"role",data:i(t),priority:3}]:[]}var m=/[??؟¿]/u,k=new RegExp("\\p{L}","u"),St=60;function Et(t){if(m.test(t)||!k.test(t))return!1;let e=[...t].length;return e>0&&e<St}function wt(t){let e=t.trim();if(!e)return[];let n;return m.test(e)?n="investigate":Et(e)&&(n="implement"),n?[{type:"intent",category:"intent",data:i(n),priority:4}]:[]}var vt=/^(?:\/goal\s+|(?:goal|objective)\s*:\s*)(.+)$/is;function Rt(t){let e=t.trim();if(!e)return[];let n=e.match(vt);if(!n)return[];let r=n[1].trim();return r?[{type:"goal",category:"goal",data:i(r),priority:4}]:[]}var xt=/(?:\bError\s*:|\bException\s*:|\bTraceback\b|\bat\s+\S+\s*\([^)]*:\d+:\d+\))/u,Tt=/[✓✔✅☑🎉]/u,At=/^\s*(?:fixed|resolved)\s*:/iu;function $t(t){let e=[];return Tt.test(t)||At.test(t)?(e.push({type:"blocker_resolved",category:"blocked-on",data:i(t),priority:2}),e):(xt.test(t)&&e.push({type:"blocker",category:"blocked-on",data:i(t),priority:2}),e)}function It(t){return t.length<=1024?[]:[{type:"data",category:"data",data:i(t),priority:4}]}var f=null;function Ct(t){let{tool_name:e,tool_response:n}=t,r=String(n??"");if(_(t))return f={tool:e,error:r.slice(0,200),callsSince:0},[];if(!f)return[];if(f.callsSince++,f.callsSince>10)return f=null,[];if(!!_(t))return[];let s=e===f.tool,a=f.tool==="Read"&&(e==="Edit"||e==="Write"||e==="apply_patch");if(s||a){let p={type:"error_resolved",category:"error-resolution",data:i(`Error in ${f.tool}: ${f.error} \u2192 Fixed`),priority:2};return f=null,[p]}return[]}function Wt(){f=null}var d=[];function Ht(t){return`${t.length}:${t.slice(0,20)}`}function Ot(t){let{tool_name:e,tool_input:n}=t,r=Ht(JSON.stringify(n).slice(0,200));if(d.push({tool:e,inputHash:r}),d.length>50&&d.splice(0,d.length-50),d.length<3)return[];let o=0;for(let s=d.length-1;s>=0&&(d[s].tool===e&&d[s].inputHash===r);s--)o++;return o>=3?(d.splice(d.length-o),[{type:"retry_detected",category:"iteration-loop",data:i(`${e} called ${o} times with similar input`),priority:2}]):[]}function Dt(){d.length=0}var Pt={run_shell_command:"Bash",read_file:"Read",read_many_files:"Read",grep_search:"Grep",search_file_content:"Grep",web_fetch:"WebFetch",write_file:"Write",edit:"Edit",glob:"Glob",todo_write:"TodoWrite",ask_user_question:"AskUserQuestion",list_directory:"LS",save_memory:"Memory",skill:"Skill",exit_plan_mode:"ExitPlanMode",agent:"Agent",bash:"Bash",view:"Read",grep:"Grep",fetch:"WebFetch",shell:"Bash",shell_command:"Bash",exec_command:"Bash","container.exec":"Bash",local_shell:"Bash",grep_files:"Grep",run_command:"Bash",view_file:"Read",read_url_content:"WebFetch",list_dir:"LS",search_web:"WebSearch"};function Mt(t){let e=Pt[t.tool_name];return!e||e===t.tool_name?t:{...t,tool_name:e}}function Bt(t){try{let e=Mt(t),n=[];return n.push(...$(e)),n.push(...I(e)),n.push(...C(e)),n.push(...H(e)),n.push(...G(e)),n.push(...W(e)),n.push(...F(e)),n.push(...K(e)),n.push(...q(e)),n.push(...z(e)),n.push(...Z(e)),n.push(...tt(e)),n.push(...J(e)),n.push(...rt(e)),n.push(...st(e)),n.push(...it(e)),n.push(...at(e)),n.push(...lt(e)),n.push(...et(e)),n.push(...nt(e)),n.push(...Ct(e)),n.push(...Ot(e)),n}catch{return[]}}function Ft(t){try{let e=[];return e.push(...jt(t)),e.push(...gt(t)),e.push(...kt(t)),e.push(...wt(t)),e.push(...Rt(t)),e.push(...$t(t)),e.push(...It(t)),e}catch{return[]}}function Ut(t){if(!t||typeof t!="object")return[];let e=t,n=[],r=e.mcp_servers,o=null;return r&&typeof r=="object"&&!Array.isArray(r)&&(o=Object.keys(r),n.push(`mcp_count:${o.length}`),o.length>0&&n.push(`mcp_servers:${o.slice(0,8).join(",")}`)),typeof e.model=="string"&&n.push(`model:${e.model.slice(0,64)}`),typeof e.permission_mode=="string"&&n.push(`permission_mode:${e.permission_mode.slice(0,32)}`),n.length===0?[]:[{type:"session_settings_snapshot",category:"env",data:i(n.join(" ")),priority:2}]}var Lt=["Latin","Cyrillic","Arabic","Han","Hangul","Hiragana","Katakana","Devanagari","Hebrew","Thai","Greek"],Nt={prompt_length:0,prompt_word_count:0,prompt_uppercase_ratio:0,prompt_file_ref_count:0,prompt_path_ref_count:0,prompt_script_primary:null,prompt_script_count:0,prompt_question_glyph_count:0,prompt_code_block_count:0,prompt_url_count:0,prompt_word_tokens:[]};function Gt(t){if(typeof t!="string"||t.length===0)return{...Nt,prompt_word_tokens:[]};let e=t.match(new RegExp("\\p{L}+","gu"))??[],n=(t.match(new RegExp("\\p{Lu}","gu"))??[]).length,r=e.join("").length,o=(t.match(/```/g)??[]).length,s={};for(let l of Lt){let u=new RegExp(`\\p{Script=${l}}`,"gu"),g=(t.match(u)??[]).length;g>0&&(s[l]=g)}let a=Object.entries(s).sort((l,u)=>u[1]-l[1])[0]?.[0]??null,p=new Set,c=[];for(let l of e){if(l.length<3)continue;let u=l.toLowerCase();p.has(u)||(p.add(u),c.push(u))}return{prompt_length:t.length,prompt_word_count:e.length,prompt_uppercase_ratio:r===0?0:n/r,prompt_file_ref_count:(t.match(/(\w+\/)+\w+\.\w+/g)??[]).length,prompt_path_ref_count:(t.match(/\.{0,2}\/[\w\/.-]+/g)??[]).length,prompt_script_primary:a,prompt_script_count:Object.keys(s).length,prompt_question_glyph_count:(t.match(/[??؟]/gu)??[]).length,prompt_code_block_count:Math.floor(o/2),prompt_url_count:(t.match(/https?:\/\/[^\s]+/gu)??[]).length,prompt_word_tokens:c}}function jt(t){if(typeof t!="string"||t.length===0)return[];let e=0;for(;e<t.length;){let o=t.charCodeAt(e);if(o!==32&&o!==9)break;e++}if(e+5>t.length)return[];if(t.slice(e,e+5)!=="/plan")return[];if(e+5<t.length){let o=t.charCodeAt(e+5);if(!(o===32||o===9||o===10||o===13))return[]}let n=t.slice(e+5).trim(),r=n.length>0?`plan via /plan slash: ${n.slice(0,120)}`:"plan via /plan slash";return[{type:"plan_enter",category:"plan",data:i(r),priority:2}]}export{Bt as extractEvents,Ut as extractSessionSettings,Ft as extractUserEvents,Gt as extractUserPromptFeatures,Wt as resetErrorResolutionState,Dt as resetIterationLoopState};
@@ -143,6 +143,19 @@ export const GEMINI_OPTS = {
143
143
  sessionIdEnv: undefined,
144
144
  };
145
145
 
146
+ /**
147
+ * Antigravity CLI (`agy`) platform options. Shares the Gemini-family session
148
+ * root (~/.gemini/context-mode/sessions). agy supplies the conversation id and
149
+ * workspace path inside the hook payload (mapped to session_id/cwd before these
150
+ * opts are consulted), so no env-var fallbacks are needed.
151
+ */
152
+ export const ANTIGRAVITY_CLI_OPTS = {
153
+ configDir: ".gemini",
154
+ configDirEnv: undefined,
155
+ projectDirEnv: undefined,
156
+ sessionIdEnv: undefined,
157
+ };
158
+
146
159
  /** VS Code Copilot platform options. */
147
160
  export const VSCODE_OPTS = {
148
161
  configDir: ".vscode",
@@ -151,6 +164,14 @@ export const VSCODE_OPTS = {
151
164
  sessionIdEnv: undefined,
152
165
  };
153
166
 
167
+ /** GitHub Copilot CLI platform options. */
168
+ export const COPILOT_OPTS = {
169
+ configDir: ".copilot",
170
+ configDirEnv: "COPILOT_HOME",
171
+ projectDirEnv: undefined,
172
+ sessionIdEnv: undefined,
173
+ };
174
+
154
175
  /** Cursor platform options. */
155
176
  export const CURSOR_OPTS = {
156
177
  configDir: ".cursor",
@@ -195,11 +195,14 @@ function enrichEventForPlatform(event, attribution) {
195
195
  if (event?.type === "blocker") enriched.blocker_status = "open";
196
196
  else if (event?.type === "blocker_resolved") enriched.blocker_status = "resolved";
197
197
 
198
- // Git events: surface commit message + mark has_commit at the event level
199
- // (rollup-level has_commit comes from the session-wide stamp; both win
200
- // when set `{...enriched, ...rollup}` order keeps rollup authoritative
201
- // for non-git events while git events stay marked).
202
- if (event?.category === "git" && dataStr.length > 0) {
198
+ // v1.0.161 (Bug 2): gate per-event commit_message + has_commit on
199
+ // type='git_commit', NOT category='git'. Non-commit git operations
200
+ // (push/diff/status) used to inflate commit_message with the operation
201
+ // name and falsely raise has_commit on rows that should have remained
202
+ // commit-neutral. The rollup spread now stamps both fields symmetrically
203
+ // from the session's latest actual commit — see SessionDB.getSessionRollup
204
+ // and src/session/extract.ts:extractGit type discriminator.
205
+ if (event?.type === "git_commit" && dataStr.length > 0) {
203
206
  enriched.commit_message = dataStr.slice(0, 500);
204
207
  enriched.has_commit = 1;
205
208
  }
@@ -42,7 +42,7 @@ await runHook(async () => {
42
42
  const { createSessionLoaders, attributeAndInsertEvents } = await import("./session-loaders.mjs");
43
43
  const { join, dirname } = await import("node:path");
44
44
  const { fileURLToPath } = await import("node:url");
45
- const { readFileSync, unlinkSync, readdirSync, rmSync, lstatSync } = await import("node:fs");
45
+ const { readFileSync, unlinkSync, readdirSync, rmSync, lstatSync, realpathSync, symlinkSync } = await import("node:fs");
46
46
 
47
47
  const detectedPlatform = detectPlatformFromEnv();
48
48
  const toolNamer = createToolNamer(detectedPlatform);
@@ -50,7 +50,7 @@ await runHook(async () => {
50
50
 
51
51
  // Resolve absolute path for imports (fileURLToPath for Windows compat)
52
52
  const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
53
- const { loadSessionDB, loadProjectAttribution } = createSessionLoaders(HOOK_DIR);
53
+ const { loadSessionDB, loadProjectAttribution, loadExtract } = createSessionLoaders(HOOK_DIR);
54
54
 
55
55
  // Emit a `session_start` canonical event at the boundary of each session
56
56
  // lifecycle transition (startup / resume / compact). The platform's insight
@@ -71,10 +71,24 @@ await runHook(async () => {
71
71
  }),
72
72
  priority: 1,
73
73
  };
74
+
75
+ // PRD #4 — emit session_settings_snapshot alongside lifecycle when
76
+ // the SessionStart envelope carries any of mcp_servers / model /
77
+ // permission_mode. Best-effort: missing fields → no snapshot.
78
+ const eventsToEmit = [lifecycleEvent];
79
+ try {
80
+ const extract = await loadExtract();
81
+ if (typeof extract.extractSessionSettings === "function") {
82
+ eventsToEmit.push(...extract.extractSessionSettings(input));
83
+ }
84
+ } catch {
85
+ // settings snapshot is opportunistic — never block lifecycle on it
86
+ }
87
+
74
88
  attributeAndInsertEvents(
75
89
  db,
76
90
  sessionId,
77
- [lifecycleEvent],
91
+ eventsToEmit,
78
92
  input,
79
93
  projectDir,
80
94
  "SessionStart",
@@ -384,12 +398,44 @@ await runHook(async () => {
384
398
  const now = Date.now();
385
399
  for (const d of readdirSync(cacheParent)) {
386
400
  if (d === myDir) continue;
401
+ const oldDir = join(cacheParent, d);
387
402
  try {
388
- const st = lstatSync(join(cacheParent, d));
389
- if (now - st.mtimeMs > ONE_HOUR) {
390
- rmSync(join(cacheParent, d), { recursive: true, force: true });
403
+ const st = lstatSync(oldDir);
404
+ let danglingBreadcrumb = false;
405
+ if (st.isSymbolicLink()) {
406
+ try {
407
+ realpathSync(oldDir);
408
+ } catch {
409
+ danglingBreadcrumb = true;
410
+ }
411
+ }
412
+ if (danglingBreadcrumb || now - st.mtimeMs > ONE_HOUR) {
413
+ rmSync(oldDir, { recursive: true, force: true });
414
+ // Leave a breadcrumb symlink (junction on Windows) in the
415
+ // removed version's place so sessions that loaded hooks
416
+ // from it before an auto-update keep resolving their
417
+ // scripts instead of erroring on every hook call until
418
+ // restart (#814, #807). Also fires when the entry was
419
+ // itself a stale breadcrumb, re-pointing it at the live
420
+ // root (a chain of updates would otherwise leave links
421
+ // targeting intermediate versions that no longer exist).
422
+ // The fresh mtime makes #644's lstat age gate protect it
423
+ // for the next hour, and the next sweep refreshes it
424
+ // again. Same pattern as healCacheMidSession (server.ts)
425
+ // and postinstall.mjs.
426
+ try {
427
+ symlinkSync(pluginRoot, oldDir, process.platform === "win32" ? "junction" : undefined);
428
+ } catch { /* best effort — plain delete is the pre-#814 behaviour */ }
391
429
  }
392
- } catch { /* skip */ }
430
+ } catch {
431
+ // On Windows, a dangling junction can fail before we can read
432
+ // its own mtime. Treat that as a stale breadcrumb and try to
433
+ // repoint it at the live root; failures remain best-effort.
434
+ try {
435
+ rmSync(oldDir, { recursive: true, force: true });
436
+ symlinkSync(pluginRoot, oldDir, process.platform === "win32" ? "junction" : undefined);
437
+ } catch { /* skip */ }
438
+ }
393
439
  }
394
440
  }
395
441
  }
package/hooks/stop.mjs ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import "./suppress-stderr.mjs";
3
+ import "./ensure-deps.mjs";
4
+ /**
5
+ * Claude Code Stop hook — record turn-end state for continuity.
6
+ *
7
+ * Stop fires when Claude is about to finish the current assistant turn. This is
8
+ * not a true session shutdown event, so record a turn_end marker and never ask
9
+ * Claude to continue.
10
+ */
11
+
12
+ import { readStdin, parseStdin, getSessionId, getSessionDBPath, getInputProjectDir } from "./session-helpers.mjs";
13
+ import { createSessionLoaders } from "./session-loaders.mjs";
14
+ import { dirname } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
18
+ const { loadSessionDB } = createSessionLoaders(HOOK_DIR);
19
+
20
+ try {
21
+ const raw = await readStdin();
22
+ const input = parseStdin(raw);
23
+ const projectDir = getInputProjectDir(input);
24
+
25
+ const { SessionDB } = await loadSessionDB();
26
+ const dbPath = getSessionDBPath(undefined, projectDir);
27
+ const db = new SessionDB({ dbPath });
28
+ const sessionId = getSessionId(input);
29
+
30
+ db.ensureSession(sessionId, projectDir);
31
+ const payload = {
32
+ stop_hook_active: input.stop_hook_active ?? false,
33
+ last_assistant_message: typeof input.last_assistant_message === "string"
34
+ ? input.last_assistant_message.slice(0, 2000)
35
+ : null,
36
+ };
37
+ db.insertEvent(sessionId, {
38
+ type: "turn_end",
39
+ category: "session",
40
+ data: JSON.stringify(payload),
41
+ priority: 1,
42
+ }, "Stop");
43
+
44
+ db.close();
45
+ } catch {
46
+ // Claude Code hooks must not block the session.
47
+ }
48
+
49
+ process.stdout.write("{}\n");
@@ -44,7 +44,7 @@ await runHook(async () => {
44
44
 
45
45
  if (trimmed.length > 0 && !isSystemMessage) {
46
46
  const { SessionDB } = await loadSessionDB();
47
- const { extractUserEvents } = await loadExtract();
47
+ const { extractUserEvents, extractUserPromptFeatures } = await loadExtract();
48
48
  const { resolveProjectAttributions } = await loadProjectAttribution();
49
49
  const dbPath = getSessionDBPath();
50
50
  const db = new SessionDB({ dbPath });
@@ -52,12 +52,19 @@ await runHook(async () => {
52
52
 
53
53
  db.ensureSession(sessionId, projectDir);
54
54
 
55
- // 1. Always save the raw prompt
55
+ // 1. Always save the raw prompt with F1 §2 features attached.
56
+ // Features attach to the existing user_prompt event payload alongside
57
+ // the raw `data` field (do NOT remove `data`). Platform Zod envelope
58
+ // is forward-compatible; new fields persist as typed columns.
59
+ const promptFeatures = typeof extractUserPromptFeatures === "function"
60
+ ? extractUserPromptFeatures(trimmed)
61
+ : {};
56
62
  const promptEvent = {
57
63
  type: "user_prompt",
58
64
  category: "user-prompt",
59
65
  data: prompt,
60
66
  priority: 1,
67
+ ...promptFeatures,
61
68
  };
62
69
  const promptAttributions = attributeAndInsertEvents(
63
70
  db, sessionId, [promptEvent], input, projectDir, "UserPromptSubmit", resolveProjectAttributions,
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.161",
6
+ "version": "1.0.163",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.161",
3
+ "version": "1.0.163",
4
4
  "type": "module",
5
5
  "description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",
@@ -62,14 +62,6 @@
62
62
  "build",
63
63
  "hooks",
64
64
  "configs",
65
- "insight/server.mjs",
66
- "insight/package.json",
67
- "insight/tsconfig.json",
68
- "insight/vite.config.ts",
69
- "insight/tailwind.config.ts",
70
- "insight/postcss.config.js",
71
- "insight/index.html",
72
- "insight/src",
73
65
  "server.bundle.mjs",
74
66
  "cli.bundle.mjs",
75
67
  "bin",
@@ -80,6 +72,7 @@
80
72
  "openclaw.plugin.json",
81
73
  "start.mjs",
82
74
  "scripts/postinstall.mjs",
75
+ "scripts/install-antigravity-cli-plugin.mjs",
83
76
  "scripts/heal-better-sqlite3.mjs",
84
77
  "scripts/heal-installed-plugins.mjs",
85
78
  "scripts/plugin-cache-integrity.mjs",
@@ -92,7 +85,7 @@
92
85
  "assert-asymmetric-drift": "node scripts/assert-asymmetric-drift.mjs",
93
86
  "bundle": "esbuild src/server.ts --bundle --platform=node --target=node18 --format=esm --outfile=server.bundle.mjs --external:better-sqlite3 --external:turndown --external:turndown-plugin-gfm --external:@mixmark-io/domino --minify && esbuild src/cli.ts --bundle --platform=node --target=node18 --format=esm --outfile=cli.bundle.mjs --external:better-sqlite3 --minify && esbuild src/session/extract.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-extract.bundle.mjs --minify && esbuild src/session/snapshot.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-snapshot.bundle.mjs --minify && esbuild src/session/db.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/session-db.bundle.mjs --external:better-sqlite3 --minify && esbuild src/security.ts --bundle --platform=node --target=node18 --format=esm --outfile=hooks/security.bundle.mjs --minify",
94
87
  "version-sync": "node scripts/version-sync.mjs",
95
- "version": "node scripts/version-sync.mjs && git add package.json .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json .codex-plugin/plugin.json .openclaw-plugin/openclaw.plugin.json .openclaw-plugin/package.json openclaw.plugin.json .pi/extensions/context-mode/package.json",
88
+ "version": "node scripts/version-sync.mjs && git add package.json .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json .codex-plugin/plugin.json .openclaw-plugin/openclaw.plugin.json .openclaw-plugin/package.json openclaw.plugin.json .pi/extensions/context-mode/package.json configs/antigravity-cli/plugin.json configs/copilot-cli/.github/plugin/plugin.json",
96
89
  "prepublishOnly": "npm run build",
97
90
  "dev": "npx tsx src/server.ts",
98
91
  "setup": "npx tsx src/cli.ts setup",
@@ -106,6 +99,7 @@
106
99
  "test:compare": "npx tsx tests/context-comparison.ts",
107
100
  "test:ecosystem": "npx tsx tests/ecosystem-benchmark.ts",
108
101
  "install:openclaw": "node -e \"if(process.platform==='win32'){console.error('OpenClaw install requires bash (Git Bash or WSL)');process.exit(1)}else{require('child_process').execSync('bash scripts/install-openclaw-plugin.sh',{stdio:'inherit'})}\"",
102
+ "install:agy": "node scripts/install-antigravity-cli-plugin.mjs",
109
103
  "postinstall": "node scripts/postinstall.mjs"
110
104
  },
111
105
  "dependencies": {