caveat-cli 0.16.1 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11,11 +11,12 @@ import {
11
11
  __export,
12
12
  __toESM,
13
13
  acquireReindexLock,
14
- appendPendingReminder,
14
+ buildAndPublishPendingReminder,
15
15
  buildCodexSidecarDiagnosticsCommand,
16
16
  buildCodexSidecarReadOnlySmokeCommand,
17
17
  buildCodexSidecarRunCommand,
18
18
  buildHookSignalSidecarContextBlock,
19
+ buildPendingSemanticKey,
19
20
  caveatEntriesToSidecarContextBlocks,
20
21
  cleanupStalePendingDirs,
21
22
  communityAdd,
@@ -26,11 +27,11 @@ import {
26
27
  createKeyserverKeyProvider,
27
28
  decideCodexSidecarExecution,
28
29
  defaultSelfIdentityTokens,
29
- drainGlobalPendingReminders,
30
- drainPendingReminders,
30
+ drainPendingRemindersDetailed,
31
31
  ensureUserConfig,
32
32
  findCaveatHome,
33
- findCaveatsForPrompt,
33
+ findCaveatsForHook,
34
+ findCaveatsForHookSegments,
34
35
  get,
35
36
  hasAnyStruggleSignal,
36
37
  initOwnSync,
@@ -63,7 +64,7 @@ import {
63
64
  userPromptSubmitReminderText,
64
65
  writeDigestMarker,
65
66
  writeUserConfigPatch
66
- } from "./chunk-HCLEIAGO.js";
67
+ } from "./chunk-3MLJQLWY.js";
67
68
 
68
69
  // ../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js
69
70
  var require_code = __commonJS({
@@ -7544,6 +7545,16 @@ import { spawnSync as spawnSync3 } from "node:child_process";
7544
7545
  import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
7545
7546
  import { tmpdir } from "node:os";
7546
7547
  import { join as join6 } from "node:path";
7548
+ var DEFAULT_HOOK_SIDECAR_TIMEOUT_MS = 12e4;
7549
+ var MAX_HOOK_SIDECAR_TIMEOUT_MS = 24e4;
7550
+ function resolveHookSidecarTimeoutMs(raw) {
7551
+ if (raw === void 0) return DEFAULT_HOOK_SIDECAR_TIMEOUT_MS;
7552
+ const value = Number(raw);
7553
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAX_HOOK_SIDECAR_TIMEOUT_MS) {
7554
+ throw new Error(`CAVEAT_HOOK_CODEX_SIDECAR_TIMEOUT_MS must be an integer from 1 to ${MAX_HOOK_SIDECAR_TIMEOUT_MS}`);
7555
+ }
7556
+ return value;
7557
+ }
7547
7558
  function compactFailureMessage(message) {
7548
7559
  const singleLine = message.replace(/\s+/g, " ").trim();
7549
7560
  if (!singleLine) return "unknown error";
@@ -7607,7 +7618,9 @@ function runCodexSidecarAdvisory(input) {
7607
7618
  const result = spawnSync3(process.execPath, args, {
7608
7619
  cwd: input.projectRoot,
7609
7620
  encoding: "utf-8",
7610
- timeout: Number(process.env.CAVEAT_HOOK_CODEX_SIDECAR_TIMEOUT_MS ?? 12e4),
7621
+ // Keep at least one minute between the longest supported model call and
7622
+ // the pending single-flight claim TTL (five minutes).
7623
+ timeout: resolveHookSidecarTimeoutMs(process.env.CAVEAT_HOOK_CODEX_SIDECAR_TIMEOUT_MS),
7611
7624
  maxBuffer: 10 * 1024 * 1024
7612
7625
  });
7613
7626
  if (result.error) {
@@ -8205,7 +8218,7 @@ function runStats(ctx) {
8205
8218
 
8206
8219
  // src/commands/serve.ts
8207
8220
  async function runServe(opts) {
8208
- const { startServer } = await import("./server-EASKBCNK.js");
8221
+ const { startServer } = await import("./server-LAJCQXS3.js");
8209
8222
  const { port, host } = startServer({ port: opts.port });
8210
8223
  process.stdout.write(`[caveat] web portal: http://${host}:${port}/
8211
8224
  `);
@@ -32670,8 +32683,10 @@ function buildContextSafely() {
32670
32683
  return null;
32671
32684
  }
32672
32685
  }
32673
- function searchCaveatsFromTextSafely(text, surface) {
32674
- if (!text) return [];
32686
+ function searchCaveatsSafely(input) {
32687
+ const inputs = Array.isArray(input) ? input : [input];
32688
+ const queryForLog = inputs.map((item) => item.surface === "user_prompt" ? item.topicText || item.failureText : item.failureText).filter(Boolean).join("\n");
32689
+ if (inputs.length === 0 || inputs.every((item) => !item.topicText && !item.failureText)) return [];
32675
32690
  let db;
32676
32691
  let caveatHome;
32677
32692
  let hits;
@@ -32680,9 +32695,10 @@ function searchCaveatsFromTextSafely(text, surface) {
32680
32695
  if (!ctx || !existsSync10(ctx.paths.dbPath)) return [];
32681
32696
  caveatHome = ctx.caveatHome;
32682
32697
  db = openDb({ path: ctx.paths.dbPath });
32683
- hits = findCaveatsForPrompt(db, text, {
32698
+ const searchOptions = {
32684
32699
  selfIdentity: defaultSelfIdentityTokens()
32685
- });
32700
+ };
32701
+ hits = inputs.length === 1 ? findCaveatsForHook(db, inputs[0], searchOptions) : findCaveatsForHookSegments(db, inputs, searchOptions);
32686
32702
  } catch (err) {
32687
32703
  const msg = err instanceof Error ? err.message : String(err);
32688
32704
  process.stderr.write(`[caveat:hook] search error: ${msg}
@@ -32699,7 +32715,7 @@ function searchCaveatsFromTextSafely(text, surface) {
32699
32715
  }
32700
32716
  } else {
32701
32717
  try {
32702
- logHookQueryMiss({ caveatHome, agent: "claude", surface, query: text });
32718
+ logHookQueryMiss({ caveatHome, agent: "claude", surface: inputs[0].surface, query: queryForLog });
32703
32719
  } catch (err) {
32704
32720
  const msg = err instanceof Error ? err.message : String(err);
32705
32721
  process.stderr.write(`[caveat:hook] query log error: ${msg}
@@ -32725,13 +32741,19 @@ function loadSignalsSafely(path) {
32725
32741
  function systemReminderOutput(text) {
32726
32742
  return `<system-reminder>${text.replace(/</g, "\u2039").replace(/>/g, "\u203A")}</system-reminder>`;
32727
32743
  }
32744
+ function claudePendingCleanupFailureText() {
32745
+ return "[caveat:hook] pending reminder cleanup failed";
32746
+ }
32728
32747
  function drainForSession(sessionId) {
32729
32748
  const ctx = buildContextSafely();
32730
32749
  if (!ctx) return [];
32731
- return [
32732
- ...drainPendingReminders(ctx.caveatHome, sessionId),
32733
- ...drainGlobalPendingReminders(ctx.caveatHome)
32734
- ];
32750
+ const local = drainPendingRemindersDetailed(ctx.caveatHome, sessionId);
32751
+ const global = drainPendingRemindersDetailed(ctx.caveatHome, "_global");
32752
+ for (const _failure of [...local.cleanupFailures, ...global.cleanupFailures]) {
32753
+ process.stderr.write(`${claudePendingCleanupFailureText()}
32754
+ `);
32755
+ }
32756
+ return [...local.reminders, ...global.reminders];
32735
32757
  }
32736
32758
  function claudeContextDedupeKey(text) {
32737
32759
  if (text.startsWith(CLAUDE_STOP_REMINDER_PREFIX)) return "claude-stop-reminder";
@@ -32750,7 +32772,7 @@ function compactClaudeContexts(contexts) {
32750
32772
  }
32751
32773
  selected.reverse();
32752
32774
  const limited = selected.slice(-CLAUDE_MAX_CONTEXT_BLOCKS);
32753
- const omitted = contexts.filter((t) => t.trim().length > 0).length - limited.length;
32775
+ const omitted = selected.length - limited.length;
32754
32776
  if (omitted > 0) {
32755
32777
  limited.push(
32756
32778
  `[caveat] pending reminder ${omitted} \u4EF6\u3092\u91CD\u8907\u307E\u305F\u306F\u4E0A\u9650\u306B\u3088\u308A\u7701\u7565\u3057\u307E\u3057\u305F\u3002`
@@ -32795,8 +32817,20 @@ function queueStopForSession(sessionId, signals, related) {
32795
32817
  if (!ctx) return;
32796
32818
  const key = stopSignalKey(signals, related);
32797
32819
  if (wasStopReminderQueued(ctx.caveatHome, sessionId, key)) return;
32820
+ let result;
32821
+ try {
32822
+ result = buildAndPublishPendingReminder(ctx.caveatHome, sessionId, buildPendingSemanticKey({
32823
+ agent: "claude",
32824
+ surface: "stop",
32825
+ refs: related,
32826
+ stopSignalDigest: key
32827
+ }), () => buildStopReminder(signals, related));
32828
+ } catch {
32829
+ process.stderr.write("[caveat:hook] pending reminder build or publish failed\n");
32830
+ return;
32831
+ }
32832
+ if (!result.ran) return;
32798
32833
  try {
32799
- appendPendingReminder(ctx.caveatHome, sessionId, buildStopReminder(signals, related));
32800
32834
  markStopReminderQueued(ctx.caveatHome, sessionId, key);
32801
32835
  } catch (err) {
32802
32836
  const msg = err instanceof Error ? err.message : String(err);
@@ -32827,6 +32861,20 @@ function extractToolResponseText(response) {
32827
32861
  }
32828
32862
  return "";
32829
32863
  }
32864
+ function toolTopicText(payload) {
32865
+ const parts = [];
32866
+ const toolName = payload.tool_name ?? payload.toolName;
32867
+ if (typeof toolName === "string") parts.push(toolName);
32868
+ const input = payload.tool_input ?? payload.toolInput;
32869
+ if (typeof input === "string") parts.push(input);
32870
+ if (input !== null && typeof input === "object" && !Array.isArray(input)) {
32871
+ const record2 = input;
32872
+ for (const key of ["command", "cmd", "query", "url"]) {
32873
+ if (typeof record2[key] === "string") parts.push(record2[key]);
32874
+ }
32875
+ }
32876
+ return parts.join("\n");
32877
+ }
32830
32878
  function isToolError(payload) {
32831
32879
  if (payload.hook_event_name === "PostToolUseFailure") return true;
32832
32880
  const resp = payload.tool_response ?? payload.toolResponse;
@@ -32850,7 +32898,7 @@ function spawnWorker(job) {
32850
32898
  workDir = mkdtempSync2(join12(root, "job-"));
32851
32899
  chmodSync(workDir, 448);
32852
32900
  workFile = join12(workDir, `${randomBytes(4).toString("hex")}.json`);
32853
- writeFileSync5(workFile, JSON.stringify({ ...job, schemaVersion: "caveat-worker-job/v1" }), { encoding: "utf-8", mode: 384, flag: "wx" });
32901
+ writeFileSync5(workFile, JSON.stringify({ ...job, schemaVersion: "caveat-worker-job/v2" }), { encoding: "utf-8", mode: 384, flag: "wx" });
32854
32902
  } catch (err) {
32855
32903
  const msg = err instanceof Error ? err.message : String(err);
32856
32904
  process.stderr.write(`[caveat:hook] worker writefile error: ${msg}
@@ -32894,15 +32942,27 @@ async function runWorker(workFile) {
32894
32942
  } catch {
32895
32943
  process.exit(0);
32896
32944
  }
32897
- if (!job.searchText || !job.sessionId) process.exit(0);
32898
- const hits = searchCaveatsFromTextSafely(job.searchText, "tool_error");
32945
+ if (!job.failureText || !job.sessionId) process.exit(0);
32946
+ const hits = searchCaveatsSafely({
32947
+ topicText: job.topicText,
32948
+ failureText: job.failureText,
32949
+ surface: "tool_error"
32950
+ });
32899
32951
  if (hits.length === 0) process.exit(0);
32900
32952
  const ctx = buildContextSafely();
32901
32953
  if (!ctx) process.exit(0);
32954
+ let result;
32902
32955
  try {
32903
- appendPendingReminder(ctx.caveatHome, job.sessionId, buildToolErrorReminder(job, hits));
32956
+ result = buildAndPublishPendingReminder(ctx.caveatHome, job.sessionId, buildPendingSemanticKey({
32957
+ agent: "claude",
32958
+ surface: "tool_error",
32959
+ refs: hits
32960
+ }), () => buildToolErrorReminder(job, hits));
32904
32961
  } catch {
32962
+ process.stderr.write("[caveat:hook] pending reminder build or publish failed\n");
32963
+ process.exit(0);
32905
32964
  }
32965
+ if (!result.ran) process.exit(0);
32906
32966
  process.exit(0);
32907
32967
  }
32908
32968
  function cleanupWorkerDir(workDir, root) {
@@ -32969,12 +33029,20 @@ function isStaleWorkerJobDir(path) {
32969
33029
  const stat = lstatSync(file2);
32970
33030
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
32971
33031
  if (!stat.isFile() || stat.isSymbolicLink() || !hasPrivateOwnership(stat, uid)) return false;
32972
- return isWorkerJob(JSON.parse(readFileSync5(file2, "utf-8")));
33032
+ return isKnownStaleWorkerJob(JSON.parse(readFileSync5(file2, "utf-8")));
32973
33033
  }
32974
33034
  function hasPrivateOwnership(stat, uid) {
32975
33035
  return process.platform === "win32" || (stat.mode & 63) === 0 && (uid === void 0 || stat.uid === uid);
32976
33036
  }
32977
33037
  function isWorkerJob(value) {
33038
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
33039
+ const job = value;
33040
+ const keys = Object.keys(job).sort();
33041
+ if (!(keys.length === 4 && keys.join(",") === "failureText,schemaVersion,sessionId,topicText") && !(keys.length === 5 && keys.join(",") === "additionalContext,failureText,schemaVersion,sessionId,topicText")) return false;
33042
+ return job.schemaVersion === "caveat-worker-job/v2" && typeof job.sessionId === "string" && typeof job.topicText === "string" && typeof job.failureText === "string" && (job.additionalContext === void 0 || job.additionalContext !== null && typeof job.additionalContext === "object" && !Array.isArray(job.additionalContext));
33043
+ }
33044
+ function isKnownStaleWorkerJob(value) {
33045
+ if (isWorkerJob(value)) return true;
32978
33046
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
32979
33047
  const job = value;
32980
33048
  const keys = Object.keys(job).sort();
@@ -33070,7 +33138,7 @@ function buildToolErrorReminder(job, hits) {
33070
33138
  const hasSidecarConfig = existsSync10(join12(projectRoot, ".codex-sidecar.yml"));
33071
33139
  if (mode === "auto" && !hasSidecarConfig) return base;
33072
33140
  const advisory = runCodexSidecarAdvisory({
33073
- searchText: job.searchText,
33141
+ searchText: job.failureText,
33074
33142
  limit: hits.length,
33075
33143
  projectRoot,
33076
33144
  prompt: [
@@ -33170,7 +33238,7 @@ async function runHook(name, arg) {
33170
33238
  const contexts = name === "stop" ? [] : drainForSession(sessionId);
33171
33239
  if (name === "user-prompt-submit") {
33172
33240
  const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
33173
- const hits = searchCaveatsFromTextSafely(prompt, "user_prompt");
33241
+ const hits = searchCaveatsSafely({ topicText: prompt, failureText: prompt, surface: "user_prompt" });
33174
33242
  if (hits.length > 0) {
33175
33243
  contexts.push(userPromptSubmitReminderText(hits));
33176
33244
  }
@@ -33198,7 +33266,8 @@ async function runHook(name, arg) {
33198
33266
  toolName: payload.tool_name ?? payload.toolName,
33199
33267
  failureKind
33200
33268
  });
33201
- spawnWorker({ sessionId, searchText: errText, ...additionalContext ? { additionalContext } : {} });
33269
+ const topicText = toolTopicText(payload);
33270
+ spawnWorker({ sessionId, topicText, failureText: errText, ...additionalContext ? { additionalContext } : {} });
33202
33271
  }
33203
33272
  process.exit(0);
33204
33273
  }
@@ -33231,7 +33300,11 @@ async function runHook(name, arg) {
33231
33300
  const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
33232
33301
  const signals = transcriptPath ? loadSignalsSafely(transcriptPath) : null;
33233
33302
  if (!signals || !hasAnyStruggleSignal(signals)) process.exit(0);
33234
- const related = searchCaveatsFromTextSafely(struggleSearchText(signals), "stop");
33303
+ const related = searchCaveatsSafely(signals.errorSnippets.map((failureText) => ({
33304
+ topicText: "",
33305
+ failureText,
33306
+ surface: "stop"
33307
+ })));
33235
33308
  queueStopForSession(sessionId, signals, related);
33236
33309
  process.exit(0);
33237
33310
  }
@@ -33285,8 +33358,10 @@ function buildContextSafely2() {
33285
33358
  return null;
33286
33359
  }
33287
33360
  }
33288
- function searchCaveatsFromTextSafely2(text, surface) {
33289
- if (!text) return [];
33361
+ function searchCaveatsSafely2(input) {
33362
+ const inputs = Array.isArray(input) ? input : [input];
33363
+ const queryForLog = inputs.map((item) => item.surface === "user_prompt" ? item.topicText || item.failureText : item.failureText).filter(Boolean).join("\n");
33364
+ if (inputs.length === 0 || inputs.every((item) => !item.topicText && !item.failureText)) return [];
33290
33365
  let db;
33291
33366
  let caveatHome;
33292
33367
  let hits;
@@ -33295,9 +33370,10 @@ function searchCaveatsFromTextSafely2(text, surface) {
33295
33370
  if (!ctx || !existsSync11(ctx.paths.dbPath)) return [];
33296
33371
  caveatHome = ctx.caveatHome;
33297
33372
  db = openDb({ path: ctx.paths.dbPath });
33298
- hits = findCaveatsForPrompt(db, text, {
33373
+ const searchOptions = {
33299
33374
  selfIdentity: defaultSelfIdentityTokens()
33300
- });
33375
+ };
33376
+ hits = inputs.length === 1 ? findCaveatsForHook(db, inputs[0], searchOptions) : findCaveatsForHookSegments(db, inputs, searchOptions);
33301
33377
  } catch (err) {
33302
33378
  const msg = err instanceof Error ? err.message : String(err);
33303
33379
  process.stderr.write(`[caveat:codex-hook] search error: ${msg}
@@ -33314,7 +33390,7 @@ function searchCaveatsFromTextSafely2(text, surface) {
33314
33390
  }
33315
33391
  } else {
33316
33392
  try {
33317
- logHookQueryMiss({ caveatHome, agent: "codex", surface, query: text });
33393
+ logHookQueryMiss({ caveatHome, agent: "codex", surface: inputs[0].surface, query: queryForLog });
33318
33394
  } catch (err) {
33319
33395
  const msg = err instanceof Error ? err.message : String(err);
33320
33396
  process.stderr.write(`[caveat:codex-hook] query log error: ${msg}
@@ -33443,15 +33519,17 @@ function buildCodexPostToolUseWorkerJob(payload) {
33443
33519
  const responseText = extractToolResponseText2(payload.tool_response ?? payload.toolResponse);
33444
33520
  const inputText = toolInputText(payload.tool_input);
33445
33521
  const transcriptOutput = transcriptPath && toolUseId ? transcriptToolOutput(transcriptPath, toolUseId) : null;
33446
- const searchText = [inputText, responseText, transcriptOutput ?? ""].map((s) => s.trim()).filter(Boolean).join("\n");
33522
+ const topicText = inputText.trim();
33523
+ const failureText = [responseText, transcriptOutput ?? ""].map((s) => s.trim()).filter(Boolean).join("\n");
33447
33524
  const knownError = isCodexToolError(payload);
33448
33525
  if (knownError) {
33449
- return { sessionId, searchText, knownError: true, transcriptPath, toolUseId };
33526
+ return { sessionId, topicText, failureText, knownError: true, transcriptPath, toolUseId };
33450
33527
  }
33451
- if (transcriptPath && toolUseId && isShellLikeTool(payload) && searchText) {
33528
+ if (transcriptPath && toolUseId && isShellLikeTool(payload) && (topicText || failureText)) {
33452
33529
  return {
33453
33530
  sessionId,
33454
- searchText,
33531
+ topicText,
33532
+ failureText,
33455
33533
  knownError: false,
33456
33534
  allowSymptomOnly: true,
33457
33535
  transcriptPath,
@@ -33468,13 +33546,19 @@ function codexContextOutput(text, eventName = "UserPromptSubmit") {
33468
33546
  }
33469
33547
  });
33470
33548
  }
33549
+ function codexPendingCleanupFailureText() {
33550
+ return "[caveat:codex-hook] pending reminder cleanup failed";
33551
+ }
33471
33552
  function drainForSession2(sessionId) {
33472
33553
  const ctx = buildContextSafely2();
33473
33554
  if (!ctx) return [];
33474
- return [
33475
- ...drainPendingReminders(ctx.caveatHome, sessionId),
33476
- ...drainGlobalPendingReminders(ctx.caveatHome)
33477
- ];
33555
+ const local = drainPendingRemindersDetailed(ctx.caveatHome, sessionId);
33556
+ const global = drainPendingRemindersDetailed(ctx.caveatHome, "_global");
33557
+ for (const _failure of [...local.cleanupFailures, ...global.cleanupFailures]) {
33558
+ process.stderr.write(`${codexPendingCleanupFailureText()}
33559
+ `);
33560
+ }
33561
+ return [...local.reminders, ...global.reminders];
33478
33562
  }
33479
33563
  function codexContextDedupeKey(text) {
33480
33564
  if (text.startsWith(CODEX_STOP_REMINDER_PREFIX)) return "codex-stop-reminder";
@@ -33493,7 +33577,7 @@ function compactCodexContexts(contexts) {
33493
33577
  }
33494
33578
  selected.reverse();
33495
33579
  const limited = selected.slice(-CODEX_MAX_CONTEXT_BLOCKS);
33496
- const omitted = contexts.filter((t) => t.trim().length > 0).length - limited.length;
33580
+ const omitted = selected.length - limited.length;
33497
33581
  if (omitted > 0) {
33498
33582
  limited.push(`[caveat] pending reminder ${omitted} \u4EF6\u3092\u91CD\u8907\u307E\u305F\u306F\u4E0A\u9650\u306B\u3088\u308A\u7701\u7565\u3057\u307E\u3057\u305F\u3002`);
33499
33583
  }
@@ -33536,8 +33620,20 @@ function queueStopForSession2(sessionId, signals, related) {
33536
33620
  if (!ctx) return;
33537
33621
  const key = stopSignalKey2(signals, related);
33538
33622
  if (wasStopReminderQueued2(ctx.caveatHome, sessionId, key)) return;
33623
+ let result;
33624
+ try {
33625
+ result = buildAndPublishPendingReminder(ctx.caveatHome, sessionId, buildPendingSemanticKey({
33626
+ agent: "codex",
33627
+ surface: "stop",
33628
+ refs: related,
33629
+ stopSignalDigest: key
33630
+ }), () => stopReminderText(signals, related));
33631
+ } catch {
33632
+ process.stderr.write("[caveat:codex-hook] pending reminder build or publish failed\n");
33633
+ return;
33634
+ }
33635
+ if (!result.ran) return;
33539
33636
  try {
33540
- appendPendingReminder(ctx.caveatHome, sessionId, stopReminderText(signals, related));
33541
33637
  markStopReminderQueued2(ctx.caveatHome, sessionId, key);
33542
33638
  } catch (err) {
33543
33639
  const msg = err instanceof Error ? err.message : String(err);
@@ -33555,8 +33651,8 @@ async function waitForTranscriptOutput(transcriptPath, toolUseId) {
33555
33651
  return null;
33556
33652
  }
33557
33653
  async function processCodexWorkerJob(job, opts = { waitForTranscript: true }) {
33558
- if (!job.searchText || !job.sessionId) return;
33559
- let searchText = job.searchText;
33654
+ if (!job.topicText && !job.failureText || !job.sessionId) return;
33655
+ let failureText = job.failureText;
33560
33656
  let knownError = job.knownError === true;
33561
33657
  if (opts.waitForTranscript && job.transcriptPath && job.toolUseId) {
33562
33658
  const transcriptOutput = await waitForTranscriptOutput(job.transcriptPath, job.toolUseId);
@@ -33566,18 +33662,30 @@ async function processCodexWorkerJob(job, opts = { waitForTranscript: true }) {
33566
33662
  if (exit2 === 0) return;
33567
33663
  knownError = true;
33568
33664
  }
33569
- searchText = [searchText, transcriptOutput].filter(Boolean).join("\n");
33665
+ failureText = [failureText, transcriptOutput].filter(Boolean).join("\n");
33570
33666
  }
33571
33667
  }
33572
33668
  if (!knownError && job.allowSymptomOnly !== true) return;
33573
- const hits = searchCaveatsFromTextSafely2(searchText, "tool_error");
33669
+ const hits = searchCaveatsSafely2({
33670
+ topicText: job.topicText,
33671
+ failureText,
33672
+ surface: "tool_error"
33673
+ });
33574
33674
  if (hits.length === 0) return;
33575
33675
  const ctx = buildContextSafely2();
33576
33676
  if (!ctx) return;
33677
+ let result;
33577
33678
  try {
33578
- appendPendingReminder(ctx.caveatHome, job.sessionId, toolErrorReminderText(hits));
33679
+ result = buildAndPublishPendingReminder(ctx.caveatHome, job.sessionId, buildPendingSemanticKey({
33680
+ agent: "codex",
33681
+ surface: "tool_error",
33682
+ refs: hits
33683
+ }), () => toolErrorReminderText(hits));
33579
33684
  } catch {
33685
+ process.stderr.write("[caveat:codex-hook] pending reminder build or publish failed\n");
33686
+ return;
33580
33687
  }
33688
+ if (!result.ran) return;
33581
33689
  }
33582
33690
  async function runCodexWorker(workFile) {
33583
33691
  let raw;
@@ -33649,7 +33757,7 @@ async function runCodexHook(name, arg) {
33649
33757
  if (name === "user-prompt-submit") {
33650
33758
  const contexts = sessionId ? drainForSession2(sessionId) : [];
33651
33759
  const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
33652
- const hits = searchCaveatsFromTextSafely2(prompt, "user_prompt");
33760
+ const hits = searchCaveatsSafely2({ topicText: prompt, failureText: prompt, surface: "user_prompt" });
33653
33761
  if (hits.length > 0) {
33654
33762
  contexts.push(userPromptSubmitReminderText(hits));
33655
33763
  }
@@ -33694,7 +33802,11 @@ async function runCodexHook(name, arg) {
33694
33802
  const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
33695
33803
  const signals = transcriptPath ? loadSignalsSafely2(transcriptPath) : null;
33696
33804
  if (!signals || !hasAnyStruggleSignal(signals)) process.exit(0);
33697
- const related = searchCaveatsFromTextSafely2(struggleSearchText(signals), "stop");
33805
+ const related = searchCaveatsSafely2(signals.errorSnippets.map((failureText) => ({
33806
+ topicText: "",
33807
+ failureText,
33808
+ surface: "stop"
33809
+ })));
33698
33810
  if (sessionId) queueStopForSession2(sessionId, signals, related);
33699
33811
  process.exit(0);
33700
33812
  }