@scotthuang/agent-knock-knock 0.11.4 → 0.11.6

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.
@@ -1,4 +1,4 @@
1
- import type { ActiveTerminalProcess, TerminalAgentAdapter, TerminalApprovalInspection, TerminalDurableCompletionRequest, TerminalProcessSnapshot, TerminalScreenInspection, TerminalScreenInspectionOptions, TerminalThreadLifecycleCapabilities, TerminalThreadLifecycleObservation, TerminalThreadLifecycleObservationRequest, TerminalThreadLifecycleOperation, TerminalThreadLifecyclePlan } from "./terminal-agent-adapter.js";
1
+ import type { ActiveTerminalProcess, TerminalAgentAdapter, TerminalApprovalInspection, TerminalDurableCompletionRequest, TerminalNativeInspectionCapabilities, TerminalNativeInspectionObservation, TerminalNativeInspectionObservationRequest, TerminalNativeInspectionOperation, TerminalNativeInspectionPlan, TerminalProcessSnapshot, TerminalScreenInspection, TerminalScreenInspectionOptions, TerminalThreadLifecycleCapabilities, TerminalThreadLifecycleObservation, TerminalThreadLifecycleObservationRequest, TerminalThreadLifecycleOperation, TerminalThreadLifecyclePlan } from "./terminal-agent-adapter.js";
2
2
  export type ClaudeProcessKind = "claude_cli";
3
3
  /** A row returned by `claude agents --json --all`. Unknown fields are intentionally ignored. */
4
4
  export interface ClaudeAgentRow {
@@ -8,6 +8,7 @@ export interface ClaudeAgentRow {
8
8
  sessionId?: string;
9
9
  startedAt?: number;
10
10
  status?: string;
11
+ waitingFor?: string;
11
12
  }
12
13
  export interface CreateClaudeTerminalAgentAdapterOptions {
13
14
  /**
@@ -39,7 +40,18 @@ export interface ClaudePendingApprovalEvidence {
39
40
  evidenceFingerprint: string;
40
41
  observedEndOffsetBytes: number;
41
42
  }
43
+ /**
44
+ * On each verified Claude TUI profile, the exact `/status` composer and its
45
+ * closed completion list remained byte-stable across an 80 ms capture interval
46
+ * and one subsequent Enter opened the Status panel. This is a Claude-local
47
+ * single-line boundary; it deliberately does not reuse Codex's 121 ms burst
48
+ * paste boundary.
49
+ */
50
+ export declare const CLAUDE_NATIVE_INSPECTION_COMPOSER_STABLE_MS = 80;
42
51
  export declare function createClaudeTerminalAgentAdapter(options?: CreateClaudeTerminalAgentAdapterOptions): TerminalAgentAdapter<ClaudeProcessKind>;
52
+ export declare function probeClaudeNativeInspection(agentVersion: string | undefined): TerminalNativeInspectionCapabilities;
53
+ export declare function planClaudeNativeInspection(operation: TerminalNativeInspectionOperation, capabilities: TerminalNativeInspectionCapabilities): TerminalNativeInspectionPlan;
54
+ export declare function observeClaudeNativeInspection(request: TerminalNativeInspectionObservationRequest): TerminalNativeInspectionObservation;
43
55
  export declare function probeClaudeThreadLifecycle(agentVersion: string | undefined): TerminalThreadLifecycleCapabilities;
44
56
  export declare function planClaudeThreadLifecycle(operation: TerminalThreadLifecycleOperation, capabilities: TerminalThreadLifecycleCapabilities): TerminalThreadLifecyclePlan;
45
57
  export declare function observeClaudeThreadLifecycle(request: TerminalThreadLifecycleObservationRequest, legacyOperation?: undefined, fallbackAgentRows?: readonly ClaudeAgentRow[]): TerminalThreadLifecycleObservation;
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import path from "node:path";
3
+ import { claudeLifecycleBehaviorProfile, claudeLifecycleCompatibilityProfile, claudeNativeInspectionBehaviorProfile, profiledClaudeNativeStatusPanelFields } from "./claude-lifecycle-compatibility.js";
3
4
  import { redactString } from "./runtime-log.js";
4
5
  import { isExactNativeThreadId } from "./managed-session.js";
5
6
  const CLAUDE_SUBCOMMANDS = new Set([
@@ -73,8 +74,31 @@ const CLAUDE_EXCERPT_LINES = 80;
73
74
  const CLAUDE_PERMISSION_DETAIL_LENGTH = 600;
74
75
  const CLAUDE_AUTO_APPROVAL_COMMAND_LENGTH = 2000;
75
76
  const CLAUDE_NATIVE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/u;
76
- const CLAUDE_LIFECYCLE_VERSION = "2.1.218";
77
- const CLAUDE_LIFECYCLE_PROFILE = "claude-code-2.1.218";
77
+ /**
78
+ * On each verified Claude TUI profile, the exact `/status` composer and its
79
+ * closed completion list remained byte-stable across an 80 ms capture interval
80
+ * and one subsequent Enter opened the Status panel. This is a Claude-local
81
+ * single-line boundary; it deliberately does not reuse Codex's 121 ms burst
82
+ * paste boundary.
83
+ */
84
+ export const CLAUDE_NATIVE_INSPECTION_COMPOSER_STABLE_MS = 80;
85
+ const CLAUDE_STATUS_PANEL_HEADER = /^\s*Settings\s+Status\s+Config\s+Usage\s+Stats\s*$/u;
86
+ const CLAUDE_STATUS_PANEL_FOOTER = /^\s*Esc to cancel\s*$/u;
87
+ const CLAUDE_STATUS_PANEL_DIVIDER = /^\s*[─━]{8,}\s*$/u;
88
+ const CLAUDE_STATUS_PANEL_FIELD = /^\s{0,4}([^:]{1,64}):\s+(.{1,1024})\s*$/u;
89
+ const CLAUDE_STATUS_PANEL_MAX_SCAN_LINES = 512;
90
+ const CLAUDE_STATUS_PANEL_MAX_LINES = 48;
91
+ const CLAUDE_STATUS_PANEL_MAX_REGION_LENGTH = 8_192;
92
+ const CLAUDE_STATUS_PANEL_MAX_FIELDS = 24;
93
+ const CLAUDE_STATUS_PANEL_MAX_FIELD_VALUE_LENGTH = 512;
94
+ const CLAUDE_STATUS_PANEL_MAX_EXCERPT_LENGTH = 4_000;
95
+ const CLAUDE_STATUS_PANEL_MAX_EVIDENCE_ENTRIES = 24;
96
+ const PROFILED_CLAUDE_STATUS_PANEL_FIELDS = new Set(profiledClaudeNativeStatusPanelFields());
97
+ const CLAUDE_STATUS_PANEL_SENSITIVE_FIELDS = new Set([
98
+ "Session name",
99
+ "Auth token"
100
+ ]);
101
+ const NATIVE_INSPECTION_FINGERPRINT = /^sha256:[0-9a-f]{64}$/u;
78
102
  const ANSI_ESCAPE_PATTERN = /\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/gu;
79
103
  export function createClaudeTerminalAgentAdapter(options = {}) {
80
104
  const agentRows = options.agentRows ?? [];
@@ -106,6 +130,9 @@ export function createClaudeTerminalAgentAdapter(options = {}) {
106
130
  probeThreadLifecycle: probeClaudeThreadLifecycle,
107
131
  planThreadLifecycle: planClaudeThreadLifecycle,
108
132
  observeThreadLifecycle: lifecycleObserver,
133
+ probeNativeInspection: probeClaudeNativeInspection,
134
+ planNativeInspection: planClaudeNativeInspection,
135
+ observeNativeInspection: observeClaudeNativeInspection,
109
136
  ...(durableCompletion
110
137
  ? {
111
138
  async detectDurableCompletion(request) {
@@ -115,6 +142,192 @@ export function createClaudeTerminalAgentAdapter(options = {}) {
115
142
  : {})
116
143
  };
117
144
  }
145
+ export function probeClaudeNativeInspection(agentVersion) {
146
+ if (!agentVersion) {
147
+ return {
148
+ status: "unknown",
149
+ statusInspection: false,
150
+ reason: "the running Claude Code version could not be verified"
151
+ };
152
+ }
153
+ const behaviorProfile = claudeNativeInspectionBehaviorProfile(agentVersion);
154
+ const supported = behaviorProfile !== undefined;
155
+ return {
156
+ status: supported ? "supported" : "unsupported",
157
+ agentVersion,
158
+ behaviorProfile,
159
+ statusInspection: supported,
160
+ reason: supported
161
+ ? "Claude Code /status native inspection is supported by the verified version"
162
+ : "this exact Claude Code version has no AKK native inspection behavior profile"
163
+ };
164
+ }
165
+ export function planClaudeNativeInspection(operation, capabilities) {
166
+ const profile = claudeLifecycleCompatibilityProfile(capabilities.agentVersion);
167
+ if (operation.kind !== "status" ||
168
+ capabilities.status !== "supported" ||
169
+ capabilities.statusInspection !== true ||
170
+ !profile ||
171
+ capabilities.behaviorProfile !== profile.nativeInspectionBehaviorProfile) {
172
+ throw new Error(capabilities.reason);
173
+ }
174
+ return {
175
+ operation,
176
+ behaviorProfile: profile.nativeInspectionBehaviorProfile,
177
+ command: "/status",
178
+ effect: "read_only",
179
+ requiresIdle: true,
180
+ composer: {
181
+ kind: "exact",
182
+ minimumStableMs: profile.nativeInspectionComposerStableMs,
183
+ maximumSettleMs: profile.nativeInspectionComposerSettleTimeoutMs
184
+ },
185
+ expectedResult: {
186
+ kind: "native_status",
187
+ presentation: "modal",
188
+ dismissal: {
189
+ keys: ["Escape"],
190
+ expected: "idle_empty_composer"
191
+ }
192
+ }
193
+ };
194
+ }
195
+ export function observeClaudeNativeInspection(request) {
196
+ const rawScreen = request.screen ?? "";
197
+ const rawScreenFingerprint = fingerprintClaudeNativeInspection(rawScreen);
198
+ const screen = normalizeClaudeNativeInspectionScreen(rawScreen);
199
+ const screenFingerprint = fingerprintClaudeNativeInspection(screen);
200
+ const inventory = claudeStatusPanelEvidenceInventory(screen);
201
+ if (inventory.status === "ambiguous") {
202
+ return {
203
+ status: "ambiguous",
204
+ screenFingerprint,
205
+ evidenceInventory: inventory.entries,
206
+ reason: inventory.reason
207
+ };
208
+ }
209
+ const baselineError = validateClaudeStatusEvidenceInventory(request.preEnterEvidenceInventory);
210
+ if (baselineError) {
211
+ return {
212
+ status: "ambiguous",
213
+ screenFingerprint,
214
+ evidenceInventory: inventory.entries,
215
+ reason: baselineError
216
+ };
217
+ }
218
+ if (request.previousScreenFingerprint !== undefined &&
219
+ (request.previousScreenFingerprint === rawScreenFingerprint ||
220
+ request.previousScreenFingerprint === screenFingerprint)) {
221
+ return {
222
+ status: "stale",
223
+ screenFingerprint,
224
+ evidenceInventory: inventory.entries,
225
+ reason: "the Claude screen did not change after the native inspection command"
226
+ };
227
+ }
228
+ const parsed = parseCurrentClaudeStatusPanel(screen);
229
+ if (parsed.status !== "observed") {
230
+ return {
231
+ status: parsed.status,
232
+ screenFingerprint,
233
+ evidenceInventory: inventory.entries,
234
+ reason: parsed.reason
235
+ };
236
+ }
237
+ const evidenceFingerprint = fingerprintClaudeNativeInspection(parsed.region);
238
+ if (request.preEnterEvidenceInventory !== undefined) {
239
+ const priorOccurrences = request.preEnterEvidenceInventory.find((entry) => entry.evidenceFingerprint === evidenceFingerprint)?.occurrenceCount ?? 0;
240
+ const currentOccurrences = inventory.entries.find((entry) => entry.evidenceFingerprint === evidenceFingerprint)?.occurrenceCount ?? 0;
241
+ if (currentOccurrences <= priorOccurrences) {
242
+ return {
243
+ status: "stale",
244
+ nativeThreadId: parsed.nativeThreadId,
245
+ observedAgentVersion: parsed.agentVersion,
246
+ evidence: "claude_status_panel",
247
+ evidenceFingerprint,
248
+ screenFingerprint,
249
+ evidenceInventory: inventory.entries,
250
+ reason: "the Claude Status panel did not add fresh exact evidence after Enter"
251
+ };
252
+ }
253
+ }
254
+ if (!claudeNativeInspectionBehaviorProfile(parsed.agentVersion)) {
255
+ return {
256
+ status: "mismatch",
257
+ nativeThreadId: parsed.nativeThreadId,
258
+ observedAgentVersion: parsed.agentVersion,
259
+ evidenceFingerprint,
260
+ screenFingerprint,
261
+ evidenceInventory: inventory.entries,
262
+ reason: `Claude /status reported unsupported version ${parsed.agentVersion}`
263
+ };
264
+ }
265
+ if (request.expectedAgentVersion !== undefined &&
266
+ parsed.agentVersion !== request.expectedAgentVersion) {
267
+ return {
268
+ status: "mismatch",
269
+ nativeThreadId: parsed.nativeThreadId,
270
+ observedAgentVersion: parsed.agentVersion,
271
+ evidenceFingerprint,
272
+ screenFingerprint,
273
+ evidenceInventory: inventory.entries,
274
+ reason: `Claude /status reported version ${parsed.agentVersion}, not the ` +
275
+ `verified running version ${request.expectedAgentVersion}`
276
+ };
277
+ }
278
+ if (!isExactNativeThreadId(request.expectedNativeThreadId)) {
279
+ return {
280
+ status: "mismatch",
281
+ nativeThreadId: parsed.nativeThreadId,
282
+ observedAgentVersion: parsed.agentVersion,
283
+ evidenceFingerprint,
284
+ screenFingerprint,
285
+ evidenceInventory: inventory.entries,
286
+ reason: "Claude native status inspection requires an exact claude agents Session identity"
287
+ };
288
+ }
289
+ if (parsed.nativeThreadId !== request.expectedNativeThreadId.toLowerCase()) {
290
+ return {
291
+ status: "mismatch",
292
+ nativeThreadId: parsed.nativeThreadId,
293
+ observedAgentVersion: parsed.agentVersion,
294
+ evidenceFingerprint,
295
+ screenFingerprint,
296
+ evidenceInventory: inventory.entries,
297
+ reason: `Claude /status reported ${parsed.nativeThreadId}, not the exact ` +
298
+ `claude agents Session ${request.expectedNativeThreadId.toLowerCase()}`
299
+ };
300
+ }
301
+ if (request.expectedCwd !== undefined &&
302
+ normalizeLifecyclePath(parsed.cwd) !==
303
+ normalizeLifecyclePath(request.expectedCwd)) {
304
+ return {
305
+ status: "mismatch",
306
+ nativeThreadId: parsed.nativeThreadId,
307
+ observedAgentVersion: parsed.agentVersion,
308
+ evidenceFingerprint,
309
+ screenFingerprint,
310
+ evidenceInventory: inventory.entries,
311
+ reason: `Claude /status cwd ${parsed.cwd} does not match the verified pane cwd`
312
+ };
313
+ }
314
+ return {
315
+ status: "observed",
316
+ nativeThreadId: parsed.nativeThreadId,
317
+ observedAgentVersion: parsed.agentVersion,
318
+ evidence: "claude_status_panel",
319
+ evidenceFingerprint,
320
+ screenFingerprint,
321
+ evidenceInventory: inventory.entries,
322
+ result: {
323
+ kind: "native_status",
324
+ nativeThreadId: parsed.nativeThreadId,
325
+ agentVersion: parsed.agentVersion,
326
+ fields: parsed.fields,
327
+ excerpt: claudeNativeStatusExcerpt(parsed.fields)
328
+ }
329
+ };
330
+ }
118
331
  export function probeClaudeThreadLifecycle(agentVersion) {
119
332
  if (!agentVersion) {
120
333
  return {
@@ -124,11 +337,12 @@ export function probeClaudeThreadLifecycle(agentVersion) {
124
337
  reason: "the running Claude Code version could not be verified"
125
338
  };
126
339
  }
127
- const supported = agentVersion === CLAUDE_LIFECYCLE_VERSION;
340
+ const behaviorProfile = claudeLifecycleBehaviorProfile(agentVersion);
341
+ const supported = behaviorProfile !== undefined;
128
342
  return {
129
343
  status: supported ? "supported" : "unsupported",
130
344
  agentVersion,
131
- behaviorProfile: supported ? CLAUDE_LIFECYCLE_PROFILE : undefined,
345
+ behaviorProfile,
132
346
  newThread: supported,
133
347
  resumeExact: supported,
134
348
  candidateDiscovery: supported,
@@ -138,9 +352,10 @@ export function probeClaudeThreadLifecycle(agentVersion) {
138
352
  };
139
353
  }
140
354
  export function planClaudeThreadLifecycle(operation, capabilities) {
355
+ const behaviorProfile = claudeLifecycleBehaviorProfile(capabilities.agentVersion);
141
356
  if (capabilities.status !== "supported" ||
142
- capabilities.agentVersion !== CLAUDE_LIFECYCLE_VERSION ||
143
- capabilities.behaviorProfile !== CLAUDE_LIFECYCLE_PROFILE) {
357
+ !behaviorProfile ||
358
+ capabilities.behaviorProfile !== behaviorProfile) {
144
359
  throw new Error(capabilities.reason);
145
360
  }
146
361
  if (operation.kind === "new_thread") {
@@ -149,7 +364,7 @@ export function planClaudeThreadLifecycle(operation, capabilities) {
149
364
  }
150
365
  return {
151
366
  operation,
152
- behaviorProfile: CLAUDE_LIFECYCLE_PROFILE,
367
+ behaviorProfile,
153
368
  steps: [{
154
369
  kind: "transition",
155
370
  command: "/clear",
@@ -168,7 +383,7 @@ export function planClaudeThreadLifecycle(operation, capabilities) {
168
383
  }
169
384
  return {
170
385
  operation,
171
- behaviorProfile: CLAUDE_LIFECYCLE_PROFILE,
386
+ behaviorProfile,
172
387
  steps: [{
173
388
  kind: "transition",
174
389
  command: `/resume ${operation.nativeThreadId}`,
@@ -280,6 +495,222 @@ export function observeClaudeThreadLifecycle(requestOrScreen, legacyOperation, f
280
495
  reason: `Claude resumed ${nativeThreadId}, not the requested native thread ${expected}`
281
496
  };
282
497
  }
498
+ function parseCurrentClaudeStatusPanel(screen) {
499
+ const lines = screen.split("\n").slice(-CLAUDE_STATUS_PANEL_MAX_SCAN_LINES);
500
+ const headers = lines
501
+ .map((line, index) => CLAUDE_STATUS_PANEL_HEADER.test(line) ? index : -1)
502
+ .filter((index) => index >= 0);
503
+ if (headers.length === 0) {
504
+ return {
505
+ status: "missing",
506
+ reason: "no current Claude Settings Status panel is visible"
507
+ };
508
+ }
509
+ const headerIndex = headers.at(-1);
510
+ if (headerIndex === 0 ||
511
+ !CLAUDE_STATUS_PANEL_DIVIDER.test(lines[headerIndex - 1])) {
512
+ return {
513
+ status: "ambiguous",
514
+ reason: "the Claude Status header is not anchored to its exact panel divider"
515
+ };
516
+ }
517
+ const footerIndexes = lines
518
+ .map((line, index) => index > headerIndex &&
519
+ index - headerIndex <= CLAUDE_STATUS_PANEL_MAX_LINES &&
520
+ CLAUDE_STATUS_PANEL_FOOTER.test(line)
521
+ ? index
522
+ : -1)
523
+ .filter((index) => index >= 0);
524
+ if (footerIndexes.length !== 1) {
525
+ return {
526
+ status: "ambiguous",
527
+ reason: "the Claude Status panel is incomplete or has ambiguous dismissal markers"
528
+ };
529
+ }
530
+ const footerIndex = footerIndexes[0];
531
+ if (lines.slice(footerIndex + 1).some((line) => line.trim().length > 0)) {
532
+ return {
533
+ status: "missing",
534
+ reason: "the newest Claude Status panel is historical rather than current"
535
+ };
536
+ }
537
+ const regionLines = lines.slice(headerIndex, footerIndex + 1);
538
+ const region = regionLines.join("\n");
539
+ if (regionLines.length > CLAUDE_STATUS_PANEL_MAX_LINES ||
540
+ region.length > CLAUDE_STATUS_PANEL_MAX_REGION_LENGTH) {
541
+ return {
542
+ status: "ambiguous",
543
+ reason: "the Claude Status panel exceeds the bounded inspection region"
544
+ };
545
+ }
546
+ const rawFields = new Map();
547
+ for (const line of regionLines.slice(1, -1)) {
548
+ if (line.trim().length === 0) {
549
+ continue;
550
+ }
551
+ const match = CLAUDE_STATUS_PANEL_FIELD.exec(line);
552
+ if (!match) {
553
+ return {
554
+ status: "ambiguous",
555
+ reason: "the Claude Status panel contains an unprofiled non-field row"
556
+ };
557
+ }
558
+ const name = match[1].trim();
559
+ const value = match[2].trim();
560
+ if (!PROFILED_CLAUDE_STATUS_PANEL_FIELDS.has(name)) {
561
+ return {
562
+ status: "ambiguous",
563
+ reason: "the Claude Status panel contains an unprofiled field"
564
+ };
565
+ }
566
+ if (rawFields.has(name)) {
567
+ return {
568
+ status: "ambiguous",
569
+ reason: `the Claude Status panel repeats field ${name}`
570
+ };
571
+ }
572
+ rawFields.set(name, value);
573
+ }
574
+ if (rawFields.size === 0 || rawFields.size > CLAUDE_STATUS_PANEL_MAX_FIELDS) {
575
+ return {
576
+ status: "ambiguous",
577
+ reason: "the Claude Status panel has an invalid number of fields"
578
+ };
579
+ }
580
+ const agentVersion = rawFields.get("Version");
581
+ const nativeThreadId = rawFields.get("Session ID")?.toLowerCase();
582
+ const cwd = rawFields.get("cwd");
583
+ const model = rawFields.get("Model");
584
+ if (!agentVersion ||
585
+ !isExactNativeThreadId(nativeThreadId) ||
586
+ !cwd ||
587
+ !path.isAbsolute(cwd) ||
588
+ !model) {
589
+ return {
590
+ status: "ambiguous",
591
+ reason: "the Claude Status panel lacks exact Version, Session ID, cwd, or Model fields"
592
+ };
593
+ }
594
+ const exactProfileFields = new Set(claudeLifecycleCompatibilityProfile(agentVersion)
595
+ ?.nativeStatusPanelFields ?? []);
596
+ const exactRequiredValues = claudeLifecycleCompatibilityProfile(agentVersion)
597
+ ?.nativeStatusPanelRequiredValues ?? {};
598
+ if (exactProfileFields.size > 0 &&
599
+ [...rawFields.keys()].some((name) => !exactProfileFields.has(name))) {
600
+ return {
601
+ status: "ambiguous",
602
+ reason: "the Claude Status panel fields do not match its exact version profile"
603
+ };
604
+ }
605
+ if (Object.entries(exactRequiredValues).some(([name, value]) => rawFields.get(name) !== value)) {
606
+ return {
607
+ status: "ambiguous",
608
+ reason: "the Claude Status panel lacks an exact field value required by its version profile"
609
+ };
610
+ }
611
+ const fields = [...rawFields.entries()].map(([name, rawValue]) => ({
612
+ name,
613
+ value: redactClaudeNativeStatusField(name, rawValue)
614
+ .slice(0, CLAUDE_STATUS_PANEL_MAX_FIELD_VALUE_LENGTH)
615
+ }));
616
+ return {
617
+ status: "observed",
618
+ region,
619
+ agentVersion,
620
+ nativeThreadId,
621
+ cwd,
622
+ fields
623
+ };
624
+ }
625
+ function claudeStatusPanelEvidenceInventory(screen) {
626
+ const lines = screen.split("\n").slice(-CLAUDE_STATUS_PANEL_MAX_SCAN_LINES);
627
+ const counts = new Map();
628
+ for (let index = 0; index < lines.length; index += 1) {
629
+ if (!CLAUDE_STATUS_PANEL_HEADER.test(lines[index])) {
630
+ continue;
631
+ }
632
+ if (index === 0 ||
633
+ !CLAUDE_STATUS_PANEL_DIVIDER.test(lines[index - 1])) {
634
+ return {
635
+ status: "ambiguous",
636
+ entries: [],
637
+ reason: "a Claude Status marker is not anchored to its exact panel divider"
638
+ };
639
+ }
640
+ const footerIndex = lines.findIndex((line, candidateIndex) => candidateIndex > index &&
641
+ candidateIndex - index <= CLAUDE_STATUS_PANEL_MAX_LINES &&
642
+ CLAUDE_STATUS_PANEL_FOOTER.test(line));
643
+ if (footerIndex < 0) {
644
+ return {
645
+ status: "ambiguous",
646
+ entries: [],
647
+ reason: "a Claude Status panel marker has no bounded exact footer"
648
+ };
649
+ }
650
+ const region = lines.slice(index, footerIndex + 1).join("\n");
651
+ if (region.length > CLAUDE_STATUS_PANEL_MAX_REGION_LENGTH) {
652
+ return {
653
+ status: "ambiguous",
654
+ entries: [],
655
+ reason: "a Claude Status panel exceeds the bounded evidence region"
656
+ };
657
+ }
658
+ const fingerprint = fingerprintClaudeNativeInspection(region);
659
+ counts.set(fingerprint, (counts.get(fingerprint) ?? 0) + 1);
660
+ index = footerIndex;
661
+ }
662
+ const entries = [...counts.entries()].map(([evidenceFingerprint, occurrenceCount]) => ({ evidenceFingerprint, occurrenceCount }));
663
+ if (entries.length > CLAUDE_STATUS_PANEL_MAX_EVIDENCE_ENTRIES) {
664
+ return {
665
+ status: "ambiguous",
666
+ entries,
667
+ reason: "the Claude Status evidence inventory exceeds its bounded entry count"
668
+ };
669
+ }
670
+ return { status: "observed", entries };
671
+ }
672
+ function validateClaudeStatusEvidenceInventory(inventory) {
673
+ if (inventory === undefined) {
674
+ return undefined;
675
+ }
676
+ if (inventory.length > CLAUDE_STATUS_PANEL_MAX_EVIDENCE_ENTRIES) {
677
+ return "the pre-Enter Claude Status evidence inventory is over-bounded";
678
+ }
679
+ const seen = new Set();
680
+ for (const entry of inventory) {
681
+ if (!NATIVE_INSPECTION_FINGERPRINT.test(entry.evidenceFingerprint) ||
682
+ !Number.isSafeInteger(entry.occurrenceCount) ||
683
+ entry.occurrenceCount < 1 ||
684
+ seen.has(entry.evidenceFingerprint)) {
685
+ return "the pre-Enter Claude Status evidence inventory is malformed";
686
+ }
687
+ seen.add(entry.evidenceFingerprint);
688
+ }
689
+ return undefined;
690
+ }
691
+ function redactClaudeNativeStatusField(name, value) {
692
+ if (CLAUDE_STATUS_PANEL_SENSITIVE_FIELDS.has(name) ||
693
+ /(?:auth|account|email|api\s*key|token|credential|login|organization|user)/iu
694
+ .test(name)) {
695
+ return "[REDACTED]";
696
+ }
697
+ return redactString(value).replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/giu, "[REDACTED EMAIL]");
698
+ }
699
+ function claudeNativeStatusExcerpt(fields) {
700
+ return [
701
+ "Claude Code native status",
702
+ ...fields.map((field) => `${field.name}: ${field.value}`)
703
+ ].join("\n").slice(0, CLAUDE_STATUS_PANEL_MAX_EXCERPT_LENGTH);
704
+ }
705
+ function normalizeClaudeNativeInspectionScreen(screen) {
706
+ return screen
707
+ .replace(ANSI_ESCAPE_PATTERN, "")
708
+ .replace(/\r\n?/gu, "\n")
709
+ .replace(/\u00a0/gu, " ");
710
+ }
711
+ function fingerprintClaudeNativeInspection(value) {
712
+ return `sha256:${createHash("sha256").update(value).digest("hex")}`;
713
+ }
283
714
  function normalizeLifecyclePath(value) {
284
715
  const candidate = nonEmptyString(value);
285
716
  return candidate === undefined ? undefined : path.resolve(candidate);