@event4u/agent-config 9.17.0 → 9.18.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.
Files changed (131) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +122 -171
  3. package/dist/agent-src/commands/challenge-me/vision.md +5 -0
  4. package/dist/agent-src/commands/evals/rule-compliance-audit.json +46 -0
  5. package/dist/agent-src/commands/feature/plan.md +20 -0
  6. package/dist/agent-src/commands/feature/roadmap.md +19 -0
  7. package/dist/agent-src/commands/pr/create.md +29 -0
  8. package/dist/agent-src/commands/roadmap/create.md +29 -0
  9. package/dist/agent-src/commands/rule-compliance-audit.md +28 -5
  10. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +133 -0
  11. package/dist/agent-src/rules/active-remediation.md +2 -0
  12. package/dist/agent-src/rules/architecture.md +2 -0
  13. package/dist/agent-src/rules/augment-edit-discipline.md +2 -0
  14. package/dist/agent-src/rules/brand-consistency.md +10 -62
  15. package/dist/agent-src/rules/brand-source-of-truth.md +62 -11
  16. package/dist/agent-src/rules/broken-access-control.md +4 -0
  17. package/dist/agent-src/rules/cli-output-handling.md +5 -0
  18. package/dist/agent-src/rules/code-comment-discipline.md +3 -0
  19. package/dist/agent-src/rules/commit-conventions.md +2 -0
  20. package/dist/agent-src/rules/cross-source-consistency.md +3 -0
  21. package/dist/agent-src/rules/design-fidelity.md +2 -0
  22. package/dist/agent-src/rules/doc-screenshot-hygiene.md +2 -0
  23. package/dist/agent-src/rules/docker-commands.md +4 -0
  24. package/dist/agent-src/rules/domain-adoption-policy.md +2 -0
  25. package/dist/agent-src/rules/domain-safety-disclaimer.md +5 -2
  26. package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
  27. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +9 -0
  28. package/dist/agent-src/rules/image-likeness-and-rights.md +4 -0
  29. package/dist/agent-src/rules/improve-before-implement.md +4 -0
  30. package/dist/agent-src/rules/laravel-routing.md +4 -0
  31. package/dist/agent-src/rules/legal-safety-floor.md +3 -0
  32. package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
  33. package/dist/agent-src/rules/media-governance-routing.md +5 -0
  34. package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
  35. package/dist/agent-src/rules/no-roadmap-references.md +2 -0
  36. package/dist/agent-src/rules/notes-first-reasoning.md +2 -0
  37. package/dist/agent-src/rules/php-coding.md +2 -0
  38. package/dist/agent-src/rules/prefer-enums-over-literals.md +3 -0
  39. package/dist/agent-src/rules/provider-lifecycle-discipline.md +5 -0
  40. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +2 -0
  41. package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
  42. package/dist/agent-src/rules/rule-type-governance.md +2 -0
  43. package/dist/agent-src/rules/scale-discipline.md +3 -0
  44. package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
  45. package/dist/agent-src/rules/security-sensitive-stop.md +9 -2
  46. package/dist/agent-src/rules/senior-engineering-discipline.md +6 -0
  47. package/dist/agent-src/rules/skill-quality.md +2 -0
  48. package/dist/agent-src/rules/source-confidentiality.md +4 -0
  49. package/dist/agent-src/rules/source-discovery-gate.md +3 -0
  50. package/dist/agent-src/rules/source-of-truth.md +2 -0
  51. package/dist/agent-src/rules/strategy-safety-floor.md +2 -0
  52. package/dist/agent-src/rules/think-before-action.md +5 -0
  53. package/dist/agent-src/rules/ui-audit-gate.md +2 -0
  54. package/dist/agent-src/skills/design-intelligence/SKILL.md +6 -180
  55. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +100 -0
  56. package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +91 -0
  57. package/dist/agent-src/skills/design-review/SKILL.md +4 -141
  58. package/dist/agent-src/skills/design-review/references/review-communication.md +92 -0
  59. package/dist/agent-src/skills/design-review/references/verification-automation.md +61 -0
  60. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +8 -27
  61. package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +31 -0
  62. package/dist/agent-src/skills/fe-design/SKILL.md +4 -289
  63. package/dist/agent-src/skills/fe-design/references/design-patterns.md +227 -0
  64. package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +75 -0
  65. package/dist/agent-src/skills/roadmap-management/SKILL.md +10 -0
  66. package/dist/agent-src/skills/roadmap-writing/SKILL.md +12 -1
  67. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  68. package/dist/agent-src/templates/roadmaps.md +12 -0
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
  70. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
  71. package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
  72. package/dist/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
  73. package/dist/cli/registry.js +2 -0
  74. package/dist/cli/registry.js.map +1 -1
  75. package/dist/cli-delegate/chunk-AIIUU73N.js +2040 -0
  76. package/dist/cli-delegate/{chunk-IMSXPRVH.js → chunk-ELMFOQPW.js} +11 -0
  77. package/dist/cli-delegate/chunk-LF2KKJ4Q.js +183 -0
  78. package/dist/cli-delegate/cmd_explain.js +36 -36
  79. package/dist/cli-delegate/cmd_export.js +1 -1
  80. package/dist/cli-delegate/cmd_handoff.js +203 -2228
  81. package/dist/cli-delegate/cmd_prune.js +1 -1
  82. package/dist/cli-delegate/cmd_route_audit.js +249 -0
  83. package/dist/cli-delegate/cmd_route_explain.js +285 -0
  84. package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
  85. package/dist/cli-delegate/cmd_sync.js +1 -1
  86. package/dist/cli-delegate/cmd_uninstall.js +1 -1
  87. package/dist/cli-delegate/cmd_validate.js +1 -1
  88. package/dist/discovery/deprecation-report.md +1 -1
  89. package/dist/discovery/discovery-manifest.json +57 -57
  90. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  91. package/dist/discovery/discovery-manifest.summary.md +1 -1
  92. package/dist/discovery/orphan-report.md +1 -1
  93. package/dist/discovery/packs.json +2 -2
  94. package/dist/discovery/trust-report.md +1 -1
  95. package/dist/discovery/workspaces.json +2 -2
  96. package/dist/install/install.mjs +11 -0
  97. package/dist/mcp/registry-manifest.json +1 -1
  98. package/dist/mcp/server.json +2 -2
  99. package/dist/router.json +1 -1
  100. package/dist/server/schemas/settings.js +12 -0
  101. package/dist/server/schemas/settings.js.map +1 -1
  102. package/package.json +1 -1
  103. package/src/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
  104. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
  105. package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
  106. package/src/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
  107. package/src/config/agent-settings.template.yml +46 -0
  108. package/src/config/evaluator-budgets.json +3 -3
  109. package/src/config/gate-coverage.yml +44 -0
  110. package/src/scripts/_cli/cmd_explain.ts +41 -36
  111. package/src/scripts/_cli/cmd_route_audit.ts +290 -0
  112. package/src/scripts/_cli/cmd_route_explain.ts +269 -0
  113. package/src/scripts/_dispatch.bash +23 -0
  114. package/src/scripts/_lib/git_env.ts +40 -0
  115. package/src/scripts/_lib/md_table.ts +27 -0
  116. package/src/scripts/_lib/planning_settings.ts +43 -0
  117. package/src/scripts/_lib/release_highlights.ts +279 -0
  118. package/src/scripts/_lib/router_match.ts +301 -0
  119. package/src/scripts/annotate_r1_outcomes.ts +258 -0
  120. package/src/scripts/check_completion_review.ts +1322 -0
  121. package/src/scripts/check_release_highlights.ts +60 -152
  122. package/src/scripts/compile_router.ts +20 -3
  123. package/src/scripts/dispatch_r2_reviewer.ts +969 -0
  124. package/src/scripts/eval_ui_triviality.ts +156 -0
  125. package/src/scripts/lint_abstraction_thresholds.ts +3 -1
  126. package/src/scripts/lint_plan_risk_register.ts +931 -0
  127. package/src/scripts/lint_trigger_collisions.ts +257 -0
  128. package/src/scripts/release.ts +47 -13
  129. package/src/scripts/router_telemetry.ts +16 -290
  130. package/src/scripts/schemas/rule.schema.json +10 -0
  131. package/src/server/schemas/settings.ts +18 -0
@@ -1,2241 +1,216 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
3
+ import {
4
+ derive_session_tag,
5
+ list_sessions,
6
+ readHookStdin,
7
+ read_entries
8
+ } from "./chunk-AIIUU73N.js";
3
9
  import "./chunk-XGA2NTU4.js";
4
10
  import {
5
11
  _RAW_KEY_PREFIXES,
6
12
  hardenedSpawnEnv
7
13
  } from "./chunk-VSYT7OHO.js";
8
- import {
9
- load_agent_settings
10
- } from "./chunk-HJ6DAJHR.js";
14
+ import "./chunk-HJ6DAJHR.js";
11
15
  import "./chunk-NDJLNC6Q.js";
12
16
  import "./chunk-OY53J6II.js";
13
17
  import "./chunk-J55WI3RR.js";
14
18
 
15
19
  // src/scripts/_cli/cmd_handoff.ts
16
20
  import { spawnSync as spawnSync2 } from "node:child_process";
17
- import * as fs6 from "node:fs";
21
+ import * as fs4 from "node:fs";
18
22
  import process4 from "node:process";
19
- import * as path5 from "node:path";
20
- import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
23
+ import * as path4 from "node:path";
24
+ import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
21
25
 
22
26
  // src/scripts/_cli/handoff_generate.ts
23
27
  import { spawnSync } from "node:child_process";
24
- import * as fs4 from "node:fs";
25
- import * as path3 from "node:path";
26
- import process3 from "node:process";
27
-
28
- // src/scripts/ai_council/redact_low_impact_entry.ts
29
- function _pyLen(s) {
30
- let n = 0;
31
- for (const _ of s) {
32
- n += 1;
33
- }
34
- return n;
35
- }
36
- function _pySliceHead(s, n) {
37
- let out = "";
38
- let i = 0;
39
- for (const ch of s) {
40
- if (i >= n) {
41
- break;
42
- }
43
- out += ch;
44
- i += 1;
45
- }
46
- return out;
47
- }
48
- function _strip(s) {
49
- return s.trim();
50
- }
51
- function _reEscape(s) {
52
- return s.replace(/[.*+?^${}()|[\]\\]/gu, (c) => "\\" + c);
53
- }
54
- function _violation(category, snippet, note = "") {
55
- return { category, snippet, note };
56
- }
57
- function _pyRepr(s) {
58
- const hasSingle = s.includes("'");
59
- const hasDouble = s.includes('"');
60
- const quote = hasSingle && !hasDouble ? '"' : "'";
61
- let body = s.replace(/\\/g, "\\\\");
62
- body = body.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
63
- if (quote === "'") {
64
- body = body.replace(/'/g, "\\'");
65
- } else {
66
- body = body.replace(/"/g, '\\"');
67
- }
68
- return `${quote}${body}${quote}`;
69
- }
70
- var RedactionResult = class {
71
- ok;
72
- violations;
73
- constructor(ok, violations = []) {
74
- this.ok = ok;
75
- this.violations = violations;
76
- }
77
- summary() {
78
- if (this.ok) {
79
- return "redaction: clean";
80
- }
81
- const parts = this.violations.map((v) => `${v.category}: ${_pyRepr(v.snippet)}`);
82
- return "redaction REFUSED \u2014 " + parts.join("; ");
83
- }
84
- };
85
- var _UWORD = "[\\p{L}\\p{N}_]";
86
- var _UNB_BEFORE = `(?<!${_UWORD})`;
87
- var _UNB_AFTER = `(?!${_UWORD})`;
88
- var _EMAIL_RE = new RegExp(
89
- `${_UNB_BEFORE}[\\p{L}\\p{N}_.+-]+@[\\p{L}\\p{N}_-]+\\.[\\p{L}\\p{N}_.-]+${_UNB_AFTER}`,
90
- "gu"
91
- );
92
- var _PATH_RE = new RegExp(
93
- `(?:^|[\\s"'(])(?:/Users/|/home/|/opt/|/private/|[A-Z]:\\\\)[\\p{L}\\p{N}_.\\-/\\\\]+`,
94
- "gmu"
95
- );
96
- var _INTERNAL_HOST_RE = new RegExp(
97
- `${_UNB_BEFORE}[a-zA-Z0-9][\\p{L}\\p{N}_.-]*\\.(?:internal|local)${_UNB_AFTER}`,
98
- "giu"
99
- );
100
- var _UD = "[\\p{Nd}]";
101
- var _MONEY_RE = new RegExp(
102
- `(?:[\\$\u20AC\xA3\xA5]\\s?${_UD}{1,3}(?:[,.]${_UD}{3})*(?:\\.${_UD}+)?|${_UNB_BEFORE}(?:USD|EUR|GBP|JPY)\\s?${_UD}+(?:[,.]${_UD}+)?)`,
103
- "gu"
104
- );
105
- var _API_KEY_RE = new RegExp(
106
- `${_UNB_BEFORE}api[_-]?key${_UNB_AFTER}\\s*[:=]\\s*[A-Za-z0-9+/=_\\-]{12,}`,
107
- "giu"
108
- );
109
- var _CODE_FENCE_RE = /`([^`]{41,})`/gu;
110
- function _checkSecrets(text) {
111
- const hits = [];
112
- for (const prefix of _RAW_KEY_PREFIXES) {
113
- const pat = new RegExp(_reEscape(prefix) + "[A-Za-z0-9_\\-]{6,}", "u");
114
- const m2 = pat.exec(text);
115
- if (m2) {
116
- hits.push(
117
- _violation(
118
- "secret",
119
- _pySliceHead(m2[0], 8) + "\u2026",
120
- `raw-key prefix ${_pyRepr(prefix)}`
121
- )
122
- );
123
- }
124
- }
125
- _API_KEY_RE.lastIndex = 0;
126
- const m = _API_KEY_RE.exec(text);
127
- if (m) {
128
- hits.push(_violation("secret", _pySliceHead(m[0], 20) + "\u2026", "inline api_key"));
129
- }
130
- return hits;
131
- }
132
- function _checkPatterns(text, repoRoot, privateDomains, customerNames, sqlIdentifiers) {
133
- const hits = [];
134
- for (const m of text.matchAll(_EMAIL_RE)) {
135
- hits.push(_violation("email", m[0]));
136
- }
137
- for (const m of text.matchAll(_PATH_RE)) {
138
- hits.push(_violation("project_path", _strip(m[0])));
139
- }
140
- if (repoRoot && text.includes(repoRoot)) {
141
- hits.push(_violation("project_path", repoRoot, "configured repo root"));
142
- }
143
- for (const m of text.matchAll(_INTERNAL_HOST_RE)) {
144
- hits.push(_violation("internal_hostname", m[0]));
145
- }
146
- for (const dom of privateDomains) {
147
- if (dom && text.includes(dom)) {
148
- hits.push(_violation("internal_hostname", dom, "configured private domain"));
149
- }
150
- }
151
- for (const m of text.matchAll(_MONEY_RE)) {
152
- hits.push(_violation("monetary_amount", m[0]));
153
- }
154
- for (const name of customerNames) {
155
- if (name && new RegExp(`${_UNB_BEFORE}${_reEscape(name)}${_UNB_AFTER}`, "iu").test(text)) {
156
- hits.push(_violation("customer_name", name));
157
- }
158
- }
159
- for (const ident of sqlIdentifiers) {
160
- if (ident && new RegExp(`${_UNB_BEFORE}${_reEscape(ident)}${_UNB_AFTER}`, "u").test(text)) {
161
- hits.push(_violation("sql_identifier", ident));
162
- }
163
- }
164
- for (const m of text.matchAll(_CODE_FENCE_RE)) {
165
- const inner = m[1];
166
- hits.push(
167
- _violation("long_code_excerpt", _pySliceHead(inner, 40) + "\u2026", `${_pyLen(inner)} chars`)
168
- );
169
- }
170
- return hits;
171
- }
172
- function redact_low_impact_entry(text, opts = {}) {
173
- const repoRoot = opts.repoRoot ?? null;
174
- const privateDomains = opts.privateDomains ?? [];
175
- const customerNames = opts.customerNames ?? [];
176
- const sqlIdentifiers = opts.sqlIdentifiers ?? [];
177
- const violations = [];
178
- violations.push(..._checkSecrets(text));
179
- violations.push(
180
- ..._checkPatterns(text, repoRoot, privateDomains, customerNames, sqlIdentifiers)
181
- );
182
- return new RedactionResult(violations.length === 0, violations);
183
- }
184
-
185
- // src/scripts/chat_history.ts
186
- import { createHash } from "node:crypto";
187
28
  import * as fs2 from "node:fs";
188
- import * as path from "node:path";
189
- import { fileURLToPath, pathToFileURL } from "node:url";
190
-
191
- // src/scripts/hooks/hook_stdin.ts
192
- import * as fs from "node:fs";
193
- var _override = null;
194
- function readHookStdin() {
195
- if (_override !== null) return _override;
196
- try {
197
- if (process.stdin.isTTY) return "";
198
- return fs.readFileSync(0, "utf-8");
199
- } catch {
200
- return "";
201
- }
202
- }
203
-
204
- // src/scripts/chat_history.ts
205
- var DEFAULT_FILE = "agents/runtime/.agent-chat-history";
206
- var DEFAULT_SETTINGS_FILE = ".agent-settings.yml";
207
- var SCHEMA_VERSION = 4;
208
- var DEFAULT_MAX_SESSIONS = 5;
209
- var VALID_FREQS = /* @__PURE__ */ new Set(["per_turn", "per_phase", "per_tool"]);
210
- var VALID_OVERFLOW = /* @__PURE__ */ new Set(["rotate", "condense"]);
211
- var REPLAY_ENV_VAR = "AGENT_CONFIG_REPLAY";
212
- function _is_replay_mode() {
213
- return (process.env[REPLAY_ENV_VAR] ?? "").trim() === "1";
214
- }
215
- var _WS_RE = /\s+/g;
216
- var SESSION_ID_LEN = 16;
217
- var SESSION_ID_UNKNOWN = "<unknown>";
218
- var SESSION_ID_LEGACY = "<legacy>";
219
- var AGENT_UNKNOWN = "<unknown>";
220
- var DEFAULT_TEXT_LIMITS = {
221
- user: 0,
222
- agent: 5e3,
223
- tool: 200,
224
- phase: 200
225
- };
226
- var EXIT_OK = 0;
227
- var EXIT_BAD_ARGS = 2;
228
- var PyFloat = class {
229
- constructor(value) {
230
- this.value = value;
231
- }
232
- value;
233
- };
234
- function _isPlainObject(v) {
235
- return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyFloat);
236
- }
237
- function _pyJsonStr(s) {
238
- let out = '"';
239
- for (const ch of s) {
240
- const code = ch.codePointAt(0);
241
- switch (ch) {
242
- case '"':
243
- out += '\\"';
244
- break;
245
- case "\\":
246
- out += "\\\\";
247
- break;
248
- case "\n":
249
- out += "\\n";
250
- break;
251
- case "\r":
252
- out += "\\r";
253
- break;
254
- case " ":
255
- out += "\\t";
256
- break;
257
- case "\b":
258
- out += "\\b";
259
- break;
260
- case "\f":
261
- out += "\\f";
262
- break;
263
- default:
264
- if (code < 32) {
265
- out += `\\u${code.toString(16).padStart(4, "0")}`;
266
- } else {
267
- out += ch;
268
- }
269
- }
270
- }
271
- return out + '"';
272
- }
273
- function _pyJsonNum(n) {
274
- if (!Number.isFinite(n)) {
275
- if (Number.isNaN(n)) {
276
- return "NaN";
277
- }
278
- return n > 0 ? "Infinity" : "-Infinity";
279
- }
280
- return String(n);
281
- }
282
- function _pyJsonFloat(n) {
283
- if (!Number.isFinite(n)) {
284
- if (Number.isNaN(n)) {
285
- return "NaN";
286
- }
287
- return n > 0 ? "Infinity" : "-Infinity";
288
- }
289
- return Number.isInteger(n) ? `${n}.0` : String(n);
290
- }
291
- function _pyJsonScalar(value) {
292
- if (value === null || value === void 0) {
293
- return "null";
294
- }
295
- if (typeof value === "boolean") {
296
- return value ? "true" : "false";
297
- }
298
- if (value instanceof PyFloat) {
299
- return _pyJsonFloat(value.value);
300
- }
301
- if (typeof value === "number") {
302
- return _pyJsonNum(value);
303
- }
304
- if (typeof value === "string") {
305
- return _pyJsonStr(value);
306
- }
307
- return null;
308
- }
309
- function _pyDumps(value) {
310
- const scalar = _pyJsonScalar(value);
311
- if (scalar !== null) {
312
- return scalar;
313
- }
314
- if (Array.isArray(value)) {
315
- return `[${value.map((v) => _pyDumps(v)).join(", ")}]`;
316
- }
317
- if (_isPlainObject(value)) {
318
- const keys = Object.keys(value);
319
- const parts = keys.map((k) => `${_pyJsonStr(k)}: ${_pyDumps(value[k])}`);
320
- return `{${parts.join(", ")}}`;
321
- }
322
- return _pyJsonStr(String(value));
323
- }
324
- function _pyDumpsIndent2(value, depth = 0) {
325
- const scalar = _pyJsonScalar(value);
326
- if (scalar !== null) {
327
- return scalar;
328
- }
329
- const pad = " ".repeat(2 * (depth + 1));
330
- const closePad = " ".repeat(2 * depth);
331
- if (Array.isArray(value)) {
332
- if (value.length === 0) {
333
- return "[]";
334
- }
335
- const items = value.map((v) => pad + _pyDumpsIndent2(v, depth + 1));
336
- return `[
337
- ${items.join(",\n")}
338
- ${closePad}]`;
339
- }
340
- if (_isPlainObject(value)) {
341
- const keys = Object.keys(value);
342
- if (keys.length === 0) {
343
- return "{}";
344
- }
345
- const items = keys.map(
346
- (k) => `${pad}${_pyJsonStr(k)}: ${_pyDumpsIndent2(value[k], depth + 1)}`
347
- );
348
- return `{
349
- ${items.join(",\n")}
350
- ${closePad}}`;
351
- }
352
- return _pyJsonStr(String(value));
353
- }
354
- function _strip2(s) {
355
- return s.replace(/^\s+/u, "").replace(/\s+$/u, "");
356
- }
357
- function _pyLen2(s) {
358
- let n = 0;
359
- for (const _ of s) {
360
- n += 1;
361
- }
362
- return n;
363
- }
364
- function _pySliceTo(s, n) {
365
- if (n <= 0) {
366
- return "";
367
- }
368
- let out = "";
369
- let i = 0;
370
- for (const ch of s) {
371
- if (i >= n) {
372
- break;
373
- }
374
- out += ch;
375
- i += 1;
376
- }
377
- return out;
378
- }
379
- function _ljust(s, width) {
380
- const pad = width - _pyLen2(s);
381
- return pad > 0 ? s + " ".repeat(pad) : s;
382
- }
383
- function _pyIntFromAny(value) {
384
- if (typeof value === "number") {
385
- return Math.trunc(value);
386
- }
387
- if (typeof value === "boolean") {
388
- return value ? 1 : 0;
389
- }
390
- if (typeof value === "string") {
391
- const parsed = Number.parseInt(value, 10);
392
- return Number.isNaN(parsed) ? 0 : parsed;
393
- }
394
- return 0;
395
- }
396
- function _pyRound(value, ndigits = 0) {
397
- if (!Number.isFinite(value)) {
398
- return value;
399
- }
400
- const factor = 10 ** ndigits;
401
- const scaled = value * factor;
402
- const floor = Math.floor(scaled);
403
- const diff = scaled - floor;
404
- let rounded;
405
- const EPS = 1e-9;
406
- if (Math.abs(diff - 0.5) < EPS) {
407
- rounded = floor % 2 === 0 ? floor : floor + 1;
408
- } else {
409
- rounded = Math.round(scaled);
410
- }
411
- return rounded / factor;
412
- }
413
- function file_path() {
414
- return process.env.AGENT_CHAT_HISTORY_FILE || DEFAULT_FILE;
415
- }
416
- function _now() {
417
- return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "+00:00");
418
- }
419
- function fingerprint(value) {
420
- const normalized = _strip2((value || "").replace(_WS_RE, " "));
421
- return createHash("sha256").update(normalized, "utf-8").digest("hex");
422
- }
423
- function derive_session_tag(session_id) {
424
- if (!session_id) {
425
- return SESSION_ID_UNKNOWN;
426
- }
427
- return fingerprint(session_id).slice(0, SESSION_ID_LEN);
428
- }
429
- function _isFile(p) {
430
- try {
431
- return fs2.statSync(p).isFile();
432
- } catch {
433
- return false;
434
- }
435
- }
436
- function _statSize(p) {
437
- try {
438
- return fs2.statSync(p).size;
439
- } catch {
440
- return -1;
441
- }
442
- }
443
- function _preview(msg, n = 80) {
444
- const flat = _strip2((msg || "").replace(_WS_RE, " "));
445
- return _pySliceTo(flat, n);
446
- }
447
- function _extract_text(obj) {
448
- let text = obj.text;
449
- if (typeof text !== "string" || !text) {
450
- const payload = obj.payload;
451
- if (_isPlainObject(payload)) {
452
- text = payload.text;
453
- }
454
- }
455
- return typeof text === "string" ? text : "";
456
- }
457
- function _summarize_session(head, tail, total, n = 60) {
458
- const seen = /* @__PURE__ */ new Set();
459
- const sample = [];
460
- for (const e of [...head, ...tail]) {
461
- if (seen.has(e)) {
462
- continue;
463
- }
464
- seen.add(e);
465
- sample.push(e);
466
- }
467
- const user_texts = sample.filter((e) => e.t === "user" && _extract_text(e)).map((e) => _extract_text(e));
468
- if (user_texts.length > 0) {
469
- const first = _preview(user_texts[0], n);
470
- if (user_texts.length > 1 && user_texts[user_texts.length - 1] !== user_texts[0]) {
471
- const last = _preview(user_texts[user_texts.length - 1], n);
472
- return `${first} \u2192 ${last}`;
473
- }
474
- return first;
475
- }
476
- const counts = /* @__PURE__ */ new Map();
477
- for (const e of sample) {
478
- const k = typeof e.t === "string" ? e.t : "?";
479
- counts.set(k, (counts.get(k) ?? 0) + 1);
480
- }
481
- const ordered = [...counts.entries()].sort((a, b) => b[1] - a[1]);
482
- const mix = ordered.map(([k, v]) => `${k}\xD7${v}`).join(" ");
483
- return `(${total} entries \u2014 no user prompts; t-mix: ${mix})`;
484
- }
485
- function _session_tag_enabled() {
486
- return (process.env.AGENT_CHAT_HISTORY_SESSION_TAG ?? "true").trim().toLowerCase() !== "false";
487
- }
488
- function _readLines(p) {
489
- const raw = fs2.readFileSync(p, "utf-8");
490
- if (raw === "") {
491
- return [];
492
- }
493
- const parts = raw.split("\n");
494
- return parts;
495
- }
496
- function _last_body_session_id(p) {
497
- const target = p ?? file_path();
498
- if (!_isFile(target) || _statSize(target) === 0) {
499
- return SESSION_ID_UNKNOWN;
500
- }
501
- let lines;
502
- try {
503
- lines = _readLines(target);
504
- } catch {
505
- return SESSION_ID_UNKNOWN;
506
- }
507
- for (let i = lines.length - 1; i >= 0; i--) {
508
- const line = _strip2(lines[i] ?? "");
509
- if (!line) {
510
- continue;
511
- }
512
- let obj;
513
- try {
514
- obj = JSON.parse(line);
515
- } catch {
516
- continue;
517
- }
518
- if (!_isPlainObject(obj) || obj.t === "header") {
519
- continue;
520
- }
521
- const sid = obj.s;
522
- if (typeof sid === "string" && sid) {
523
- return sid;
524
- }
525
- }
526
- return SESSION_ID_UNKNOWN;
527
- }
528
- function read_header(p) {
529
- const target = p ?? file_path();
530
- if (!_isFile(target) || _statSize(target) === 0) {
531
- return null;
532
- }
533
- let first;
534
- try {
535
- const raw = fs2.readFileSync(target, "utf-8");
536
- first = _strip2(raw.split("\n", 1)[0] ?? "");
537
- } catch {
538
- return null;
539
- }
540
- if (!first) {
541
- return null;
542
- }
543
- try {
544
- const obj = JSON.parse(first);
545
- if (!(_isPlainObject(obj) && obj.t === "header")) {
546
- return null;
547
- }
548
- return obj;
549
- } catch {
550
- return null;
551
- }
552
- }
553
- function _build_header(freq) {
554
- return { t: "header", v: SCHEMA_VERSION, started: _now(), freq };
555
- }
556
- function init(freq = "per_phase", options = {}) {
557
- if (!VALID_FREQS.has(freq)) {
558
- throw new ValueError(`freq must be one of ${_pyReprSortedList(VALID_FREQS)}`);
559
- }
560
- const target = options.path ?? file_path();
561
- const header = _build_header(freq);
562
- if (_is_replay_mode()) {
563
- return header;
564
- }
565
- fs2.mkdirSync(path.dirname(target), { recursive: true });
566
- fs2.writeFileSync(target, _pyDumps(header) + "\n", { encoding: "utf-8" });
567
- return header;
568
- }
569
- function migrate_header(p, options = {}) {
570
- const target = p ?? file_path();
571
- const existing = read_header(target);
572
- if (existing === null) {
573
- return null;
574
- }
575
- let current_v;
576
- try {
577
- current_v = _pyIntFromAny(existing.v ?? 0);
578
- } catch {
579
- current_v = 0;
580
- }
581
- if (current_v >= SCHEMA_VERSION) {
582
- return null;
583
- }
584
- let chosen_freq = options.freq || (typeof existing.freq === "string" ? existing.freq : "") || "per_phase";
585
- if (!VALID_FREQS.has(chosen_freq)) {
586
- chosen_freq = "per_phase";
587
- }
588
- const new_header = _build_header(chosen_freq);
589
- if (typeof existing.started === "string") {
590
- new_header.started = existing.started;
591
- }
592
- const raw = fs2.readFileSync(target, "utf-8");
593
- const lines = _pySplitlines(raw);
594
- if (lines.length === 0) {
595
- return null;
596
- }
597
- lines[0] = _pyDumps(new_header);
598
- _atomic_write_text(target, lines.join("\n") + "\n");
599
- return new_header;
600
- }
601
- function _pySplitlines(text) {
602
- if (text === "") {
603
- return [];
604
- }
605
- const out = [];
606
- let cur = "";
607
- for (let i = 0; i < text.length; i++) {
608
- const ch = text[i];
609
- if (ch === "\n") {
610
- out.push(cur);
611
- cur = "";
612
- } else if (ch === "\r") {
613
- out.push(cur);
614
- cur = "";
615
- if (text[i + 1] === "\n") {
616
- i += 1;
617
- }
618
- } else {
619
- cur += ch;
620
- }
621
- }
622
- if (cur !== "") {
623
- out.push(cur);
624
- }
625
- return out;
626
- }
627
- function append(entry, options = {}) {
628
- if (!_isPlainObject(entry) || !entry.t) {
629
- throw new ValueError("entry must be a dict with non-empty 't' key");
630
- }
631
- if (entry.t === "header") {
632
- throw new ValueError("use init() to write the header, not append()");
633
- }
634
- const target = options.path ?? file_path();
635
- if (!("ts" in entry)) {
636
- entry.ts = _now();
637
- }
638
- const session = options.session;
639
- if (session !== void 0 && session !== null) {
640
- entry.s = session;
641
- } else if (!("s" in entry) && _session_tag_enabled()) {
642
- entry.s = _last_body_session_id(target);
643
- }
644
- if (_is_replay_mode()) {
645
- return;
646
- }
647
- fs2.appendFileSync(target, _pyDumps(entry) + "\n", { encoding: "utf-8" });
648
- }
649
- function _atomic_write_text(target, text) {
650
- if (_is_replay_mode()) {
651
- return;
652
- }
653
- const suffix = path.extname(target);
654
- const uniq = `${process.pid}.${createHash("sha256").update(`${process.pid}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}`;
655
- const tmp = target.slice(0, target.length - suffix.length) + `${suffix}.${uniq}.tmp`;
656
- try {
657
- fs2.writeFileSync(tmp, text, { encoding: "utf-8" });
658
- fs2.renameSync(tmp, target);
659
- } catch (exc) {
660
- try {
661
- fs2.unlinkSync(tmp);
662
- } catch {
663
- }
664
- throw exc;
665
- }
666
- }
667
- function _normalize_entries(entries) {
668
- const out = [];
669
- for (const raw of entries || []) {
670
- if (!_isPlainObject(raw) || !raw.t) {
671
- throw new ValueError("each entry must be a dict with non-empty 't' key");
672
- }
673
- if (raw.t === "header") {
674
- throw new ValueError("entries must not contain headers");
675
- }
676
- const e = { ...raw };
677
- if (!("ts" in e)) {
678
- e.ts = _now();
679
- }
680
- out.push(e);
681
- }
682
- return out;
683
- }
684
- function reset_with_entries(entries, freq = "per_phase", options = {}) {
685
- if (!VALID_FREQS.has(freq)) {
686
- throw new ValueError(`freq must be one of ${_pyReprSortedList(VALID_FREQS)}`);
687
- }
688
- const target = options.path ?? file_path();
689
- const header = _build_header(freq);
690
- const body = _normalize_entries(entries);
691
- fs2.mkdirSync(path.dirname(target), { recursive: true });
692
- const lines = [_pyDumps(header), ...body.map((e) => _pyDumps(e))];
693
- _atomic_write_text(target, lines.join("\n") + "\n");
694
- return header;
695
- }
696
- function prepend_entries(entries, options = {}) {
697
- const target = options.path ?? file_path();
698
- if (!_isFile(target)) {
699
- throw new FileNotFoundError(`no file at ${target}`);
700
- }
701
- const existing = _readLinesKeepNewline(target);
702
- if (existing.length === 0) {
703
- throw new ValueError(`empty file at ${target}`);
704
- }
705
- const header_line = existing[0];
706
- const body = existing.slice(1);
707
- const new_lines = _normalize_entries(entries).map((e) => _pyDumps(e) + "\n");
708
- _atomic_write_text(target, header_line + new_lines.join("") + body.join(""));
709
- return new_lines.length;
710
- }
711
- function _readLinesKeepNewline(p) {
712
- const raw = fs2.readFileSync(p, "utf-8");
713
- if (raw === "") {
714
- return [];
715
- }
716
- const out = [];
717
- let start = 0;
718
- for (let i = 0; i < raw.length; i++) {
719
- if (raw[i] === "\n") {
720
- out.push(raw.slice(start, i + 1));
721
- start = i + 1;
722
- }
723
- }
724
- if (start < raw.length) {
725
- out.push(raw.slice(start));
726
- }
727
- return out;
728
- }
729
- function clear(options = {}) {
730
- if (_is_replay_mode()) {
731
- return;
732
- }
733
- const target = options.path ?? file_path();
734
- if (fs2.existsSync(target)) {
735
- fs2.unlinkSync(target);
736
- }
737
- }
738
- function read_entries(options = {}) {
739
- return read_entries_with_refs(options).map((p) => p.entry);
740
- }
741
- function read_entries_with_refs(options = {}) {
742
- const target = options.path ?? file_path();
743
- const last = options.last ?? null;
744
- const session = options.session ?? null;
745
- const agent = options.agent ?? null;
746
- if (!_isFile(target)) {
747
- return [];
748
- }
749
- let pairs = [];
750
- const lines = _readLines(target);
751
- let ref = 0;
752
- for (let i = 0; i < lines.length; i++) {
753
- const line = _strip2(lines[i] ?? "");
754
- if (!line) {
755
- continue;
756
- }
757
- let obj;
758
- try {
759
- obj = JSON.parse(line);
760
- } catch {
761
- continue;
762
- }
763
- if (i === 0 && _isPlainObject(obj) && obj.t === "header") {
764
- continue;
765
- }
766
- if (_isPlainObject(obj)) {
767
- pairs.push({ entry: obj, ref });
768
- ref += 1;
769
- }
770
- }
771
- if (session !== null) {
772
- pairs = pairs.filter((p) => p.entry.s === session);
773
- }
774
- if (agent !== null) {
775
- if (agent === AGENT_UNKNOWN) {
776
- pairs = pairs.filter((p) => !p.entry.agent);
777
- } else {
778
- pairs = pairs.filter((p) => p.entry.agent === agent);
779
- }
780
- }
781
- if (last !== null && last >= 0) {
782
- pairs = pairs.slice(pairs.length - last);
783
- }
784
- return pairs;
785
- }
786
- function read_entries_for_current(options = {}) {
787
- const target = options.path ?? file_path();
788
- const last = options.last ?? null;
789
- const kill = (process.env.AGENT_CHAT_HISTORY_SESSION_FILTER ?? "true").trim().toLowerCase();
790
- if (kill === "false") {
791
- return read_entries({ last, path: target, session: null });
792
- }
793
- return read_entries({ last, path: target, session: _last_body_session_id(target) });
794
- }
795
- function list_sessions(options = {}) {
796
- const target = options.path ?? file_path();
797
- const summary = options.summary ?? false;
798
- const buckets = /* @__PURE__ */ new Map();
799
- const _bucket = (sid) => {
800
- let b = buckets.get(sid);
801
- if (b === void 0) {
802
- b = { id: sid, count: 0, first_ts: null, last_ts: null, preview: "", agents: [] };
803
- if (summary) {
804
- b._head = [];
805
- b._tail = [];
806
- }
807
- buckets.set(sid, b);
808
- }
809
- return b;
810
- };
811
- if (_isFile(target)) {
812
- const lines = _readLines(target);
813
- for (let i = 0; i < lines.length; i++) {
814
- const line = _strip2(lines[i] ?? "");
815
- if (!line) {
816
- continue;
817
- }
818
- let obj;
819
- try {
820
- obj = JSON.parse(line);
821
- } catch {
822
- continue;
823
- }
824
- if (!_isPlainObject(obj)) {
825
- continue;
826
- }
827
- if (i === 0 && obj.t === "header") {
828
- continue;
829
- }
830
- const rawSid = obj.s;
831
- const sid = typeof rawSid === "string" && rawSid ? rawSid : SESSION_ID_LEGACY;
832
- const b = _bucket(sid);
833
- b.count += 1;
834
- const agent = typeof obj.agent === "string" ? obj.agent : null;
835
- const tag = agent ? agent : AGENT_UNKNOWN;
836
- if (!b.agents.includes(tag)) {
837
- b.agents.push(tag);
838
- }
839
- const ts = obj.ts;
840
- if (typeof ts === "string" && ts) {
841
- if (b.first_ts === null || ts < b.first_ts) {
842
- b.first_ts = ts;
843
- }
844
- if (b.last_ts === null || ts > b.last_ts) {
845
- b.last_ts = ts;
846
- }
847
- }
848
- if (summary) {
849
- if (b._head.length < 5) {
850
- b._head.push(obj);
851
- }
852
- const tail = b._tail;
853
- tail.push(obj);
854
- if (tail.length > 5) {
855
- tail.shift();
856
- }
857
- }
858
- if (!b.preview || b._preview_from !== "user") {
859
- if (obj.t === "user") {
860
- const payload = _isPlainObject(obj.payload) ? obj.payload : {};
861
- const text = obj.text || payload.text || "";
862
- if (typeof text === "string" && text) {
863
- b.preview = _preview(text);
864
- b._preview_from = "user";
865
- }
866
- } else if (!b.preview) {
867
- const text = obj.text || "";
868
- if (typeof text === "string" && text) {
869
- b.preview = _preview(text);
870
- b._preview_from = "any";
871
- }
872
- }
873
- }
874
- }
875
- }
876
- const out = [];
877
- for (const b of buckets.values()) {
878
- delete b._preview_from;
879
- b.agents = [...b.agents].sort(_pyStrCompare);
880
- if (summary) {
881
- const head = b._head ?? [];
882
- const tail = b._tail ?? [];
883
- delete b._head;
884
- delete b._tail;
885
- b.summary = _summarize_session(head, tail, b.count);
886
- }
887
- out.push(b);
888
- }
889
- out.sort((a, b) => _pyStrCompare(b.last_ts || "", a.last_ts || ""));
890
- return out;
891
- }
892
- function status(options = {}) {
893
- const target = options.path ?? file_path();
894
- if (!_isFile(target)) {
895
- return { exists: false, path: target };
896
- }
897
- const header = read_header(target);
898
- let entry_count = 0;
899
- const per_agent = /* @__PURE__ */ new Map();
900
- const lines = _readLines(target);
901
- for (let i = 0; i < lines.length; i++) {
902
- const line = _strip2(lines[i] ?? "");
903
- if (!line) {
904
- continue;
905
- }
906
- let obj;
907
- try {
908
- obj = JSON.parse(line);
909
- } catch {
910
- continue;
911
- }
912
- if (!_isPlainObject(obj)) {
913
- continue;
914
- }
915
- if (i === 0 && obj.t === "header") {
916
- continue;
917
- }
918
- entry_count += 1;
919
- const agent = typeof obj.agent === "string" ? obj.agent : null;
920
- const tag = agent ? agent : AGENT_UNKNOWN;
921
- per_agent.set(tag, (per_agent.get(tag) ?? 0) + 1);
922
- }
923
- const size = _statSize(target);
924
- const sortedAgents = [...per_agent.entries()].sort((a, b) => _pyStrCompare(a[0], b[0]));
925
- const per_agent_obj = {};
926
- for (const [k, v] of sortedAgents) {
927
- per_agent_obj[k] = v;
928
- }
929
- const agents = { total: per_agent.size, per_agent: per_agent_obj };
930
- return {
931
- exists: true,
932
- path: target,
933
- size_bytes: size,
934
- size_kb: new PyFloat(_pyRound(size / 1024, 1)),
935
- entries: entry_count,
936
- header,
937
- agents
938
- };
939
- }
940
- function _pyStrCompare(a, b) {
941
- if (a < b) {
942
- return -1;
943
- }
944
- if (a > b) {
945
- return 1;
946
- }
947
- return 0;
948
- }
949
- function _load_chat_history_section(settings_path) {
950
- const data = load_agent_settings({ project_path: settings_path });
951
- const section = data.chat_history;
952
- return _isPlainObject(section) ? section : null;
953
- }
954
- function _read_chat_history_enabled(settings_path) {
955
- const section = _load_chat_history_section(settings_path);
956
- if (section === null) {
957
- return false;
958
- }
959
- return _pyBool(section.enabled ?? false);
960
- }
961
- function _pyBool(v) {
962
- if (v === null || v === void 0 || v === false) {
963
- return false;
964
- }
965
- if (v === true) {
966
- return true;
967
- }
968
- if (typeof v === "number") {
969
- return v !== 0;
970
- }
971
- if (typeof v === "string") {
972
- return v.length > 0;
973
- }
974
- if (Array.isArray(v)) {
975
- return v.length > 0;
976
- }
977
- if (_isPlainObject(v)) {
978
- return Object.keys(v).length > 0;
979
- }
980
- return true;
981
- }
982
- var VALID_HOOK_EVENTS = [
983
- "session_start",
984
- "session_end",
985
- "user_prompt",
986
- "agent_response",
987
- "tool_use",
988
- "phase",
989
- "stop"
990
- ];
991
- var VALID_HOOK_EVENTS_SET = new Set(VALID_HOOK_EVENTS);
992
- var HOOK_EVENT_ENTRY_TYPE = {
993
- user_prompt: "user",
994
- agent_response: "agent",
995
- tool_use: "tool",
996
- phase: "phase",
997
- stop: "agent",
998
- session_end: "phase"
999
- };
1000
- var CADENCE_EVENTS = {
1001
- per_turn: /* @__PURE__ */ new Set(["stop", "agent_response", "user_prompt"]),
1002
- per_phase: /* @__PURE__ */ new Set(["phase", "stop", "user_prompt"]),
1003
- per_tool: /* @__PURE__ */ new Set(["tool_use"])
1004
- };
1005
- var PLATFORM_EVENT_MAP = {
1006
- claude: {
1007
- SessionStart: "session_start",
1008
- UserPromptSubmit: "user_prompt",
1009
- PostToolUse: "tool_use",
1010
- Stop: "stop",
1011
- SessionEnd: "session_end",
1012
- PreCompact: "phase"
1013
- },
1014
- cowork: {
1015
- SessionStart: "session_start",
1016
- UserPromptSubmit: "user_prompt",
1017
- PostToolUse: "tool_use",
1018
- Stop: "stop",
1019
- SessionEnd: "session_end",
1020
- PreCompact: "phase"
1021
- },
1022
- augment: {
1023
- SessionStart: "session_start",
1024
- Stop: "stop",
1025
- PostToolUse: "tool_use",
1026
- SessionEnd: "session_end"
1027
- },
1028
- cursor: {
1029
- sessionStart: "session_start",
1030
- sessionEnd: "session_end",
1031
- afterAgentResponse: "agent_response",
1032
- stop: "stop",
1033
- postToolUse: "tool_use",
1034
- beforeSubmitPrompt: "user_prompt"
1035
- },
1036
- cline: {
1037
- TaskStart: "session_start",
1038
- TaskComplete: "session_end",
1039
- UserPromptSubmit: "user_prompt",
1040
- PostToolUse: "tool_use"
1041
- },
1042
- windsurf: {
1043
- pre_user_prompt: "user_prompt",
1044
- post_cascade_response: "agent_response",
1045
- post_cascade_response_with_transcript: "agent_response",
1046
- post_setup_worktree: "phase"
1047
- },
1048
- gemini: {
1049
- SessionStart: "session_start",
1050
- AfterAgent: "agent_response",
1051
- AfterTool: "tool_use",
1052
- SessionEnd: "session_end"
1053
- },
1054
- generic: Object.fromEntries(VALID_HOOK_EVENTS.map((ev) => [ev, ev]))
1055
- };
1056
- var VALID_PLATFORMS = Object.keys(PLATFORM_EVENT_MAP);
1057
- function _read_chat_history_frequency(settings_path) {
1058
- const section = _load_chat_history_section(settings_path);
1059
- if (section === null) {
1060
- return "per_phase";
1061
- }
1062
- const val = String(section.frequency ?? "per_phase").toLowerCase();
1063
- return VALID_FREQS.has(val) ? val : "per_phase";
1064
- }
1065
- function _read_chat_history_max_sessions(settings_path) {
1066
- const section = _load_chat_history_section(settings_path);
1067
- if (section === null) {
1068
- return DEFAULT_MAX_SESSIONS;
1069
- }
1070
- const rawN = section.max_sessions ?? DEFAULT_MAX_SESSIONS;
1071
- let n;
1072
- if (typeof rawN === "number" && Number.isFinite(rawN)) {
1073
- n = Math.trunc(rawN);
1074
- } else if (typeof rawN === "string") {
1075
- const parsed = Number.parseInt(rawN, 10);
1076
- if (Number.isNaN(parsed)) {
1077
- return DEFAULT_MAX_SESSIONS;
1078
- }
1079
- n = parsed;
1080
- } else {
1081
- return DEFAULT_MAX_SESSIONS;
1082
- }
1083
- return Math.max(1, n);
1084
- }
1085
- function _read_text_limits(settings_path) {
1086
- const out = { ...DEFAULT_TEXT_LIMITS };
1087
- const section = _load_chat_history_section(settings_path);
1088
- if (section === null) {
1089
- return out;
1090
- }
1091
- const overrides = section.text_limits;
1092
- if (!_isPlainObject(overrides)) {
1093
- return out;
1094
- }
1095
- for (const [kind, val] of Object.entries(overrides)) {
1096
- let n;
1097
- if (typeof val === "number" && Number.isFinite(val)) {
1098
- n = Math.trunc(val);
1099
- } else if (typeof val === "string") {
1100
- const parsed = Number.parseInt(val, 10);
1101
- if (Number.isNaN(parsed)) {
1102
- continue;
1103
- }
1104
- n = parsed;
1105
- } else {
1106
- continue;
1107
- }
1108
- out[kind] = Math.max(0, n);
1109
- }
1110
- return out;
1111
- }
1112
- function _apply_text_limit(text, kind, limits) {
1113
- if (!text) {
1114
- return "";
1115
- }
1116
- const n = limits[kind] ?? DEFAULT_TEXT_LIMITS[kind] ?? 0;
1117
- if (n <= 0) {
1118
- return text;
1119
- }
1120
- const flat = _strip2(text.replace(_WS_RE, " "));
1121
- if (_pyLen2(flat) <= n) {
1122
- return flat;
1123
- }
1124
- return `${_pySliceTo(flat, n)} \u2026 [+${_pyLen2(flat) - n} chars]`;
1125
- }
1126
- function prune_sessions(max_sessions = DEFAULT_MAX_SESSIONS, options = {}) {
1127
- if (max_sessions < 1) {
1128
- max_sessions = 1;
1129
- }
1130
- const target = options.path ?? file_path();
1131
- if (!_isFile(target)) {
1132
- return { action: "noop", kept_sessions: 0, dropped_sessions: 0, dropped_entries: 0 };
1133
- }
1134
- const lines = _readLinesKeepNewline(target);
1135
- if (lines.length <= 1) {
1136
- return { action: "noop", kept_sessions: 0, dropped_sessions: 0, dropped_entries: 0 };
1137
- }
1138
- const header_line = lines[0];
1139
- const body = lines.slice(1);
1140
- const last_pos = /* @__PURE__ */ new Map();
1141
- const parsed = [];
1142
- for (let idx = 0; idx < body.length; idx++) {
1143
- const line = body[idx];
1144
- const stripped = _strip2(line);
1145
- if (!stripped) {
1146
- continue;
1147
- }
1148
- let obj;
1149
- try {
1150
- obj = JSON.parse(stripped);
1151
- } catch {
1152
- parsed.push([SESSION_ID_LEGACY, line]);
1153
- last_pos.set(SESSION_ID_LEGACY, idx);
1154
- continue;
1155
- }
1156
- if (!_isPlainObject(obj)) {
1157
- continue;
1158
- }
1159
- const sid = typeof obj.s === "string" ? obj.s : SESSION_ID_LEGACY;
1160
- parsed.push([sid, line]);
1161
- last_pos.set(sid, idx);
1162
- }
1163
- if (last_pos.size <= max_sessions) {
1164
- return {
1165
- action: "noop",
1166
- kept_sessions: last_pos.size,
1167
- dropped_sessions: 0,
1168
- dropped_entries: 0
1169
- };
1170
- }
1171
- const ranked = [...last_pos.entries()].sort((a, b) => b[1] - a[1]);
1172
- const keep_set = new Set(ranked.slice(0, max_sessions).map(([sid]) => sid));
1173
- const drop_set = new Set(ranked.slice(max_sessions).map(([sid]) => sid));
1174
- const kept_lines = parsed.filter(([sid]) => keep_set.has(sid)).map(([, line]) => line);
1175
- const dropped_entries = parsed.length - kept_lines.length;
1176
- _atomic_write_text(target, header_line + kept_lines.join(""));
1177
- return {
1178
- action: "pruned",
1179
- kept_sessions: keep_set.size,
1180
- dropped_sessions: drop_set.size,
1181
- dropped_entries
1182
- };
1183
- }
1184
- function overflow_handle(max_kb, mode = "rotate", options = {}) {
1185
- if (!VALID_OVERFLOW.has(mode)) {
1186
- throw new ValueError(`mode must be one of ${_pyReprSortedList(VALID_OVERFLOW)}`);
1187
- }
1188
- const target = options.path ?? file_path();
1189
- if (!_isFile(target) || _statSize(target) <= max_kb * 1024) {
1190
- return { action: "noop", kept: null, dropped: 0 };
1191
- }
1192
- const lines = _readLinesKeepNewline(target);
1193
- if (lines.length === 0) {
1194
- return { action: "noop", kept: 0, dropped: 0 };
1195
- }
1196
- const header_line = lines[0];
1197
- const entries = lines.slice(1);
1198
- if (mode === "rotate") {
1199
- const budget = max_kb * 1024 - Buffer.byteLength(header_line, "utf-8");
1200
- const kept = [];
1201
- let total = 0;
1202
- for (let i = entries.length - 1; i >= 0; i--) {
1203
- const line = entries[i];
1204
- const size = Buffer.byteLength(line, "utf-8");
1205
- if (total + size > budget) {
1206
- break;
1207
- }
1208
- kept.push(line);
1209
- total += size;
1210
- }
1211
- kept.reverse();
1212
- const dropped = entries.length - kept.length;
1213
- _atomic_write_text(target, header_line + kept.join(""));
1214
- return { action: "rotate", kept: kept.length, dropped };
1215
- }
1216
- const marker = {
1217
- t: "needs_condense",
1218
- ts: _now(),
1219
- reason: `file exceeded ${max_kb} KB, condense-mode requested`
1220
- };
1221
- append(marker, { path: target });
1222
- return { action: "condense_marked", kept: entries.length, dropped: 0 };
1223
- }
1224
- function hook_append(event, options = {}) {
1225
- if (!VALID_HOOK_EVENTS_SET.has(event)) {
1226
- throw new ValueError(`event must be one of ${_pyReprSortedListArr(VALID_HOOK_EVENTS)}`);
1227
- }
1228
- const dry_run = options.dry_run ?? false;
1229
- const sp = options.settings_path ?? DEFAULT_SETTINGS_FILE;
1230
- if (!_read_chat_history_enabled(sp)) {
1231
- const out = { action: "disabled", event };
1232
- if (dry_run) {
1233
- out.dry_run = true;
1234
- }
1235
- return out;
1236
- }
1237
- const target = options.path ?? file_path();
1238
- const payload = options.payload ?? {};
1239
- const session_id = options.session_id ?? null;
1240
- const s_tag = session_id ? derive_session_tag(session_id) : SESSION_ID_UNKNOWN;
1241
- const prior_s = _isFile(target) ? _last_body_session_id(target) : SESSION_ID_UNKNOWN;
1242
- const is_new_session = s_tag !== SESSION_ID_UNKNOWN && prior_s !== SESSION_ID_UNKNOWN && prior_s !== s_tag;
1243
- if (is_new_session) {
1244
- process.stderr.write(
1245
- `chat-history session_rotation event=${event} prior_s=${prior_s} new_s=${s_tag}
1246
- `
1247
- );
1248
- }
1249
- if (!dry_run) {
1250
- if (!_isFile(target) || read_header(target) === null) {
1251
- const freq2 = _read_chat_history_frequency(sp);
1252
- init(freq2, { path: target });
1253
- } else {
1254
- migrate_header(target, { freq: _read_chat_history_frequency(sp) });
1255
- }
1256
- }
1257
- const _maybe_prune = () => {
1258
- if (dry_run || !is_new_session) {
1259
- return;
1260
- }
1261
- const max_n = _read_chat_history_max_sessions(sp);
1262
- try {
1263
- prune_sessions(max_n, { path: target });
1264
- } catch (exc) {
1265
- process.stderr.write(`chat-history prune_failed: ${_excStr(exc)}
1266
- `);
1267
- }
1268
- };
1269
- if (event === "session_start") {
1270
- _maybe_prune();
1271
- const action = "session_start_noop";
1272
- const out = { action: dry_run ? "dry_run" : action, event, s: s_tag };
1273
- if (dry_run) {
1274
- out.would_action = action;
1275
- out.dry_run = true;
1276
- }
1277
- return out;
1278
- }
1279
- if (event === "session_end") {
1280
- _maybe_prune();
1281
- const action = "session_end_noop";
1282
- const out = { action: dry_run ? "dry_run" : action, event, s: s_tag };
1283
- if (dry_run) {
1284
- out.would_action = action;
1285
- out.dry_run = true;
1286
- }
1287
- return out;
1288
- }
1289
- const freq = _read_chat_history_frequency(sp);
1290
- if (!(CADENCE_EVENTS[freq] ?? /* @__PURE__ */ new Set()).has(event)) {
1291
- const out = { action: "skipped_cadence", event, frequency: freq };
1292
- if (dry_run) {
1293
- out.dry_run = true;
1294
- }
1295
- return out;
1296
- }
1297
- const entry_type = HOOK_EVENT_ENTRY_TYPE[event] ?? "agent";
1298
- const limits = _read_text_limits(sp);
1299
- const entry = { t: entry_type };
1300
- const text = String(payload.text ?? "");
1301
- if (text) {
1302
- const sliced = _apply_text_limit(text, entry_type, limits);
1303
- if (sliced) {
1304
- entry.text = sliced;
1305
- }
1306
- }
1307
- if (event === "tool_use") {
1308
- const tool = payload.tool;
1309
- if (tool) {
1310
- entry.tool = String(tool);
1311
- }
1312
- }
1313
- for (const k of ["agent", "source", "phase", "decision"]) {
1314
- const v = payload[k];
1315
- if (v) {
1316
- entry[k] = String(v);
1317
- }
1318
- }
1319
- if (dry_run) {
1320
- const preview = { ...entry };
1321
- preview.s = s_tag;
1322
- return {
1323
- action: "dry_run",
1324
- would_action: "appended",
1325
- event,
1326
- type: entry_type,
1327
- s: s_tag,
1328
- entry_preview: preview,
1329
- dry_run: true
1330
- };
1331
- }
1332
- append(entry, { path: target, session: s_tag });
1333
- _maybe_prune();
1334
- return { action: "appended", event, type: entry_type, s: s_tag };
1335
- }
1336
- function _extract_augment_conversation(payload) {
1337
- const conv = payload.conversation;
1338
- if (!_isPlainObject(conv)) {
1339
- return ["", ""];
1340
- }
1341
- const user = conv.userPrompt;
1342
- const agent = conv.agentTextResponse;
1343
- const user_s = typeof user === "string" ? _strip2(user) : "";
1344
- const agent_s = typeof agent === "string" ? _strip2(agent) : "";
1345
- return [user_s, agent_s];
1346
- }
1347
- function _extract_claude_transcript_response(transcript_path) {
1348
- if (!transcript_path) {
1349
- return "";
1350
- }
1351
- if (!_isFile(transcript_path)) {
1352
- return "";
1353
- }
1354
- let last_text = "";
1355
- let lines;
1356
- try {
1357
- lines = _readLines(transcript_path);
1358
- } catch {
1359
- return "";
1360
- }
1361
- for (const rawLine of lines) {
1362
- const line = _strip2(rawLine);
1363
- if (!line) {
1364
- continue;
1365
- }
1366
- let obj;
1367
- try {
1368
- obj = JSON.parse(line);
1369
- } catch {
1370
- continue;
1371
- }
1372
- if (!_isPlainObject(obj)) {
1373
- continue;
1374
- }
1375
- if (obj.type !== "assistant") {
1376
- continue;
1377
- }
1378
- const msg = obj.message;
1379
- if (!_isPlainObject(msg)) {
1380
- continue;
1381
- }
1382
- const content = msg.content;
1383
- if (typeof content === "string") {
1384
- last_text = content;
1385
- } else if (Array.isArray(content)) {
1386
- const parts = [];
1387
- for (const blk of content) {
1388
- if (_isPlainObject(blk) && blk.type === "text") {
1389
- const t = blk.text ?? "";
1390
- if (typeof t === "string") {
1391
- parts.push(t);
1392
- }
1393
- }
1394
- }
1395
- if (parts.length > 0) {
1396
- last_text = parts.join("\n");
1397
- }
1398
- }
1399
- }
1400
- return _strip2(last_text);
1401
- }
1402
- function _extract_cursor_text(payload, event) {
1403
- if (event === "stop" || event === "agent_response") {
1404
- const tp = payload.transcript_path ?? payload.transcriptPath;
1405
- if (typeof tp === "string") {
1406
- const txt = _extract_claude_transcript_response(tp);
1407
- if (txt) {
1408
- return txt;
1409
- }
1410
- }
1411
- }
1412
- return "";
1413
- }
1414
- function _extract_cline_text(payload, event) {
1415
- if (event === "user_prompt") {
1416
- const v = payload.prompt ?? payload.userPrompt;
1417
- if (typeof v === "string" && _strip2(v)) {
1418
- return _strip2(v);
1419
- }
1420
- }
1421
- return "";
1422
- }
1423
- function _extract_gemini_text(payload, event) {
1424
- if (event === "agent_response" || event === "stop") {
1425
- const v = payload.prompt_response ?? payload.promptResponse;
1426
- if (typeof v === "string" && _strip2(v)) {
1427
- return _strip2(v);
1428
- }
1429
- const tp = payload.transcript_path ?? payload.transcriptPath;
1430
- if (typeof tp === "string") {
1431
- const txt = _extract_claude_transcript_response(tp);
1432
- if (txt) {
1433
- return txt;
1434
- }
1435
- }
1436
- }
1437
- return "";
1438
- }
1439
- function _extract_windsurf_text(payload, event) {
1440
- if (event === "agent_response" || event === "stop") {
1441
- const info = payload.tool_info ?? payload.toolInfo;
1442
- if (_isPlainObject(info)) {
1443
- const v = info.response ?? info.text;
1444
- if (typeof v === "string" && _strip2(v)) {
1445
- return _strip2(v);
1446
- }
1447
- }
1448
- const tp = payload.transcript_path ?? payload.transcriptPath;
1449
- if (typeof tp === "string") {
1450
- const txt = _extract_claude_transcript_response(tp);
1451
- if (txt) {
1452
- return txt;
1453
- }
1454
- }
1455
- }
1456
- return "";
1457
- }
1458
- function _extract_hook_text(payload, options = {}) {
1459
- const platform = options.platform ?? null;
1460
- const event = options.event ?? null;
1461
- if (platform === "augment") {
1462
- const [user, agent] = _extract_augment_conversation(payload);
1463
- if (event === "user_prompt" && user) {
1464
- return user;
1465
- }
1466
- if ((event === "stop" || event === "agent_response") && agent) {
1467
- return agent;
1468
- }
1469
- if (agent) {
1470
- return agent;
1471
- }
1472
- if (user) {
1473
- return user;
1474
- }
1475
- }
1476
- if ((platform === "claude" || platform === "cowork") && (event === "stop" || event === "agent_response")) {
1477
- const tp = payload.transcript_path ?? payload.transcriptPath;
1478
- if (typeof tp === "string") {
1479
- const txt = _extract_claude_transcript_response(tp);
1480
- if (txt) {
1481
- return txt;
1482
- }
1483
- }
1484
- }
1485
- if (platform === "cursor") {
1486
- const txt = _extract_cursor_text(payload, event);
1487
- if (txt) {
1488
- return txt;
1489
- }
1490
- }
1491
- if (platform === "cline") {
1492
- const txt = _extract_cline_text(payload, event);
1493
- if (txt) {
1494
- return txt;
1495
- }
1496
- }
1497
- if (platform === "gemini") {
1498
- const txt = _extract_gemini_text(payload, event);
1499
- if (txt) {
1500
- return txt;
1501
- }
1502
- }
1503
- if (platform === "windsurf") {
1504
- const txt = _extract_windsurf_text(payload, event);
1505
- if (txt) {
1506
- return txt;
1507
- }
1508
- }
1509
- for (const key of [
1510
- "prompt",
1511
- "user_prompt",
1512
- "first_user_msg",
1513
- "firstUserMsg",
1514
- "userMessage",
1515
- "user_message",
1516
- "text",
1517
- "response",
1518
- "message",
1519
- "content"
1520
- ]) {
1521
- const v = payload[key];
1522
- if (typeof v === "string" && _strip2(v)) {
1523
- return _strip2(v);
1524
- }
1525
- }
1526
- const tr = payload.tool_response ?? payload.toolResponse;
1527
- if (_isPlainObject(tr)) {
1528
- for (const key of ["output", "stdout", "result", "text"]) {
1529
- const v = tr[key];
1530
- if (typeof v === "string" && _strip2(v)) {
1531
- return _strip2(v);
1532
- }
1533
- }
1534
- }
1535
- return "";
1536
- }
1537
- function _extract_hook_tool(payload) {
1538
- for (const key of ["tool_name", "toolName", "tool"]) {
1539
- const v = payload[key];
1540
- if (typeof v === "string" && _strip2(v)) {
1541
- return _strip2(v);
1542
- }
1543
- }
1544
- return "";
1545
- }
1546
- function _extract_hook_event(payload) {
1547
- for (const key of ["hook_event_name", "event", "eventName", "event_name"]) {
1548
- const v = payload[key];
1549
- if (typeof v === "string" && _strip2(v)) {
1550
- return _strip2(v);
1551
- }
1552
- }
1553
- return "";
1554
- }
1555
- function _extract_session_id(payload) {
1556
- for (const key of ["session_id", "sessionId", "task_id", "taskId", "conversation_id", "conversationId"]) {
1557
- const v = payload[key];
1558
- if (typeof v === "string" && _strip2(v)) {
1559
- return _strip2(v);
1560
- }
29
+ import * as path2 from "node:path";
30
+ import process3 from "node:process";
31
+
32
+ // src/scripts/ai_council/redact_low_impact_entry.ts
33
+ function _pyLen(s) {
34
+ let n = 0;
35
+ for (const _ of s) {
36
+ n += 1;
1561
37
  }
1562
- return "";
38
+ return n;
1563
39
  }
1564
- function hook_dispatch(platform, raw_json, options = {}) {
1565
- if (!(platform in PLATFORM_EVENT_MAP)) {
1566
- throw new ValueError(
1567
- `unknown platform: ${_pyRepr2(platform)}; expected one of ${_pyReprSortedListArr(VALID_PLATFORMS)}`
1568
- );
1569
- }
1570
- const event_override = options.event_override ?? null;
1571
- const dry_run = options.dry_run ?? false;
1572
- const raw = _strip2(raw_json || "");
1573
- let payload;
1574
- if (!raw) {
1575
- payload = {};
1576
- } else {
1577
- let parsed;
1578
- try {
1579
- parsed = JSON.parse(raw);
1580
- } catch (exc) {
1581
- throw new ValueError(`invalid JSON on stdin: ${_excStr(exc)}`);
1582
- }
1583
- if (!_isPlainObject(parsed)) {
1584
- throw new ValueError("stdin JSON must decode to an object");
40
+ function _pySliceHead(s, n) {
41
+ let out = "";
42
+ let i = 0;
43
+ for (const ch of s) {
44
+ if (i >= n) {
45
+ break;
1585
46
  }
1586
- payload = parsed;
1587
- }
1588
- let envelope_event = "";
1589
- if (["schema_version", "platform", "event", "payload"].every((k) => k in payload)) {
1590
- envelope_event = _strip2(
1591
- String(payload.native_event || payload.event || "")
1592
- );
1593
- const inner = payload.payload;
1594
- payload = _isPlainObject(inner) ? inner : {};
1595
- }
1596
- const raw_event = _strip2(
1597
- String(event_override || envelope_event || _extract_hook_event(payload) || "")
1598
- );
1599
- const event = PLATFORM_EVENT_MAP[platform][raw_event];
1600
- if (!event) {
1601
- return { action: "skipped_unmapped_event", platform, raw_event };
1602
- }
1603
- const text = _extract_hook_text(payload, { platform, event });
1604
- const tool = _extract_hook_tool(payload);
1605
- const session_id = _extract_session_id(payload);
1606
- let augment_user_prompt = "";
1607
- if (platform === "augment" && event === "stop") {
1608
- const [u] = _extract_augment_conversation(payload);
1609
- augment_user_prompt = u;
1610
- }
1611
- const hook_payload = {
1612
- source: `hook:${platform}:${raw_event}`,
1613
- agent: platform
1614
- };
1615
- if (text && event !== "session_start") {
1616
- hook_payload.text = text;
1617
- }
1618
- if (tool) {
1619
- hook_payload.tool = tool;
1620
- }
1621
- if (augment_user_prompt) {
1622
- hook_append("user_prompt", {
1623
- session_id,
1624
- payload: {
1625
- text: augment_user_prompt,
1626
- source: `hook:${platform}:${raw_event}:user`,
1627
- agent: platform
1628
- },
1629
- path: options.path,
1630
- settings_path: options.settings_path,
1631
- dry_run
1632
- });
1633
- }
1634
- return hook_append(event, {
1635
- session_id,
1636
- payload: hook_payload,
1637
- path: options.path,
1638
- settings_path: options.settings_path,
1639
- dry_run
1640
- });
1641
- }
1642
- var ValueError = class extends Error {
1643
- };
1644
- var FileNotFoundError = class extends Error {
1645
- };
1646
- function _excStr(exc) {
1647
- if (exc instanceof Error) {
1648
- return exc.message;
1649
- }
1650
- return String(exc);
1651
- }
1652
- function _pyRepr2(s) {
1653
- if (!s.includes("'") || s.includes('"')) {
1654
- return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
47
+ out += ch;
48
+ i += 1;
1655
49
  }
1656
- return `'${s}'`;
1657
- }
1658
- function _pyReprSortedList(set) {
1659
- const items = [...set].sort(_pyStrCompare);
1660
- return `[${items.map((i) => _pyRepr2(i)).join(", ")}]`;
1661
- }
1662
- function _pyReprSortedListArr(arr) {
1663
- const items = [...arr].sort(_pyStrCompare);
1664
- return `[${items.map((i) => _pyRepr2(i)).join(", ")}]`;
1665
- }
1666
- var PROG = "chat_history.py";
1667
- var SUBCMDS = [
1668
- "init",
1669
- "append",
1670
- "status",
1671
- "reset",
1672
- "prune-sessions",
1673
- "prepend",
1674
- "clear",
1675
- "read",
1676
- "sessions",
1677
- "rotate",
1678
- "hook-append",
1679
- "hook-dispatch"
1680
- ];
1681
- function _print(line) {
1682
- process.stdout.write(line + "\n");
1683
- }
1684
- function _eprint(line) {
1685
- process.stderr.write(line + "\n");
1686
- }
1687
- var _TOP_USAGE = `usage: ${PROG} [-h]
1688
- {${SUBCMDS.join(",")}}
1689
- ...
1690
- `;
1691
- function _argError(usage, prog, message) {
1692
- process.stderr.write(usage);
1693
- process.stderr.write(`${prog}: error: ${message}
1694
- `);
1695
- return EXIT_BAD_ARGS;
50
+ return out;
1696
51
  }
1697
- function _cmd_init(argv) {
1698
- let freq = "per_phase";
1699
- for (let i = 0; i < argv.length; i++) {
1700
- const a = argv[i];
1701
- if (a === "--freq") {
1702
- freq = argv[i + 1] ?? freq;
1703
- i += 1;
1704
- } else if (a.startsWith("--freq=")) {
1705
- freq = a.slice("--freq=".length);
1706
- }
1707
- }
1708
- if (!VALID_FREQS.has(freq)) {
1709
- return _argError(
1710
- `usage: ${PROG} init [-h] [--freq {per_phase,per_tool,per_turn}]
1711
- `,
1712
- `${PROG} init`,
1713
- `argument --freq: invalid choice: ${_pyRepr2(freq)} (choose from 'per_phase', 'per_tool', 'per_turn')`
1714
- );
1715
- }
1716
- const h = init(freq);
1717
- _print(_pyDumps(h));
1718
- return 0;
52
+ function _strip(s) {
53
+ return s.trim();
1719
54
  }
1720
- function _cmd_hook_append(argv) {
1721
- let event = null;
1722
- let session_id = null;
1723
- let payloadRaw = null;
1724
- let settings = null;
1725
- let dry_run = false;
1726
- for (let i = 0; i < argv.length; i++) {
1727
- const a = argv[i];
1728
- if (a === "--event") {
1729
- event = argv[++i] ?? null;
1730
- } else if (a.startsWith("--event=")) {
1731
- event = a.slice("--event=".length);
1732
- } else if (a === "--session-id") {
1733
- session_id = argv[++i] ?? null;
1734
- } else if (a.startsWith("--session-id=")) {
1735
- session_id = a.slice("--session-id=".length);
1736
- } else if (a === "--payload") {
1737
- payloadRaw = argv[++i] ?? null;
1738
- } else if (a.startsWith("--payload=")) {
1739
- payloadRaw = a.slice("--payload=".length);
1740
- } else if (a === "--settings") {
1741
- settings = argv[++i] ?? null;
1742
- } else if (a.startsWith("--settings=")) {
1743
- settings = a.slice("--settings=".length);
1744
- } else if (a === "--dry-run") {
1745
- dry_run = true;
1746
- }
1747
- }
1748
- const _hookAppendUsage = `usage: ${PROG} hook-append [-h] --event
1749
- {agent_response,phase,session_end,session_start,stop,tool_use,user_prompt}
1750
- [--session-id SESSION_ID]
1751
- [--payload PAYLOAD] [--settings SETTINGS]
1752
- [--dry-run]
1753
- `;
1754
- if (event === null) {
1755
- return _argError(_hookAppendUsage, `${PROG} hook-append`, "the following arguments are required: --event");
1756
- }
1757
- if (!VALID_HOOK_EVENTS_SET.has(event)) {
1758
- const choices = [...VALID_HOOK_EVENTS].sort(_pyStrCompare).map((c) => _pyRepr2(c)).join(", ");
1759
- return _argError(
1760
- _hookAppendUsage,
1761
- `${PROG} hook-append`,
1762
- `argument --event: invalid choice: ${_pyRepr2(event)} (choose from ${choices})`
1763
- );
1764
- }
1765
- let payload = {};
1766
- if (payloadRaw) {
1767
- let parsed;
1768
- try {
1769
- parsed = JSON.parse(payloadRaw);
1770
- } catch (exc) {
1771
- _eprint(`error: --payload must be valid JSON: ${_excStr(exc)}`);
1772
- return EXIT_BAD_ARGS;
1773
- }
1774
- if (!_isPlainObject(parsed)) {
1775
- _eprint("error: --payload must decode to a JSON object");
1776
- return EXIT_BAD_ARGS;
1777
- }
1778
- payload = parsed;
1779
- }
1780
- try {
1781
- const result = hook_append(event, {
1782
- session_id,
1783
- payload,
1784
- settings_path: settings,
1785
- dry_run
1786
- });
1787
- _print(_pyDumps(result));
1788
- } catch (exc) {
1789
- if (exc instanceof ValueError) {
1790
- _eprint(`error: ${exc.message}`);
1791
- return EXIT_BAD_ARGS;
1792
- }
1793
- throw exc;
1794
- }
1795
- return EXIT_OK;
55
+ function _reEscape(s) {
56
+ return s.replace(/[.*+?^${}()|[\]\\]/gu, (c) => "\\" + c);
1796
57
  }
1797
- function _cmd_hook_dispatch(argv) {
1798
- let platform = null;
1799
- let event = null;
1800
- let settings = null;
1801
- let dry_run = false;
1802
- for (let i = 0; i < argv.length; i++) {
1803
- const a = argv[i];
1804
- if (a === "--platform") {
1805
- platform = argv[++i] ?? null;
1806
- } else if (a.startsWith("--platform=")) {
1807
- platform = a.slice("--platform=".length);
1808
- } else if (a === "--event") {
1809
- event = argv[++i] ?? null;
1810
- } else if (a.startsWith("--event=")) {
1811
- event = a.slice("--event=".length);
1812
- } else if (a === "--settings") {
1813
- settings = argv[++i] ?? null;
1814
- } else if (a.startsWith("--settings=")) {
1815
- settings = a.slice("--settings=".length);
1816
- } else if (a === "--dry-run") {
1817
- dry_run = true;
1818
- }
1819
- }
1820
- const _hookDispatchUsage = `usage: ${PROG} hook-dispatch [-h] --platform
1821
- {augment,claude,cline,cowork,cursor,gemini,generic,windsurf}
1822
- [--event EVENT] [--settings SETTINGS]
1823
- [--dry-run]
1824
- `;
1825
- if (platform === null) {
1826
- return _argError(
1827
- _hookDispatchUsage,
1828
- `${PROG} hook-dispatch`,
1829
- "the following arguments are required: --platform"
1830
- );
1831
- }
1832
- if (!(platform in PLATFORM_EVENT_MAP)) {
1833
- const choices = [...VALID_PLATFORMS].sort(_pyStrCompare).map((c) => _pyRepr2(c)).join(", ");
1834
- return _argError(
1835
- _hookDispatchUsage,
1836
- `${PROG} hook-dispatch`,
1837
- `argument --platform: invalid choice: ${_pyRepr2(platform)} (choose from ${choices})`
1838
- );
1839
- }
1840
- const raw = _readStdin();
1841
- try {
1842
- const result = hook_dispatch(platform, raw, {
1843
- event_override: event,
1844
- settings_path: settings,
1845
- dry_run
1846
- });
1847
- _print(_pyDumps(result));
1848
- } catch (exc) {
1849
- if (exc instanceof ValueError) {
1850
- _eprint(`error: ${exc.message}`);
1851
- return EXIT_BAD_ARGS;
1852
- }
1853
- throw exc;
1854
- }
1855
- return EXIT_OK;
58
+ function _violation(category, snippet, note = "") {
59
+ return { category, snippet, note };
1856
60
  }
1857
- function _cmd_append(argv) {
1858
- let type_ = null;
1859
- let jsonRaw = null;
1860
- let session_id = null;
1861
- for (let i = 0; i < argv.length; i++) {
1862
- const a = argv[i];
1863
- if (a === "--type") {
1864
- type_ = argv[++i] ?? null;
1865
- } else if (a.startsWith("--type=")) {
1866
- type_ = a.slice("--type=".length);
1867
- } else if (a === "--json") {
1868
- jsonRaw = argv[++i] ?? null;
1869
- } else if (a.startsWith("--json=")) {
1870
- jsonRaw = a.slice("--json=".length);
1871
- } else if (a === "--session-id") {
1872
- session_id = argv[++i] ?? null;
1873
- } else if (a.startsWith("--session-id=")) {
1874
- session_id = a.slice("--session-id=".length);
1875
- }
1876
- }
1877
- const entry = jsonRaw ? JSON.parse(jsonRaw) : {};
1878
- if (!("t" in entry)) {
1879
- entry.t = type_;
1880
- }
1881
- if (!entry.t) {
1882
- _eprint("error: --type or a 't' key in --json is required");
1883
- return EXIT_BAD_ARGS;
61
+ function _pyRepr(s) {
62
+ const hasSingle = s.includes("'");
63
+ const hasDouble = s.includes('"');
64
+ const quote = hasSingle && !hasDouble ? '"' : "'";
65
+ let body = s.replace(/\\/g, "\\\\");
66
+ body = body.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
67
+ if (quote === "'") {
68
+ body = body.replace(/'/g, "\\'");
69
+ } else {
70
+ body = body.replace(/"/g, '\\"');
1884
71
  }
1885
- const session = session_id ? derive_session_tag(session_id) : null;
1886
- append(entry, { session });
1887
- return EXIT_OK;
1888
- }
1889
- function _cmd_status() {
1890
- _print(_pyDumpsIndent2(status()));
1891
- return 0;
72
+ return `${quote}${body}${quote}`;
1892
73
  }
1893
- function _loadEntriesArg(entriesJson, entriesStdin) {
1894
- const raw = entriesStdin ? _readStdin() : entriesJson || "[]";
1895
- let data;
1896
- try {
1897
- data = JSON.parse(raw);
1898
- } catch (exc) {
1899
- throw new ValueError(`invalid JSON for entries: ${_excStr(exc)}`);
1900
- }
1901
- if (!Array.isArray(data)) {
1902
- throw new ValueError("entries must be a JSON array");
74
+ var RedactionResult = class {
75
+ ok;
76
+ violations;
77
+ constructor(ok, violations = []) {
78
+ this.ok = ok;
79
+ this.violations = violations;
1903
80
  }
1904
- return data;
1905
- }
1906
- function _cmd_reset(argv) {
1907
- let freq = "per_phase";
1908
- let entriesJson = null;
1909
- let entriesStdin = false;
1910
- for (let i = 0; i < argv.length; i++) {
1911
- const a = argv[i];
1912
- if (a === "--freq") {
1913
- freq = argv[++i] ?? freq;
1914
- } else if (a.startsWith("--freq=")) {
1915
- freq = a.slice("--freq=".length);
1916
- } else if (a === "--entries-json") {
1917
- entriesJson = argv[++i] ?? null;
1918
- } else if (a.startsWith("--entries-json=")) {
1919
- entriesJson = a.slice("--entries-json=".length);
1920
- } else if (a === "--entries-stdin") {
1921
- entriesStdin = true;
81
+ summary() {
82
+ if (this.ok) {
83
+ return "redaction: clean";
1922
84
  }
85
+ const parts = this.violations.map((v) => `${v.category}: ${_pyRepr(v.snippet)}`);
86
+ return "redaction REFUSED \u2014 " + parts.join("; ");
1923
87
  }
1924
- if (!VALID_FREQS.has(freq)) {
1925
- return _argError(
1926
- `usage: ${PROG} reset [-h] [--freq {per_phase,per_tool,per_turn}]
1927
- (--entries-json ENTRIES_JSON | --entries-stdin)
1928
- `,
1929
- `${PROG} reset`,
1930
- `argument --freq: invalid choice: ${_pyRepr2(freq)} (choose from 'per_phase', 'per_tool', 'per_turn')`
1931
- );
1932
- }
1933
- if (entriesJson === null && !entriesStdin) {
1934
- return _argError(
1935
- `usage: ${PROG} reset [-h] [--freq {per_phase,per_tool,per_turn}]
1936
- (--entries-json ENTRIES_JSON | --entries-stdin)
1937
- `,
1938
- `${PROG} reset`,
1939
- "one of the arguments --entries-json --entries-stdin is required"
1940
- );
1941
- }
1942
- const entries = _loadEntriesArg(entriesJson, entriesStdin);
1943
- const h = reset_with_entries(entries, freq);
1944
- _print(_pyDumps(h));
1945
- return 0;
1946
- }
1947
- function _cmd_prune_sessions(argv) {
1948
- let maxSessions = null;
1949
- let settings = null;
1950
- for (let i = 0; i < argv.length; i++) {
1951
- const a = argv[i];
1952
- if (a === "--max-sessions") {
1953
- maxSessions = _pyIntFromAny(argv[++i] ?? "");
1954
- } else if (a.startsWith("--max-sessions=")) {
1955
- maxSessions = _pyIntFromAny(a.slice("--max-sessions=".length));
1956
- } else if (a === "--settings") {
1957
- settings = argv[++i] ?? null;
1958
- } else if (a.startsWith("--settings=")) {
1959
- settings = a.slice("--settings=".length);
88
+ };
89
+ var _UWORD = "[\\p{L}\\p{N}_]";
90
+ var _UNB_BEFORE = `(?<!${_UWORD})`;
91
+ var _UNB_AFTER = `(?!${_UWORD})`;
92
+ var _EMAIL_RE = new RegExp(
93
+ `${_UNB_BEFORE}[\\p{L}\\p{N}_.+-]+@[\\p{L}\\p{N}_-]+\\.[\\p{L}\\p{N}_.-]+${_UNB_AFTER}`,
94
+ "gu"
95
+ );
96
+ var _PATH_RE = new RegExp(
97
+ `(?:^|[\\s"'(])(?:/Users/|/home/|/opt/|/private/|[A-Z]:\\\\)[\\p{L}\\p{N}_.\\-/\\\\]+`,
98
+ "gmu"
99
+ );
100
+ var _INTERNAL_HOST_RE = new RegExp(
101
+ `${_UNB_BEFORE}[a-zA-Z0-9][\\p{L}\\p{N}_.-]*\\.(?:internal|local)${_UNB_AFTER}`,
102
+ "giu"
103
+ );
104
+ var _UD = "[\\p{Nd}]";
105
+ var _MONEY_RE = new RegExp(
106
+ `(?:[\\$\u20AC\xA3\xA5]\\s?${_UD}{1,3}(?:[,.]${_UD}{3})*(?:\\.${_UD}+)?|${_UNB_BEFORE}(?:USD|EUR|GBP|JPY)\\s?${_UD}+(?:[,.]${_UD}+)?)`,
107
+ "gu"
108
+ );
109
+ var _API_KEY_RE = new RegExp(
110
+ `${_UNB_BEFORE}api[_-]?key${_UNB_AFTER}\\s*[:=]\\s*[A-Za-z0-9+/=_\\-]{12,}`,
111
+ "giu"
112
+ );
113
+ var _CODE_FENCE_RE = /`([^`]{41,})`/gu;
114
+ function _checkSecrets(text) {
115
+ const hits = [];
116
+ for (const prefix of _RAW_KEY_PREFIXES) {
117
+ const pat = new RegExp(_reEscape(prefix) + "[A-Za-z0-9_\\-]{6,}", "u");
118
+ const m2 = pat.exec(text);
119
+ if (m2) {
120
+ hits.push(
121
+ _violation(
122
+ "secret",
123
+ _pySliceHead(m2[0], 8) + "\u2026",
124
+ `raw-key prefix ${_pyRepr(prefix)}`
125
+ )
126
+ );
1960
127
  }
1961
128
  }
1962
- let max_n;
1963
- if (maxSessions !== null) {
1964
- max_n = Math.max(1, maxSessions);
1965
- } else {
1966
- const sp = settings ?? DEFAULT_SETTINGS_FILE;
1967
- max_n = _read_chat_history_max_sessions(sp);
129
+ _API_KEY_RE.lastIndex = 0;
130
+ const m = _API_KEY_RE.exec(text);
131
+ if (m) {
132
+ hits.push(_violation("secret", _pySliceHead(m[0], 20) + "\u2026", "inline api_key"));
1968
133
  }
1969
- const result = prune_sessions(max_n);
1970
- _print(_pyDumps(result));
1971
- return EXIT_OK;
134
+ return hits;
1972
135
  }
1973
- function _cmd_prepend(argv) {
1974
- let entriesJson = null;
1975
- let entriesStdin = false;
1976
- for (let i = 0; i < argv.length; i++) {
1977
- const a = argv[i];
1978
- if (a === "--entries-json") {
1979
- entriesJson = argv[++i] ?? null;
1980
- } else if (a.startsWith("--entries-json=")) {
1981
- entriesJson = a.slice("--entries-json=".length);
1982
- } else if (a === "--entries-stdin") {
1983
- entriesStdin = true;
1984
- }
1985
- }
1986
- if (entriesJson === null && !entriesStdin) {
1987
- return _argError(
1988
- `usage: ${PROG} prepend [-h] (--entries-json ENTRIES_JSON | --entries-stdin)
1989
- `,
1990
- `${PROG} prepend`,
1991
- "one of the arguments --entries-json --entries-stdin is required"
1992
- );
136
+ function _checkPatterns(text, repoRoot, privateDomains, customerNames, sqlIdentifiers) {
137
+ const hits = [];
138
+ for (const m of text.matchAll(_EMAIL_RE)) {
139
+ hits.push(_violation("email", m[0]));
1993
140
  }
1994
- const entries = _loadEntriesArg(entriesJson, entriesStdin);
1995
- const n = prepend_entries(entries);
1996
- _print(_pyDumps({ prepended: n }));
1997
- return 0;
1998
- }
1999
- function _cmd_clear() {
2000
- clear();
2001
- return 0;
2002
- }
2003
- function _cmd_read(argv) {
2004
- let last = 5;
2005
- let lastSeen = false;
2006
- let all = false;
2007
- let session = null;
2008
- let agent = null;
2009
- for (let i = 0; i < argv.length; i++) {
2010
- const a = argv[i];
2011
- if (a === "--last") {
2012
- last = _pyIntFromAny(argv[++i] ?? "");
2013
- lastSeen = true;
2014
- } else if (a.startsWith("--last=")) {
2015
- last = _pyIntFromAny(a.slice("--last=".length));
2016
- lastSeen = true;
2017
- } else if (a === "--all") {
2018
- all = true;
2019
- } else if (a === "--session") {
2020
- session = argv[++i] ?? null;
2021
- } else if (a.startsWith("--session=")) {
2022
- session = a.slice("--session=".length);
2023
- } else if (a === "--agent") {
2024
- agent = argv[++i] ?? null;
2025
- } else if (a.startsWith("--agent=")) {
2026
- agent = a.slice("--agent=".length);
2027
- }
141
+ for (const m of text.matchAll(_PATH_RE)) {
142
+ hits.push(_violation("project_path", _strip(m[0])));
2028
143
  }
2029
- if (lastSeen && all) {
2030
- return _argError(
2031
- `usage: ${PROG} read [-h] [--last LAST | --all] [--session SESSION]
2032
- [--agent AGENT]
2033
- `,
2034
- `${PROG} read`,
2035
- "argument --all: not allowed with argument --last"
2036
- );
144
+ if (repoRoot && text.includes(repoRoot)) {
145
+ hits.push(_violation("project_path", repoRoot, "configured repo root"));
2037
146
  }
2038
- const lastArg = all ? null : last;
2039
- let entries;
2040
- if (all) {
2041
- entries = read_entries({ last: lastArg, session: null, agent });
2042
- } else if (session !== null) {
2043
- entries = read_entries({ last: lastArg, session, agent });
2044
- } else if (agent !== null) {
2045
- const sid = _last_body_session_id(file_path());
2046
- entries = read_entries({ last: lastArg, session: sid, agent });
2047
- } else {
2048
- entries = read_entries_for_current({ last: lastArg });
147
+ for (const m of text.matchAll(_INTERNAL_HOST_RE)) {
148
+ hits.push(_violation("internal_hostname", m[0]));
2049
149
  }
2050
- _print(_pyDumpsIndent2(entries));
2051
- return 0;
2052
- }
2053
- function _cmd_sessions(argv) {
2054
- let limit = 20;
2055
- let includeEmpty = false;
2056
- let json = false;
2057
- let summary = false;
2058
- for (let i = 0; i < argv.length; i++) {
2059
- const a = argv[i];
2060
- if (a === "--limit") {
2061
- limit = _pyIntFromAny(argv[++i] ?? "");
2062
- } else if (a.startsWith("--limit=")) {
2063
- limit = _pyIntFromAny(a.slice("--limit=".length));
2064
- } else if (a === "--include-empty") {
2065
- includeEmpty = true;
2066
- } else if (a === "--json") {
2067
- json = true;
2068
- } else if (a === "--summary") {
2069
- summary = true;
150
+ for (const dom of privateDomains) {
151
+ if (dom && text.includes(dom)) {
152
+ hits.push(_violation("internal_hostname", dom, "configured private domain"));
2070
153
  }
2071
154
  }
2072
- let sessions = list_sessions({ summary });
2073
- if (!includeEmpty) {
2074
- sessions = sessions.filter((s) => s.count > 0);
2075
- }
2076
- sessions = sessions.slice(0, limit);
2077
- if (json) {
2078
- _print(_pyDumpsIndent2(sessions));
2079
- return 0;
2080
- }
2081
- if (sessions.length === 0) {
2082
- _print("(no sessions)");
2083
- return 0;
155
+ for (const m of text.matchAll(_MONEY_RE)) {
156
+ hits.push(_violation("monetary_amount", m[0]));
2084
157
  }
2085
- const lastCol = summary ? "SUMMARY" : "PREVIEW";
2086
- const rows = [["ID", "COUNT", "AGENTS", "LAST_TS", lastCol]];
2087
- for (const s of sessions) {
2088
- const lastVal = summary ? s.summary : s.preview;
2089
- const agentsVal = (s.agents || []).join(",") || "-";
2090
- rows.push([s.id, String(s.count), agentsVal, s.last_ts || "-", lastVal || "-"]);
2091
- }
2092
- const widths = [];
2093
- for (let i = 0; i < 5; i++) {
2094
- widths.push(Math.max(...rows.map((r) => _pyLen2(r[i]))));
2095
- }
2096
- for (let i = 0; i < rows.length; i++) {
2097
- const r = rows[i];
2098
- const line = r.map((c, j) => _ljust(c, widths[j])).join(" ");
2099
- _print(line);
2100
- if (i === 0) {
2101
- _print(widths.map((w) => "-".repeat(w)).join(" "));
158
+ for (const name of customerNames) {
159
+ if (name && new RegExp(`${_UNB_BEFORE}${_reEscape(name)}${_UNB_AFTER}`, "iu").test(text)) {
160
+ hits.push(_violation("customer_name", name));
2102
161
  }
2103
162
  }
2104
- return 0;
2105
- }
2106
- function _cmd_rotate(argv) {
2107
- let maxKb = 256;
2108
- let mode = "rotate";
2109
- for (let i = 0; i < argv.length; i++) {
2110
- const a = argv[i];
2111
- if (a === "--max-kb") {
2112
- maxKb = _pyIntFromAny(argv[++i] ?? "");
2113
- } else if (a.startsWith("--max-kb=")) {
2114
- maxKb = _pyIntFromAny(a.slice("--max-kb=".length));
2115
- } else if (a === "--mode") {
2116
- mode = argv[++i] ?? mode;
2117
- } else if (a.startsWith("--mode=")) {
2118
- mode = a.slice("--mode=".length);
163
+ for (const ident of sqlIdentifiers) {
164
+ if (ident && new RegExp(`${_UNB_BEFORE}${_reEscape(ident)}${_UNB_AFTER}`, "u").test(text)) {
165
+ hits.push(_violation("sql_identifier", ident));
2119
166
  }
2120
167
  }
2121
- if (!VALID_OVERFLOW.has(mode)) {
2122
- return _argError(
2123
- `usage: ${PROG} rotate [-h] [--max-kb MAX_KB]
2124
- [--mode {condense,rotate}]
2125
- `,
2126
- `${PROG} rotate`,
2127
- `argument --mode: invalid choice: ${_pyRepr2(mode)} (choose from 'condense', 'rotate')`
2128
- );
2129
- }
2130
- const result = overflow_handle(maxKb, mode);
2131
- _print(_pyDumps(result));
2132
- return 0;
2133
- }
2134
- function _readStdin() {
2135
- return readHookStdin();
2136
- }
2137
- function main(argv = process.argv.slice(2)) {
2138
- const cmd = argv[0];
2139
- if (cmd === void 0) {
2140
- return _argError(_TOP_USAGE, PROG, "the following arguments are required: cmd");
2141
- }
2142
- if (!SUBCMDS.includes(cmd)) {
2143
- const choices = SUBCMDS.map((c) => _pyRepr2(c)).join(", ");
2144
- return _argError(
2145
- _TOP_USAGE,
2146
- PROG,
2147
- `argument cmd: invalid choice: ${_pyRepr2(cmd)} (choose from ${choices})`
168
+ for (const m of text.matchAll(_CODE_FENCE_RE)) {
169
+ const inner = m[1];
170
+ hits.push(
171
+ _violation("long_code_excerpt", _pySliceHead(inner, 40) + "\u2026", `${_pyLen(inner)} chars`)
2148
172
  );
2149
173
  }
2150
- const rest = argv.slice(1);
2151
- switch (cmd) {
2152
- case "init":
2153
- return _cmd_init(rest);
2154
- case "append":
2155
- return _cmd_append(rest);
2156
- case "status":
2157
- return _cmd_status();
2158
- case "reset":
2159
- return _cmd_reset(rest);
2160
- case "prune-sessions":
2161
- return _cmd_prune_sessions(rest);
2162
- case "prepend":
2163
- return _cmd_prepend(rest);
2164
- case "clear":
2165
- return _cmd_clear();
2166
- case "read":
2167
- return _cmd_read(rest);
2168
- case "sessions":
2169
- return _cmd_sessions(rest);
2170
- case "rotate":
2171
- return _cmd_rotate(rest);
2172
- case "hook-append":
2173
- return _cmd_hook_append(rest);
2174
- case "hook-dispatch":
2175
- return _cmd_hook_dispatch(rest);
2176
- default:
2177
- return EXIT_BAD_ARGS;
2178
- }
2179
- }
2180
- function _isCliEntry() {
2181
- if (true) {
2182
- return false;
2183
- }
2184
- if (process.argv[1] === void 0) {
2185
- return false;
2186
- }
2187
- const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
2188
- if (import.meta.url === argvUrl) {
2189
- return true;
2190
- }
2191
- try {
2192
- const here = fs2.realpathSync(fileURLToPath(import.meta.url));
2193
- const argv = fs2.realpathSync(path.resolve(process.argv[1]));
2194
- return here === argv;
2195
- } catch {
2196
- return false;
2197
- }
174
+ return hits;
2198
175
  }
2199
- var _invokedDirectly = _isCliEntry();
2200
- if (_invokedDirectly) {
2201
- try {
2202
- process.exitCode = main();
2203
- } catch (exc) {
2204
- if (exc instanceof ValueError || exc instanceof FileNotFoundError) {
2205
- process.stderr.write(`${exc.constructor.name}: ${exc.message}
2206
- `);
2207
- process.exitCode = 1;
2208
- } else {
2209
- throw exc;
2210
- }
2211
- }
176
+ function redact_low_impact_entry(text, opts = {}) {
177
+ const repoRoot = opts.repoRoot ?? null;
178
+ const privateDomains = opts.privateDomains ?? [];
179
+ const customerNames = opts.customerNames ?? [];
180
+ const sqlIdentifiers = opts.sqlIdentifiers ?? [];
181
+ const violations = [];
182
+ violations.push(..._checkSecrets(text));
183
+ violations.push(
184
+ ..._checkPatterns(text, repoRoot, privateDomains, customerNames, sqlIdentifiers)
185
+ );
186
+ return new RedactionResult(violations.length === 0, violations);
2212
187
  }
2213
188
 
2214
189
  // src/scripts/handoff_context_hook.ts
2215
- import * as fs3 from "node:fs";
2216
- import * as path2 from "node:path";
190
+ import * as fs from "node:fs";
191
+ import * as path from "node:path";
2217
192
  import process2 from "node:process";
2218
- import { fileURLToPath as fileURLToPath2 } from "node:url";
2219
- var HANDOFF_CONTEXT_REL = path2.join("agents", "runtime", "state", "handoff-context.md");
193
+ import { fileURLToPath } from "node:url";
194
+ var HANDOFF_CONTEXT_REL = path.join("agents", "runtime", "state", "handoff-context.md");
2220
195
  var MAX_AGE_HOURS = 48;
2221
- var REPLAY_ENV_VAR2 = "AGENT_CONFIG_REPLAY";
2222
- function _is_replay_mode2() {
2223
- return (process2.env[REPLAY_ENV_VAR2] ?? "").trim() === "1";
196
+ var REPLAY_ENV_VAR = "AGENT_CONFIG_REPLAY";
197
+ function _is_replay_mode() {
198
+ return (process2.env[REPLAY_ENV_VAR] ?? "").trim() === "1";
2224
199
  }
2225
200
  function _handoff_context_path(root) {
2226
- return process2.env.AGENT_HANDOFF_CONTEXT_FILE || path2.join(root, HANDOFF_CONTEXT_REL);
201
+ return process2.env.AGENT_HANDOFF_CONTEXT_FILE || path.join(root, HANDOFF_CONTEXT_REL);
2227
202
  }
2228
203
  function consume_handoff_context(root, now = /* @__PURE__ */ new Date()) {
2229
204
  const target = _handoff_context_path(root);
2230
205
  let text;
2231
206
  try {
2232
- text = fs3.readFileSync(target, "utf-8");
207
+ text = fs.readFileSync(target, "utf-8");
2233
208
  } catch {
2234
209
  return { action: "absent", reason: "no handoff file" };
2235
210
  }
2236
211
  const remove = () => {
2237
212
  try {
2238
- fs3.unlinkSync(target);
213
+ fs.unlinkSync(target);
2239
214
  } catch {
2240
215
  }
2241
216
  };
@@ -2264,7 +239,7 @@ function consume_handoff_context(root, now = /* @__PURE__ */ new Date()) {
2264
239
  remove();
2265
240
  return { action: "inject", reason: `handoff consumed (session=${sourceSession})`, context: block };
2266
241
  }
2267
- function main2() {
242
+ function main() {
2268
243
  let envelope = {};
2269
244
  try {
2270
245
  const raw = readHookStdin().trim();
@@ -2279,7 +254,7 @@ function main2() {
2279
254
  const event = String(envelope.event ?? "");
2280
255
  const root = String(envelope.workspace_root ?? process2.cwd());
2281
256
  try {
2282
- if (_is_replay_mode2()) {
257
+ if (_is_replay_mode()) {
2283
258
  return 0;
2284
259
  }
2285
260
  if (event === "session_start") {
@@ -2301,8 +276,8 @@ function main2() {
2301
276
  return 0;
2302
277
  }
2303
278
  var _bundled = true;
2304
- if (!_bundled && fileURLToPath2(import.meta.url) === path2.resolve(process2.argv[1] ?? "")) {
2305
- process2.exit(main2());
279
+ if (!_bundled && fileURLToPath(import.meta.url) === path.resolve(process2.argv[1] ?? "")) {
280
+ process2.exit(main());
2306
281
  }
2307
282
 
2308
283
  // src/scripts/_cli/handoff_generate.ts
@@ -2325,7 +300,7 @@ var LlmPolishNotImplementedError = class extends Error {
2325
300
  };
2326
301
  function _read_lines(file) {
2327
302
  try {
2328
- return fs4.readFileSync(file, "utf-8").split("\n");
303
+ return fs2.readFileSync(file, "utf-8").split("\n");
2329
304
  } catch {
2330
305
  return [];
2331
306
  }
@@ -2602,22 +577,22 @@ function build_handoff(session, options = {}) {
2602
577
  return text;
2603
578
  }
2604
579
  function handoff_target_path(root) {
2605
- return process3.env.AGENT_HANDOFF_CONTEXT_FILE || path3.join(root, HANDOFF_CONTEXT_REL);
580
+ return process3.env.AGENT_HANDOFF_CONTEXT_FILE || path2.join(root, HANDOFF_CONTEXT_REL);
2606
581
  }
2607
582
  function write_handoff(text, options = {}) {
2608
583
  const root = options.cwd ?? process3.cwd();
2609
584
  const target = handoff_target_path(root);
2610
- fs4.mkdirSync(path3.dirname(target), { recursive: true });
585
+ fs2.mkdirSync(path2.dirname(target), { recursive: true });
2611
586
  const tmp = `${target}.tmp-${process3.pid}`;
2612
- fs4.writeFileSync(tmp, text, { encoding: "utf-8" });
2613
- fs4.renameSync(tmp, target);
587
+ fs2.writeFileSync(tmp, text, { encoding: "utf-8" });
588
+ fs2.renameSync(tmp, target);
2614
589
  return target;
2615
590
  }
2616
591
 
2617
592
  // src/scripts/_cli/handoff_sessions.ts
2618
- import * as fs5 from "node:fs";
593
+ import * as fs3 from "node:fs";
2619
594
  import * as os from "node:os";
2620
- import * as path4 from "node:path";
595
+ import * as path3 from "node:path";
2621
596
  var DEFAULT_CAP = 15;
2622
597
  var SUMMARY_SNIPPET_CHARS = 120;
2623
598
  function encode_project_path(cwd) {
@@ -2635,7 +610,7 @@ function _summarize(first, last) {
2635
610
  }
2636
611
  function _read_lines2(file) {
2637
612
  try {
2638
- return fs5.readFileSync(file, "utf-8").split("\n");
613
+ return fs3.readFileSync(file, "utf-8").split("\n");
2639
614
  } catch {
2640
615
  return [];
2641
616
  }
@@ -2652,10 +627,10 @@ function _parse_json2(line) {
2652
627
  }
2653
628
  return null;
2654
629
  }
2655
- var CHAT_HISTORY_REL = path4.join("agents", "runtime", ".agent-chat-history");
630
+ var CHAT_HISTORY_REL = path3.join("agents", "runtime", ".agent-chat-history");
2656
631
  function _chat_history_path(opts) {
2657
632
  if (opts.chatHistoryPath) return opts.chatHistoryPath;
2658
- return process.env.AGENT_CHAT_HISTORY_FILE || path4.join(opts.cwd ?? process.cwd(), CHAT_HISTORY_REL);
633
+ return process.env.AGENT_CHAT_HISTORY_FILE || path3.join(opts.cwd ?? process.cwd(), CHAT_HISTORY_REL);
2659
634
  }
2660
635
  function _chat_history_summaries(histPath) {
2661
636
  const bySession = /* @__PURE__ */ new Map();
@@ -2740,19 +715,19 @@ function _scan_claude_transcript(file) {
2740
715
  return scan;
2741
716
  }
2742
717
  function _claude_transcript_sessions(opts, cap) {
2743
- const root = opts.claudeProjectsRoot ?? path4.join(os.homedir(), ".claude", "projects");
718
+ const root = opts.claudeProjectsRoot ?? path3.join(os.homedir(), ".claude", "projects");
2744
719
  const cwd = opts.cwd ?? process.cwd();
2745
- const projectDir = path4.join(root, encode_project_path(cwd));
720
+ const projectDir = path3.join(root, encode_project_path(cwd));
2746
721
  let names;
2747
722
  try {
2748
- names = fs5.readdirSync(projectDir).filter((n) => n.endsWith(".jsonl"));
723
+ names = fs3.readdirSync(projectDir).filter((n) => n.endsWith(".jsonl"));
2749
724
  } catch {
2750
725
  return [];
2751
726
  }
2752
727
  const files = names.map((n) => {
2753
- const full = path4.join(projectDir, n);
728
+ const full = path3.join(projectDir, n);
2754
729
  try {
2755
- return { full, id: path4.basename(n, ".jsonl"), mtime: fs5.statSync(full).mtimeMs };
730
+ return { full, id: path3.basename(n, ".jsonl"), mtime: fs3.statSync(full).mtimeMs };
2756
731
  } catch {
2757
732
  return null;
2758
733
  }
@@ -2777,13 +752,13 @@ function _claude_transcript_sessions(opts, cap) {
2777
752
  function _codex_meta(file) {
2778
753
  let firstLine = "";
2779
754
  try {
2780
- const fd = fs5.openSync(file, "r");
755
+ const fd = fs3.openSync(file, "r");
2781
756
  try {
2782
757
  const buf = Buffer.alloc(8192);
2783
- const n = fs5.readSync(fd, buf, 0, buf.length, 0);
758
+ const n = fs3.readSync(fd, buf, 0, buf.length, 0);
2784
759
  firstLine = buf.toString("utf-8", 0, n).split("\n")[0] ?? "";
2785
760
  } finally {
2786
- fs5.closeSync(fd);
761
+ fs3.closeSync(fd);
2787
762
  }
2788
763
  } catch {
2789
764
  return null;
@@ -2808,17 +783,17 @@ function _codex_files(root) {
2808
783
  const dir = stack.pop();
2809
784
  let entries;
2810
785
  try {
2811
- entries = fs5.readdirSync(dir, { withFileTypes: true });
786
+ entries = fs3.readdirSync(dir, { withFileTypes: true });
2812
787
  } catch {
2813
788
  continue;
2814
789
  }
2815
790
  for (const e of entries) {
2816
- const full = path4.join(dir, e.name);
791
+ const full = path3.join(dir, e.name);
2817
792
  if (e.isDirectory()) stack.push(full);
2818
793
  else if (e.isFile() && e.name.endsWith(".jsonl")) out.push(full);
2819
794
  }
2820
795
  }
2821
- return out.sort((a, b) => path4.basename(a) < path4.basename(b) ? 1 : -1);
796
+ return out.sort((a, b) => path3.basename(a) < path3.basename(b) ? 1 : -1);
2822
797
  }
2823
798
  function _codex_prompt_text(rec) {
2824
799
  if (rec.type !== "event_msg") return null;
@@ -2846,13 +821,13 @@ function _scan_codex_session(file) {
2846
821
  return scan;
2847
822
  }
2848
823
  function _matches_repo(sessionCwd, repoCwd) {
2849
- const norm = (p) => path4.resolve(p).replace(/\/+$/, "");
824
+ const norm = (p) => path3.resolve(p).replace(/\/+$/, "");
2850
825
  const a = norm(sessionCwd);
2851
826
  const b = norm(repoCwd);
2852
- return a === b || a.startsWith(b + path4.sep);
827
+ return a === b || a.startsWith(b + path3.sep);
2853
828
  }
2854
829
  function _codex_sessions(opts, cap) {
2855
- const root = opts.codexSessionsRoot ?? path4.join(os.homedir(), ".codex", "sessions");
830
+ const root = opts.codexSessionsRoot ?? path3.join(os.homedir(), ".codex", "sessions");
2856
831
  const cwd = opts.cwd ?? process.cwd();
2857
832
  const matching = [];
2858
833
  for (const file of _codex_files(root)) {
@@ -2900,8 +875,8 @@ function list_handoff_sessions(opts = {}) {
2900
875
  }
2901
876
 
2902
877
  // src/scripts/_cli/cmd_handoff.ts
2903
- var PROG2 = "agent-config handoff";
2904
- var USAGE = `usage: ${PROG2} [--list] [--json] [--session <id>] [--print] [--launch <host>] [--llm] [--root <path>]`;
878
+ var PROG = "agent-config handoff";
879
+ var USAGE = `usage: ${PROG} [--list] [--json] [--session <id>] [--print] [--launch <host>] [--llm] [--root <path>]`;
2905
880
  var _HELP = `${USAGE}
2906
881
 
2907
882
  Pick a recent session, generate a deterministic handoff, and seed a fresh
@@ -2952,7 +927,7 @@ var LAUNCH_ADAPTERS = {
2952
927
  function _default_runner(cmd) {
2953
928
  const r = spawnSync2(cmd[0], cmd.slice(1), { stdio: "inherit" });
2954
929
  if (r.error) {
2955
- process4.stderr.write(`${PROG2}: cannot run ${cmd[0]}: ${String(r.error)}
930
+ process4.stderr.write(`${PROG}: cannot run ${cmd[0]}: ${String(r.error)}
2956
931
  `);
2957
932
  return 127;
2958
933
  }
@@ -2961,14 +936,14 @@ function _default_runner(cmd) {
2961
936
  function _tty_read_line(prompt) {
2962
937
  try {
2963
938
  process4.stdout.write(prompt);
2964
- const fd = fs6.openSync("/dev/tty", "rs");
939
+ const fd = fs4.openSync("/dev/tty", "rs");
2965
940
  try {
2966
941
  const buf = Buffer.alloc(1);
2967
942
  let line = "";
2968
943
  for (; ; ) {
2969
944
  let n = 0;
2970
945
  try {
2971
- n = fs6.readSync(fd, buf, 0, 1, null);
946
+ n = fs4.readSync(fd, buf, 0, 1, null);
2972
947
  } catch (exc) {
2973
948
  const code = exc.code;
2974
949
  if (code === "EAGAIN" || code === "EINTR") continue;
@@ -2981,15 +956,15 @@ function _tty_read_line(prompt) {
2981
956
  }
2982
957
  return line;
2983
958
  } finally {
2984
- fs6.closeSync(fd);
959
+ fs4.closeSync(fd);
2985
960
  }
2986
961
  } catch {
2987
962
  return null;
2988
963
  }
2989
964
  }
2990
- function _argError2(err, message) {
965
+ function _argError(err, message) {
2991
966
  err(`${USAGE}
2992
- ${PROG2}: error: ${message}
967
+ ${PROG}: error: ${message}
2993
968
  `);
2994
969
  throw new ArgparseExit(2);
2995
970
  }
@@ -3010,7 +985,7 @@ function _parse(argv, err) {
3010
985
  if (eq >= 0) return arg.slice(eq + 1);
3011
986
  i += 1;
3012
987
  const value = argv[i];
3013
- if (value === void 0) _argError2(err, `argument ${flag}: expected one argument`);
988
+ if (value === void 0) _argError(err, `argument ${flag}: expected one argument`);
3014
989
  return value;
3015
990
  };
3016
991
  while (i < argv.length) {
@@ -3043,7 +1018,7 @@ function _parse(argv, err) {
3043
1018
  opts.root = takeValue("--root");
3044
1019
  break;
3045
1020
  default:
3046
- _argError2(err, `unrecognized arguments: ${arg}`);
1021
+ _argError(err, `unrecognized arguments: ${arg}`);
3047
1022
  }
3048
1023
  i += 1;
3049
1024
  }
@@ -3061,7 +1036,7 @@ function _render_list(sessions, json) {
3061
1036
  if (json) return JSON.stringify(sessions, null, 2) + "\n";
3062
1037
  return sessions.map((s, i) => render_session_line(s, i)).join("\n") + "\n";
3063
1038
  }
3064
- function main3(argv = null, options = {}) {
1039
+ function main2(argv = null, options = {}) {
3065
1040
  const out = options.out ?? ((t) => void process4.stdout.write(t));
3066
1041
  const err = options.err ?? ((t) => void process4.stderr.write(t));
3067
1042
  const isTTY = options.isTTY ?? Boolean(process4.stdin.isTTY && process4.stdout.isTTY);
@@ -3075,7 +1050,7 @@ function main3(argv = null, options = {}) {
3075
1050
  throw exc;
3076
1051
  }
3077
1052
  const envRoot = process4.env.AGENT_CONFIG_ROOT_OVERRIDE === "1" ? process4.env.AGENT_CONFIG_PROJECT_ROOT || null : null;
3078
- const root = path5.resolve(opts.root ?? envRoot ?? process4.cwd());
1053
+ const root = path4.resolve(opts.root ?? envRoot ?? process4.cwd());
3079
1054
  const sessions = list_handoff_sessions({ cwd: root });
3080
1055
  if (opts.list) {
3081
1056
  if (sessions.length === 0) {
@@ -3086,19 +1061,19 @@ function main3(argv = null, options = {}) {
3086
1061
  return 0;
3087
1062
  }
3088
1063
  if (sessions.length === 0) {
3089
- err(`${PROG2}: no recent sessions found for this repo
1064
+ err(`${PROG}: no recent sessions found for this repo
3090
1065
  `);
3091
1066
  return 1;
3092
1067
  }
3093
1068
  if (opts.launch && !(opts.launch in LAUNCH_ADAPTERS)) {
3094
1069
  err(`${USAGE}
3095
- ${PROG2}: error: unknown --launch host '${opts.launch}' (claude | codex)
1070
+ ${PROG}: error: unknown --launch host '${opts.launch}' (claude | codex)
3096
1071
  `);
3097
1072
  return 2;
3098
1073
  }
3099
1074
  const adapter = opts.launch ? LAUNCH_ADAPTERS[opts.launch] : null;
3100
1075
  if (opts.launch && adapter && !adapter.supports_launch) {
3101
- err(`${PROG2}: --launch ${opts.launch} is not supported yet (hook injection already works there)
1076
+ err(`${PROG}: --launch ${opts.launch} is not supported yet (hook injection already works there)
3102
1077
  `);
3103
1078
  return 2;
3104
1079
  }
@@ -3106,12 +1081,12 @@ ${PROG2}: error: unknown --launch host '${opts.launch}' (claude | codex)
3106
1081
  if (opts.session) {
3107
1082
  const matches = sessions.filter((s) => s.id === opts.session || s.id.startsWith(opts.session));
3108
1083
  if (matches.length === 0) {
3109
- err(`${PROG2}: session '${opts.session}' not found \u2014 run --list
1084
+ err(`${PROG}: session '${opts.session}' not found \u2014 run --list
3110
1085
  `);
3111
1086
  return 1;
3112
1087
  }
3113
1088
  if (matches.length > 1) {
3114
- err(`${PROG2}: session prefix '${opts.session}' is ambiguous (${matches.length} matches)
1089
+ err(`${PROG}: session prefix '${opts.session}' is ambiguous (${matches.length} matches)
3115
1090
  `);
3116
1091
  return 2;
3117
1092
  }
@@ -3122,14 +1097,14 @@ ${PROG2}: error: unknown --launch host '${opts.launch}' (claude | codex)
3122
1097
  const raw = readLine(`Pick a session [1-${sessions.length}]: `);
3123
1098
  const n = Number((raw ?? "").trim());
3124
1099
  if (!Number.isInteger(n) || n < 1 || n > sessions.length) {
3125
- err(`${PROG2}: invalid selection
1100
+ err(`${PROG}: invalid selection
3126
1101
  `);
3127
1102
  return 2;
3128
1103
  }
3129
1104
  picked = sessions[n - 1];
3130
1105
  } else {
3131
1106
  out(_render_list(sessions, false));
3132
- err(`${PROG2}: non-interactive invocation needs --session <id> (list above)
1107
+ err(`${PROG}: non-interactive invocation needs --session <id> (list above)
3133
1108
  `);
3134
1109
  return 2;
3135
1110
  }
@@ -3139,7 +1114,7 @@ ${PROG2}: error: unknown --launch host '${opts.launch}' (claude | codex)
3139
1114
  text = build_handoff(picked, { cwd: root, llm: opts.llm });
3140
1115
  } catch (exc) {
3141
1116
  if (exc instanceof LlmPolishNotImplementedError) {
3142
- err(`${PROG2}: ${exc.message}
1117
+ err(`${PROG}: ${exc.message}
3143
1118
  `);
3144
1119
  return 2;
3145
1120
  }
@@ -3154,7 +1129,7 @@ ${PROG2}: error: unknown --launch host '${opts.launch}' (claude | codex)
3154
1129
  `);
3155
1130
  return runner(adapter.build_argv(text));
3156
1131
  }
3157
- const existed = fs6.existsSync(handoff_target_path(root));
1132
+ const existed = fs4.existsSync(handoff_target_path(root));
3158
1133
  const target = write_handoff(text, { cwd: root });
3159
1134
  if (existed) out("note: replaced an unconsumed handoff \u2014 only the most recent one persists.\n");
3160
1135
  out(`handoff written: ${target}
@@ -3167,28 +1142,28 @@ ${PROG2}: error: unknown --launch host '${opts.launch}' (claude | codex)
3167
1142
  }
3168
1143
  return 0;
3169
1144
  }
3170
- function _isCliEntry2() {
1145
+ function _isCliEntry() {
3171
1146
  if (process4.argv[1] === void 0) {
3172
1147
  return false;
3173
1148
  }
3174
- const argvUrl = pathToFileURL2(path5.resolve(process4.argv[1])).href;
1149
+ const argvUrl = pathToFileURL(path4.resolve(process4.argv[1])).href;
3175
1150
  if (import.meta.url === argvUrl) {
3176
1151
  return true;
3177
1152
  }
3178
1153
  try {
3179
- const here = fs6.realpathSync(fileURLToPath3(import.meta.url));
3180
- const argv = fs6.realpathSync(path5.resolve(process4.argv[1]));
1154
+ const here = fs4.realpathSync(fileURLToPath2(import.meta.url));
1155
+ const argv = fs4.realpathSync(path4.resolve(process4.argv[1]));
3181
1156
  return here === argv;
3182
1157
  } catch {
3183
1158
  return false;
3184
1159
  }
3185
1160
  }
3186
- if (_isCliEntry2()) {
3187
- process4.exitCode = main3();
1161
+ if (_isCliEntry()) {
1162
+ process4.exitCode = main2();
3188
1163
  }
3189
1164
  export {
3190
1165
  LAUNCH_ADAPTERS,
3191
1166
  _parse,
3192
- main3 as main,
1167
+ main2 as main,
3193
1168
  render_session_line
3194
1169
  };