@basou/core 0.23.0 → 0.24.0

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
@@ -21,69 +21,161 @@ function summarizeAdapterOutput(_stream, _raw) {
21
21
  throw new Error("adapter_output summary is not implemented in this release");
22
22
  }
23
23
 
24
+ // src/adapters/claude-code/ask-user-question.ts
25
+ function readString(value) {
26
+ return typeof value === "string" && value.length > 0 ? value : void 0;
27
+ }
28
+ function isObject(value) {
29
+ return typeof value === "object" && value !== null && !Array.isArray(value);
30
+ }
31
+ function toolUsesOf(record) {
32
+ const message = isObject(record.message) ? record.message : void 0;
33
+ const content = message !== void 0 && Array.isArray(message.content) ? message.content : [];
34
+ const result = [];
35
+ for (const item of content) {
36
+ if (isObject(item) && readString(item.type) === "tool_use") result.push(item);
37
+ }
38
+ return result;
39
+ }
40
+ function indexAskAnswers(records) {
41
+ const byId = /* @__PURE__ */ new Map();
42
+ for (const record of records) {
43
+ const result = record.toolUseResult;
44
+ if (!isObject(result)) continue;
45
+ const answers = result.answers;
46
+ if (!isObject(answers)) continue;
47
+ const message = isObject(record.message) ? record.message : void 0;
48
+ const content = message !== void 0 && Array.isArray(message.content) ? message.content : [];
49
+ for (const item of content) {
50
+ if (isObject(item) && readString(item.type) === "tool_result") {
51
+ const id = readString(item.tool_use_id);
52
+ if (id !== void 0) byId.set(id, answers);
53
+ }
54
+ }
55
+ }
56
+ return byId;
57
+ }
58
+ function readOfferedOptions(input) {
59
+ const byQuestion = /* @__PURE__ */ new Map();
60
+ const questions = Array.isArray(input.questions) ? input.questions : [];
61
+ for (const q of questions) {
62
+ if (!isObject(q)) continue;
63
+ const text = readString(q.question);
64
+ if (text === void 0) continue;
65
+ const labels = /* @__PURE__ */ new Set();
66
+ const options = Array.isArray(q.options) ? q.options : [];
67
+ for (const o of options) {
68
+ if (!isObject(o)) continue;
69
+ const label = readString(o.label);
70
+ if (label !== void 0) labels.add(label.trim());
71
+ }
72
+ byQuestion.set(text, labels);
73
+ }
74
+ return byQuestion;
75
+ }
76
+ function countUncapturedDecisionPoints(records) {
77
+ const answersById = indexAskAnswers(records);
78
+ if (answersById.size === 0) return 0;
79
+ let count = 0;
80
+ for (const record of records) {
81
+ if (readString(record.type) !== "assistant") continue;
82
+ for (const tool of toolUsesOf(record)) {
83
+ if (readString(tool.name) !== "AskUserQuestion") continue;
84
+ const useId = readString(tool.id);
85
+ const answers = useId !== void 0 ? answersById.get(useId) : void 0;
86
+ if (answers === void 0) continue;
87
+ const input = isObject(tool.input) ? tool.input : void 0;
88
+ if (input === void 0) continue;
89
+ const offeredByQuestion = readOfferedOptions(input);
90
+ for (const [question, answer] of Object.entries(answers)) {
91
+ if (question.length === 0) continue;
92
+ const trimmed = typeof answer === "string" ? answer.trim() : "";
93
+ if (trimmed.length === 0) continue;
94
+ const offered = offeredByQuestion.get(question);
95
+ if (offered === void 0 || !offered.has(trimmed)) count += 1;
96
+ }
97
+ }
98
+ }
99
+ return count;
100
+ }
101
+
24
102
  // src/adapters/claude-code/stop-hook.ts
25
- var DEFAULT_STOP_HOOK_MIN_ACTIONS = 5;
103
+ var DEFAULT_STOP_HOOK_MIN_EDITS = 2;
26
104
  var CAPTURE_COMMAND_PATTERN = /(?:^|[\n;&|(])\s*basou\s+(?:decision\s+(?:capture|record)|note)\b/;
27
105
  var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "NotebookEdit"]);
28
106
  function evaluateStopHook(input) {
29
- const minActions = input.minActions ?? DEFAULT_STOP_HOOK_MIN_ACTIONS;
107
+ const minEdits = input.minEdits ?? DEFAULT_STOP_HOOK_MIN_EDITS;
30
108
  if (input.stopHookActive) {
31
- return { kind: "silent", reason: "stop_hook_active", commandCount: 0, fileCount: 0 };
109
+ return {
110
+ kind: "silent",
111
+ reason: "stop_hook_active",
112
+ commandCount: 0,
113
+ fileCount: 0,
114
+ decisionPointCount: 0
115
+ };
32
116
  }
33
117
  let commandCount = 0;
34
118
  let fileCount = 0;
35
119
  let captured = false;
36
120
  for (const record of input.records) {
37
- if (readString(record.type) !== "assistant") continue;
38
- for (const tool of toolUsesOf(record)) {
39
- const name = readString(tool.name);
121
+ if (readString2(record.type) !== "assistant") continue;
122
+ for (const tool of toolUsesOf2(record)) {
123
+ const name = readString2(tool.name);
40
124
  if (name === void 0) continue;
41
125
  if (name === "Bash") {
42
126
  commandCount += 1;
43
- const input2 = isObject(tool.input) ? tool.input : void 0;
44
- const command = input2 !== void 0 ? readString(input2.command) : void 0;
127
+ const toolInput = isObject2(tool.input) ? tool.input : void 0;
128
+ const command = toolInput !== void 0 ? readString2(toolInput.command) : void 0;
45
129
  if (command !== void 0 && CAPTURE_COMMAND_PATTERN.test(command)) captured = true;
46
130
  } else if (FILE_EDIT_TOOLS.has(name)) {
47
131
  fileCount += 1;
48
132
  }
49
133
  }
50
134
  }
135
+ const decisionPointCount = countUncapturedDecisionPoints(input.records);
136
+ const counts = { commandCount, fileCount, decisionPointCount };
51
137
  if (captured) {
52
- return { kind: "silent", reason: "already_captured", commandCount, fileCount };
138
+ return { kind: "silent", reason: "already_captured", ...counts };
53
139
  }
54
- if (commandCount + fileCount < minActions) {
55
- return { kind: "silent", reason: "not_substantive", commandCount, fileCount };
140
+ const substantive = fileCount >= minEdits || decisionPointCount > 0;
141
+ if (!substantive) {
142
+ return { kind: "silent", reason: "not_substantive", ...counts };
56
143
  }
57
- return {
58
- kind: "nudge",
59
- additionalContext: renderNudge(commandCount, fileCount),
60
- commandCount,
61
- fileCount
62
- };
144
+ return { kind: "nudge", additionalContext: renderNudge(counts), ...counts };
63
145
  }
64
- function renderNudge(commandCount, fileCount) {
65
- const ran = `${commandCount} ${commandCount === 1 ? "command" : "commands"}`;
66
- const edited = `${fileCount} ${fileCount === 1 ? "file" : "files"}`;
146
+ function renderNudge(counts) {
147
+ const did = [];
148
+ if (counts.commandCount > 0) {
149
+ did.push(`ran ${counts.commandCount} ${counts.commandCount === 1 ? "command" : "commands"}`);
150
+ }
151
+ if (counts.fileCount > 0) {
152
+ did.push(`edited ${counts.fileCount} ${counts.fileCount === 1 ? "file" : "files"}`);
153
+ }
154
+ if (counts.decisionPointCount > 0) {
155
+ const n = counts.decisionPointCount;
156
+ did.push(`answered ${n} open-ended ${n === 1 ? "question" : "questions"}`);
157
+ }
158
+ const summary = did.length > 0 ? did.join(", ") : "did substantive work";
67
159
  return [
68
- `This session ran ${ran} and edited ${edited} but recorded no decisions or next step.`,
160
+ `This session ${summary} but recorded no decisions or next step.`,
69
161
  "If meaningful decisions were made (the chosen approach, rejected alternatives, and why) or there is a clear next step, capture them now so the next session can resume correctly:",
70
162
  ' - Decisions: run `basou decision capture` and pipe a JSON array (one object per decision; "title" required, plus optional rationale/alternatives/rejected_reason/linked_files; set "kind":"track" for an unfinished strategic direction).',
71
163
  ' - Next step: run `basou note "<what you would do next>"`.',
72
164
  "If nothing is worth capturing, just stop \u2014 do not invent decisions."
73
165
  ].join("\n");
74
166
  }
75
- function readString(value) {
167
+ function readString2(value) {
76
168
  return typeof value === "string" && value.length > 0 ? value : void 0;
77
169
  }
78
- function isObject(value) {
170
+ function isObject2(value) {
79
171
  return typeof value === "object" && value !== null && !Array.isArray(value);
80
172
  }
81
- function toolUsesOf(record) {
82
- const message = isObject(record.message) ? record.message : void 0;
173
+ function toolUsesOf2(record) {
174
+ const message = isObject2(record.message) ? record.message : void 0;
83
175
  const content = message !== void 0 && Array.isArray(message.content) ? message.content : [];
84
176
  const result = [];
85
177
  for (const item of content) {
86
- if (isObject(item) && readString(item.type) === "tool_use") result.push(item);
178
+ if (isObject2(item) && readString2(item.type) === "tool_use") result.push(item);
87
179
  }
88
180
  return result;
89
181
  }
@@ -195,21 +287,21 @@ function claudeTranscriptToImportPayload(records, options) {
195
287
  const engagementTsMs = [];
196
288
  const seenEngagementMessageIds = /* @__PURE__ */ new Set();
197
289
  for (const record of records) {
198
- const ts = readString2(record.timestamp);
290
+ const ts = readString3(record.timestamp);
199
291
  if (ts === void 0) continue;
200
292
  if (minTs === void 0 || Date.parse(ts) < Date.parse(minTs)) minTs = ts;
201
293
  if (maxTs === void 0 || Date.parse(ts) > Date.parse(maxTs)) maxTs = ts;
202
- if (workingDir === void 0) workingDir = readString2(record.cwd);
203
- if (claudeSessionId === void 0) claudeSessionId = readString2(record.sessionId);
294
+ if (workingDir === void 0) workingDir = readString3(record.cwd);
295
+ if (claudeSessionId === void 0) claudeSessionId = readString3(record.sessionId);
204
296
  if (record.isSidechain !== true) {
205
297
  const tsMs = Date.parse(ts);
206
298
  if (Number.isFinite(tsMs)) {
207
- const recType = readString2(record.type);
299
+ const recType = readString3(record.type);
208
300
  if (recType === "user") {
209
301
  if (isHumanUserMessage(record)) engagementTsMs.push(tsMs);
210
302
  } else if (recType === "assistant") {
211
- const msg = isObject2(record.message) ? record.message : void 0;
212
- const mid = msg !== void 0 ? readString2(msg.id) : void 0;
303
+ const msg = isObject3(record.message) ? record.message : void 0;
304
+ const mid = msg !== void 0 ? readString3(msg.id) : void 0;
213
305
  if (mid === void 0 || !seenEngagementMessageIds.has(mid)) {
214
306
  if (mid !== void 0) seenEngagementMessageIds.add(mid);
215
307
  engagementTsMs.push(tsMs);
@@ -217,11 +309,11 @@ function claudeTranscriptToImportPayload(records, options) {
217
309
  }
218
310
  }
219
311
  }
220
- if (readString2(record.type) !== "assistant") continue;
221
- const message = isObject2(record.message) ? record.message : void 0;
222
- const usage = message !== void 0 && isObject2(message.usage) ? message.usage : void 0;
312
+ if (readString3(record.type) !== "assistant") continue;
313
+ const message = isObject3(record.message) ? record.message : void 0;
314
+ const usage = message !== void 0 && isObject3(message.usage) ? message.usage : void 0;
223
315
  if (usage !== void 0) {
224
- const messageId = message !== void 0 ? readString2(message.id) : void 0;
316
+ const messageId = message !== void 0 ? readString3(message.id) : void 0;
225
317
  const alreadyCounted = messageId !== void 0 && seenMessageIds.has(messageId);
226
318
  if (!alreadyCounted) {
227
319
  if (messageId !== void 0) seenMessageIds.add(messageId);
@@ -230,20 +322,20 @@ function claudeTranscriptToImportPayload(records, options) {
230
322
  cachedInputTokens += readNonNegInt(usage.cache_read_input_tokens);
231
323
  }
232
324
  }
233
- const cwd = readString2(record.cwd) ?? workingDir ?? ".";
325
+ const cwd = readString3(record.cwd) ?? workingDir ?? ".";
234
326
  for (const item of toolUses(record)) {
235
- const name = readString2(item.name);
236
- const input = isObject2(item.input) ? item.input : void 0;
327
+ const name = readString3(item.name);
328
+ const input = isObject3(item.input) ? item.input : void 0;
237
329
  if (input === void 0) continue;
238
330
  if (name === "Bash") {
239
- const command = readString2(input.command);
331
+ const command = readString3(input.command);
240
332
  if (command !== void 0) {
241
333
  derived.push(commandExecutedEvent(ts, placeholderSessionId, command, cwd));
242
334
  }
243
335
  continue;
244
336
  }
245
337
  if (name === "AskUserQuestion") {
246
- const useId = readString2(item.id);
338
+ const useId = readString3(item.id);
247
339
  const answers = useId !== void 0 ? askAnswers.get(useId) : void 0;
248
340
  if (answers !== void 0) {
249
341
  const offeredByQuestion = readOfferedOptions(input);
@@ -261,7 +353,7 @@ function claudeTranscriptToImportPayload(records, options) {
261
353
  continue;
262
354
  }
263
355
  if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
264
- const path2 = readString2(input.file_path) ?? readString2(input.notebook_path);
356
+ const path2 = readString3(input.file_path) ?? readString3(input.notebook_path);
265
357
  if (path2 !== void 0) {
266
358
  const changeType = name === "Write" ? "added" : "modified";
267
359
  relatedFiles.add(path2);
@@ -363,76 +455,40 @@ function decisionRecordedEvent(occurredAt, sessionId, title) {
363
455
  title
364
456
  };
365
457
  }
366
- function readString2(value) {
458
+ function readString3(value) {
367
459
  return typeof value === "string" && value.length > 0 ? value : void 0;
368
460
  }
369
461
  function readNonNegInt(value) {
370
462
  return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : 0;
371
463
  }
372
- function isObject2(value) {
464
+ function isObject3(value) {
373
465
  return typeof value === "object" && value !== null && !Array.isArray(value);
374
466
  }
375
467
  function isHumanUserMessage(record) {
376
- const message = isObject2(record.message) ? record.message : void 0;
468
+ const message = isObject3(record.message) ? record.message : void 0;
377
469
  if (message === void 0) return false;
378
470
  const content = message.content;
379
471
  if (typeof content === "string") return content.length > 0;
380
472
  if (Array.isArray(content)) {
381
473
  return content.some((block) => {
382
- if (!isObject2(block)) return false;
383
- const type = readString2(block.type);
474
+ if (!isObject3(block)) return false;
475
+ const type = readString3(block.type);
384
476
  return type !== void 0 && type !== "tool_result";
385
477
  });
386
478
  }
387
479
  return false;
388
480
  }
389
481
  function toolUses(record) {
390
- const message = isObject2(record.message) ? record.message : void 0;
482
+ const message = isObject3(record.message) ? record.message : void 0;
391
483
  const content = message !== void 0 && Array.isArray(message.content) ? message.content : [];
392
484
  const result = [];
393
485
  for (const item of content) {
394
- if (isObject2(item) && readString2(item.type) === "tool_use") {
486
+ if (isObject3(item) && readString3(item.type) === "tool_use") {
395
487
  result.push(item);
396
488
  }
397
489
  }
398
490
  return result;
399
491
  }
400
- function indexAskAnswers(records) {
401
- const byId = /* @__PURE__ */ new Map();
402
- for (const record of records) {
403
- const result = record.toolUseResult;
404
- if (!isObject2(result)) continue;
405
- const answers = result.answers;
406
- if (!isObject2(answers)) continue;
407
- const message = isObject2(record.message) ? record.message : void 0;
408
- const content = message !== void 0 && Array.isArray(message.content) ? message.content : [];
409
- for (const item of content) {
410
- if (isObject2(item) && readString2(item.type) === "tool_result") {
411
- const id = readString2(item.tool_use_id);
412
- if (id !== void 0) byId.set(id, answers);
413
- }
414
- }
415
- }
416
- return byId;
417
- }
418
- function readOfferedOptions(input) {
419
- const byQuestion = /* @__PURE__ */ new Map();
420
- const questions = Array.isArray(input.questions) ? input.questions : [];
421
- for (const q of questions) {
422
- if (!isObject2(q)) continue;
423
- const text = readString2(q.question);
424
- if (text === void 0) continue;
425
- const labels = /* @__PURE__ */ new Set();
426
- const options = Array.isArray(q.options) ? q.options : [];
427
- for (const o of options) {
428
- if (!isObject2(o)) continue;
429
- const label = readString2(o.label);
430
- if (label !== void 0) labels.add(label.trim());
431
- }
432
- byQuestion.set(text, labels);
433
- }
434
- return byQuestion;
435
- }
436
492
 
437
493
  // src/adapters/codex/rollout-importer.ts
438
494
  var CODEX_IMPORT_SOURCE = "codex-import";
@@ -450,31 +506,31 @@ function codexRolloutToImportPayload(records, options) {
450
506
  const completions = [];
451
507
  const completedTurnIds = /* @__PURE__ */ new Set();
452
508
  for (const record of records) {
453
- const ts = readString3(record.timestamp);
509
+ const ts = readString4(record.timestamp);
454
510
  if (ts === void 0) continue;
455
511
  if (minTs === void 0 || Date.parse(ts) < Date.parse(minTs)) minTs = ts;
456
512
  if (maxTs === void 0 || Date.parse(ts) > Date.parse(maxTs)) maxTs = ts;
457
- const payload2 = isObject3(record.payload) ? record.payload : void 0;
513
+ const payload2 = isObject4(record.payload) ? record.payload : void 0;
458
514
  if (payload2 === void 0) continue;
459
- if (readString3(record.type) === "session_meta") {
460
- if (workingDir === void 0) workingDir = readString3(payload2.cwd);
461
- if (codexSessionId === void 0) codexSessionId = readString3(payload2.id);
515
+ if (readString4(record.type) === "session_meta") {
516
+ if (workingDir === void 0) workingDir = readString4(payload2.cwd);
517
+ if (codexSessionId === void 0) codexSessionId = readString4(payload2.id);
462
518
  continue;
463
519
  }
464
- if (readString3(record.type) === "event_msg" && readString3(payload2.type) === "token_count") {
465
- const info = isObject3(payload2.info) ? payload2.info : void 0;
466
- const totals = info !== void 0 && isObject3(info.total_token_usage) ? info.total_token_usage : void 0;
520
+ if (readString4(record.type) === "event_msg" && readString4(payload2.type) === "token_count") {
521
+ const info = isObject4(payload2.info) ? payload2.info : void 0;
522
+ const totals = info !== void 0 && isObject4(info.total_token_usage) ? info.total_token_usage : void 0;
467
523
  if (totals !== void 0) lastTokenTotals = totals;
468
524
  continue;
469
525
  }
470
- if (readString3(record.type) === "event_msg") {
471
- const pt = readString3(payload2.type);
526
+ if (readString4(record.type) === "event_msg") {
527
+ const pt = readString4(payload2.type);
472
528
  if (pt === "user_message" || pt === "agent_message" || pt === "task_started" || pt === "task_complete") {
473
529
  const tsMs = Date.parse(ts);
474
530
  if (Number.isFinite(tsMs)) engagementTsMs.push(tsMs);
475
531
  }
476
532
  if (pt === "task_complete") {
477
- const turnId = readString3(payload2.turn_id);
533
+ const turnId = readString4(payload2.turn_id);
478
534
  if (turnId === void 0 || !completedTurnIds.has(turnId)) {
479
535
  if (turnId !== void 0) completedTurnIds.add(turnId);
480
536
  completions.push({
@@ -485,9 +541,9 @@ function codexRolloutToImportPayload(records, options) {
485
541
  }
486
542
  continue;
487
543
  }
488
- if (readString3(record.type) !== "response_item") continue;
489
- if (readString3(payload2.type) !== "function_call") continue;
490
- if (readString3(payload2.name) !== "exec_command") continue;
544
+ if (readString4(record.type) !== "response_item") continue;
545
+ if (readString4(payload2.type) !== "function_call") continue;
546
+ if (readString4(payload2.name) !== "exec_command") continue;
491
547
  const command = readExecCommand(payload2.arguments);
492
548
  if (command === void 0) continue;
493
549
  const cwd = command.workdir ?? workingDir ?? ".";
@@ -598,17 +654,17 @@ function commandExecutedEvent2(occurredAt, sessionId, command, cwd, outcome) {
598
654
  duration_ms: outcome.durationMs
599
655
  };
600
656
  }
601
- function readString3(value) {
657
+ function readString4(value) {
602
658
  return typeof value === "string" && value.length > 0 ? value : void 0;
603
659
  }
604
660
  function readNonNegInt2(value) {
605
661
  return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : 0;
606
662
  }
607
- function isObject3(value) {
663
+ function isObject4(value) {
608
664
  return typeof value === "object" && value !== null && !Array.isArray(value);
609
665
  }
610
666
  function readExecCommand(value) {
611
- const raw = readString3(value);
667
+ const raw = readString4(value);
612
668
  if (raw === void 0) return void 0;
613
669
  let parsed;
614
670
  try {
@@ -616,19 +672,19 @@ function readExecCommand(value) {
616
672
  } catch {
617
673
  return void 0;
618
674
  }
619
- if (!isObject3(parsed)) return void 0;
620
- const cmd = readString3(parsed.cmd);
675
+ if (!isObject4(parsed)) return void 0;
676
+ const cmd = readString4(parsed.cmd);
621
677
  if (cmd === void 0) return void 0;
622
- return { cmd, workdir: readString3(parsed.workdir) };
678
+ return { cmd, workdir: readString4(parsed.workdir) };
623
679
  }
624
680
  function readCallId(value, outputs) {
625
- const callId = readString3(value);
681
+ const callId = readString4(value);
626
682
  return callId !== void 0 ? outputs.get(callId) : void 0;
627
683
  }
628
684
  function turnIntervalFromComplete(endTs, payload, startMsByTurnId) {
629
685
  const endMs = Date.parse(endTs);
630
686
  if (!Number.isFinite(endMs)) return void 0;
631
- const turnId = readString3(payload.turn_id);
687
+ const turnId = readString4(payload.turn_id);
632
688
  const indexedStart = turnId !== void 0 ? startMsByTurnId.get(turnId) : void 0;
633
689
  const durationMs = readNonNegInt2(payload.duration_ms);
634
690
  const startMs = indexedStart !== void 0 ? indexedStart : durationMs > 0 ? endMs - durationMs : void 0;
@@ -638,11 +694,11 @@ function turnIntervalFromComplete(endTs, payload, startMsByTurnId) {
638
694
  function indexTaskStarts(records) {
639
695
  const byTurnId = /* @__PURE__ */ new Map();
640
696
  for (const record of records) {
641
- if (readString3(record.type) !== "event_msg") continue;
642
- const payload = isObject3(record.payload) ? record.payload : void 0;
643
- if (payload === void 0 || readString3(payload.type) !== "task_started") continue;
644
- const turnId = readString3(payload.turn_id);
645
- const startMs = Date.parse(readString3(record.timestamp) ?? "");
697
+ if (readString4(record.type) !== "event_msg") continue;
698
+ const payload = isObject4(record.payload) ? record.payload : void 0;
699
+ if (payload === void 0 || readString4(payload.type) !== "task_started") continue;
700
+ const turnId = readString4(payload.turn_id);
701
+ const startMs = Date.parse(readString4(record.timestamp) ?? "");
646
702
  if (turnId !== void 0 && Number.isFinite(startMs) && !byTurnId.has(turnId)) {
647
703
  byTurnId.set(turnId, startMs);
648
704
  }
@@ -664,12 +720,12 @@ function parseWallTimeMs(output) {
664
720
  function indexOutputs(records) {
665
721
  const byId = /* @__PURE__ */ new Map();
666
722
  for (const record of records) {
667
- if (readString3(record.type) !== "response_item") continue;
668
- const payload = isObject3(record.payload) ? record.payload : void 0;
723
+ if (readString4(record.type) !== "response_item") continue;
724
+ const payload = isObject4(record.payload) ? record.payload : void 0;
669
725
  if (payload === void 0) continue;
670
- if (readString3(payload.type) !== "function_call_output") continue;
671
- const callId = readString3(payload.call_id);
672
- const output = readString3(payload.output);
726
+ if (readString4(payload.type) !== "function_call_output") continue;
727
+ const callId = readString4(payload.call_id);
728
+ const output = readString4(payload.output);
673
729
  if (callId !== void 0 && output !== void 0) byId.set(callId, output);
674
730
  }
675
731
  return byId;
@@ -5120,12 +5176,11 @@ function stalenessBanner(staleness) {
5120
5176
  if (staleness === null) return [];
5121
5177
  if ((staleness.unverifiableSessions ?? 0) > 0) {
5122
5178
  return [
5123
- `> \u26A0\uFE0F **\u6700\u65B0\u3067\u306F\u306A\u3044\u53EF\u80FD\u6027** \u2014 \u5909\u5316\u3057\u305F\u304C\u5B89\u5168\u306B\u53D6\u308A\u8FBC\u3081\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.unverifiableSessions} \u4EF6\u3042\u308A\u307E\u3059\u3002\u7740\u624B\u524D\u306B \`basou verify\` / \`basou refresh --force\`(\u8A73\u7D30\u306F\u672B\u5C3E\u300C\u3053\u308C\u306F\u6700\u65B0\u304B\u300D)\u3002`
5179
+ `> \u26A0\uFE0F **\u518D\u53D6\u308A\u8FBC\u307F\u304C\u5FC5\u8981** \u2014 native \u30ED\u30B0\u304C\u5909\u5316\u3057\u305F\u304C\u901A\u5E38\u306E refresh \u3067\u306F\u53D6\u308A\u8FBC\u3081\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.unverifiableSessions} \u4EF6\u3042\u308A\u307E\u3059\u3002\`basou refresh --force\` \u3067\u518D\u53D6\u308A\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044(\u8A73\u7D30\u306F\u672B\u5C3E\u300C\u3053\u308C\u306F\u6700\u65B0\u304B\u300D)\u3002`
5124
5180
  ];
5125
5181
  }
5126
- if (staleness.newSessions > 0 || staleness.updatedSessions > 0) {
5127
- const parts = [];
5128
- if (staleness.newSessions > 0) parts.push(`\u65B0\u898F ${staleness.newSessions} \u4EF6`);
5182
+ if (staleness.newSessions > 0) {
5183
+ const parts = [`\u65B0\u898F ${staleness.newSessions} \u4EF6`];
5129
5184
  if (staleness.updatedSessions > 0) parts.push(`\u66F4\u65B0 ${staleness.updatedSessions} \u4EF6`);
5130
5185
  return [
5131
5186
  `> \u26A0\uFE0F **\u53E4\u3044\u3067\u3059\uFF08\u672A\u53D6\u308A\u8FBC\u307F ${parts.join("\u30FB")}\uFF09** \u2014 \u7740\u624B\u524D\u306B\u5FC5\u305A \`basou refresh\` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044(\u8A73\u7D30\u306F\u672B\u5C3E\u300C\u3053\u308C\u306F\u6700\u65B0\u304B\u300D)\u3002`
@@ -5136,19 +5191,30 @@ function stalenessBanner(staleness) {
5136
5191
  function freshnessVerdict(summary, staleness, now) {
5137
5192
  if (staleness !== null && (staleness.unverifiableSessions ?? 0) > 0) {
5138
5193
  return [
5139
- `\u26A0\uFE0F \u6700\u65B0\u304B\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002\u5909\u5316\u3057\u305F\u304C\u5B89\u5168\u306B\u53D6\u308A\u8FBC\u3081\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.unverifiableSessions} \u4EF6\u3042\u308A\u307E\u3059(\u30CF\u30C3\u30B7\u30E5\u30C1\u30A7\u30FC\u30F3\u7834\u640D\u30FB\u975E\u8FFD\u8A18\u5909\u66F4\u306A\u3069)\u3002`,
5140
- "`basou verify` \u3067\u78BA\u8A8D\u3057\u3001`basou refresh --force` \u3067\u518D\u53D6\u308A\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
5194
+ `\u26A0\uFE0F native \u30ED\u30B0\u304C\u5909\u5316\u3057\u307E\u3057\u305F\u304C\u3001\u901A\u5E38\u306E \`basou refresh\` \u3067\u306F\u5B89\u5168\u306B\u518D\u53D6\u308A\u8FBC\u307F\u3067\u304D\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.unverifiableSessions} \u4EF6\u3042\u308A\u307E\u3059(\u975E\u8FFD\u8A18\u5909\u66F4\u30FB\u524D\u30C1\u30A7\u30FC\u30F3\u4E0D\u6574\u5408\u306A\u3069)\u3002`,
5195
+ "`basou refresh --force` \u3067\u518D\u53D6\u308A\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002(`basou verify` \u306F\u5225\u7269=\u53D6\u308A\u8FBC\u307F\u6E08\u307F\u30C7\u30FC\u30BF\u306E\u6539\u7AC4/\u7834\u640D\u691C\u67FB\u3067\u3001\u30D8\u30C3\u30C0\u306E suspect \u3068\u306F\u5225\u8EF8\u3067\u3059\u3002verify \u304C clean \u3067\u3082\u672A\u53D6\u308A\u8FBC\u307F\u306F\u6B8B\u308A\u5F97\u307E\u3059\u3002)"
5141
5196
  ];
5142
5197
  }
5143
- if (staleness !== null && (staleness.newSessions > 0 || staleness.updatedSessions > 0)) {
5144
- const parts = [];
5145
- if (staleness.newSessions > 0) parts.push(`\u65B0\u898F ${staleness.newSessions} \u4EF6`);
5198
+ if (staleness !== null && staleness.newSessions > 0) {
5199
+ const parts = [`\u65B0\u898F ${staleness.newSessions} \u4EF6`];
5146
5200
  if (staleness.updatedSessions > 0) parts.push(`\u66F4\u65B0 ${staleness.updatedSessions} \u4EF6`);
5147
5201
  return [
5148
5202
  `\u26A0\uFE0F \u53E4\u3044\u3067\u3059\u3002\u6700\u5F8C\u306E\u53D6\u308A\u8FBC\u307F\u4EE5\u964D\u306B\u672A\u53D6\u308A\u8FBC\u307F\u306E\u4F5C\u696D\u304C\u3042\u308A\u307E\u3059(${parts.join("\u30FB")})\u3002`,
5149
5203
  "\u7740\u624B\u524D\u306B\u5FC5\u305A `basou refresh` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
5150
5204
  ];
5151
5205
  }
5206
+ if (staleness !== null && staleness.updatedSessions > 0) {
5207
+ const lines2 = [
5208
+ `\u26A0\uFE0F \u66F4\u65B0\u3055\u308C\u305F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.updatedSessions} \u4EF6\u3042\u308A\u307E\u3059\u3002\`basou refresh\` \u3067\u53D6\u308A\u8FBC\u3081\u307E\u3059\u3002`,
5209
+ "(\u9032\u884C\u4E2D\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u3042\u308B\u5834\u5408\u3001\u305D\u308C\u81EA\u8EAB\u306F\u53D6\u308A\u8FBC\u307F\u5F8C\u3082\u5897\u3048\u7D9A\u3051\u308B\u305F\u3081\u6B8B\u308A\u307E\u3059\uFF1D\u6B63\u5E38\u3067\u3059\u3002)"
5210
+ ];
5211
+ if (summary.suspects.length > 0) {
5212
+ lines2.push(
5213
+ `\u307E\u305F\u8981\u6CE8\u610F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${summary.suspects.length} \u4EF6\u3042\u308A\u307E\u3059(\u4E0A\u8A18\u300C\u8981\u6CE8\u610F session\u300D\u53C2\u7167)\u3002`
5214
+ );
5215
+ }
5216
+ return lines2;
5217
+ }
5152
5218
  if (summary.freshness.newestStartedAt === null) {
5153
5219
  return [
5154
5220
  "\u2139\uFE0F \u307E\u3060\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093\u3002",
@@ -7529,7 +7595,7 @@ export {
7529
7595
  CLAUDE_IMPORT_SOURCE,
7530
7596
  CODEX_IMPORT_SOURCE,
7531
7597
  ChildProcessRunner,
7532
- DEFAULT_STOP_HOOK_MIN_ACTIONS,
7598
+ DEFAULT_STOP_HOOK_MIN_EDITS,
7533
7599
  DecisionIdSchema,
7534
7600
  EventIdSchema,
7535
7601
  EventSchema,