harnery 0.41.0 → 0.43.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/commander.d.ts +16 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +21 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +63 -5
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +113 -12
- package/dist/commands/browse.d.ts +164 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +263 -51
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +52 -0
- package/dist/commands/disk.d.ts +6 -0
- package/dist/commands/disk.d.ts.map +1 -0
- package/dist/commands/disk.js +80 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +48 -1
- package/dist/commands/files.d.ts +16 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -2
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +43 -9
- package/dist/commands/rm.d.ts +4 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +87 -0
- package/dist/commands/servers.d.ts +7 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +394 -0
- package/dist/commands/tunnel.d.ts +65 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +372 -103
- package/dist/commands/web-fetch.d.ts +3 -2
- package/dist/commands/web-fetch.d.ts.map +1 -1
- package/dist/commands/web-fetch.js +25 -19
- package/dist/commands/web.d.ts +7 -1
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +9 -3
- package/dist/core/adapters/profiles.d.ts +9 -0
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +22 -1
- package/dist/core/adapters/types.d.ts +9 -0
- package/dist/core/adapters/types.d.ts.map +1 -1
- package/dist/core/agents/cli.js +13 -6
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +11 -5
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +59 -13
- package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +60 -0
- package/dist/core/agents/state/names.d.ts +43 -3
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +87 -3
- package/dist/core/artifacts/delivery-card.js +9 -2
- package/dist/core/artifacts/index.d.ts +140 -4
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +522 -49
- package/dist/core/config.d.ts +68 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +128 -3
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +2 -2
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +16 -0
- package/dist/core/events/v3/projection.js +9 -1
- package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-reader.js +42 -16
- package/dist/core/hooks/adapter/events.d.ts +17 -0
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +5 -0
- package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
- package/dist/core/hooks/adapter/wiring.d.ts +37 -7
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +62 -4
- package/dist/core/hooks/cli.js +131 -23
- package/dist/core/hooks/fork-lineage.d.ts +40 -0
- package/dist/core/hooks/fork-lineage.d.ts.map +1 -0
- package/dist/core/hooks/fork-lineage.js +62 -0
- package/dist/core/hooks/resolve/transcript.d.ts +16 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +84 -0
- package/dist/core/hooks/run-markers.d.ts +37 -0
- package/dist/core/hooks/run-markers.d.ts.map +1 -0
- package/dist/core/hooks/run-markers.js +109 -0
- package/dist/core/hooks/shell-rm.d.ts +2 -0
- package/dist/core/hooks/shell-rm.d.ts.map +1 -0
- package/dist/core/hooks/shell-rm.js +105 -0
- package/dist/core/hooks/shell-tokens.d.ts +12 -0
- package/dist/core/hooks/shell-tokens.d.ts.map +1 -0
- package/dist/core/hooks/shell-tokens.js +97 -0
- package/dist/core/hooks/shell-waiter.d.ts.map +1 -1
- package/dist/core/hooks/shell-waiter.js +3 -93
- package/dist/core/semantic/contract.d.ts +5 -5
- package/dist/core/servers/index.d.ts +211 -0
- package/dist/core/servers/index.d.ts.map +1 -0
- package/dist/core/servers/index.js +560 -0
- package/dist/core/servers/net.d.ts +58 -0
- package/dist/core/servers/net.d.ts.map +1 -0
- package/dist/core/servers/net.js +270 -0
- package/dist/core/servers/tunnels.d.ts +14 -0
- package/dist/core/servers/tunnels.d.ts.map +1 -0
- package/dist/core/servers/tunnels.js +33 -0
- package/dist/core/storage/builtins.js +1 -0
- package/dist/core/storage/process-log.d.ts +12 -0
- package/dist/core/storage/process-log.d.ts.map +1 -1
- package/dist/core/storage/process-log.js +10 -2
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +30 -1
- package/dist/core/workflow/sandbox-projection.d.ts +13 -2
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +23 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +6 -1
- package/dist/core/workflow/spawn-codex.d.ts +2 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +20 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +6 -1
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +4 -1
- package/dist/core/workflow/types.d.ts +47 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/worker-access.d.ts +92 -0
- package/dist/core/workflow/worker-access.d.ts.map +1 -0
- package/dist/core/workflow/worker-access.js +450 -0
- package/dist/lib/browser/batch-steps.d.ts +18 -0
- package/dist/lib/browser/batch-steps.d.ts.map +1 -0
- package/dist/lib/browser/batch-steps.js +82 -0
- package/dist/lib/browser/collect/collector.d.ts +99 -0
- package/dist/lib/browser/collect/collector.d.ts.map +1 -0
- package/dist/lib/browser/collect/collector.js +202 -0
- package/dist/lib/browser/collect/fields.d.ts +31 -0
- package/dist/lib/browser/collect/fields.d.ts.map +1 -0
- package/dist/lib/browser/collect/fields.js +75 -0
- package/dist/lib/browser/collect/index.d.ts +6 -0
- package/dist/lib/browser/collect/index.d.ts.map +1 -0
- package/dist/lib/browser/collect/index.js +5 -0
- package/dist/lib/browser/collect/page-driver.d.ts +14 -0
- package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
- package/dist/lib/browser/collect/page-driver.js +306 -0
- package/dist/lib/browser/collect/presets.d.ts +52 -0
- package/dist/lib/browser/collect/presets.d.ts.map +1 -0
- package/dist/lib/browser/collect/presets.js +176 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
- package/dist/lib/browser/collect/scroll-plan.js +118 -0
- package/dist/lib/browser/index.d.ts +1 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.d.ts +2 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +13 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +1 -0
- package/dist/lib/browser/windows-chrome.d.ts +59 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
- package/dist/lib/browser/windows-chrome.js +179 -0
- package/dist/lib/claude-desktop-share.d.ts +136 -0
- package/dist/lib/claude-desktop-share.d.ts.map +1 -0
- package/dist/lib/claude-desktop-share.js +394 -0
- package/dist/lib/disk-usage.d.ts +50 -0
- package/dist/lib/disk-usage.d.ts.map +1 -0
- package/dist/lib/disk-usage.js +303 -0
- package/dist/lib/docs-lint.d.ts +6 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +8 -3
- package/dist/lib/guarded-remove.d.ts +34 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -0
- package/dist/lib/guarded-remove.js +310 -0
- package/dist/lib/instructions/templates.js +8 -8
- package/dist/lib/tunnel/allowlist.d.ts +44 -0
- package/dist/lib/tunnel/allowlist.d.ts.map +1 -0
- package/dist/lib/tunnel/allowlist.js +204 -0
- package/dist/lib/tunnel/current-address.d.ts +41 -0
- package/dist/lib/tunnel/current-address.d.ts.map +1 -0
- package/dist/lib/tunnel/current-address.js +123 -0
- package/dist/lib/tunnel/error-page.d.ts +1 -1
- package/dist/lib/tunnel/error-page.d.ts.map +1 -1
- package/dist/lib/tunnel/error-page.js +19 -8
- package/dist/lib/tunnel/gate.js +33 -7
- package/dist/lib/tunnel/path-scope.d.ts +22 -0
- package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
- package/dist/lib/tunnel/path-scope.js +62 -0
- package/dist/lib/tunnel/processes.d.ts +39 -0
- package/dist/lib/tunnel/processes.d.ts.map +1 -0
- package/dist/lib/tunnel/processes.js +121 -0
- package/dist/lib/tunnel/state.d.ts +33 -3
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +115 -30
- package/package.json +7 -1
- package/schemas/config.schema.json +91 -0
- package/src/commander.ts +51 -0
- package/src/commands/agents.ts +111 -11
- package/src/commands/artifacts.ts +171 -19
- package/src/commands/browse.ts +400 -57
- package/src/commands/claude-desktop.ts +61 -0
- package/src/commands/disk.ts +114 -0
- package/src/commands/doctor.ts +52 -1
- package/src/commands/files.ts +93 -2
- package/src/commands/init.ts +55 -5
- package/src/commands/rm.ts +115 -0
- package/src/commands/servers.ts +506 -0
- package/src/commands/tunnel.ts +494 -116
- package/src/commands/web-fetch.ts +30 -19
- package/src/commands/web.ts +9 -3
- package/src/core/adapters/profiles.ts +22 -1
- package/src/core/adapters/types.ts +10 -0
- package/src/core/agents/cli.ts +14 -6
- package/src/core/agents/render/prompt-context.ts +25 -8
- package/src/core/agents/render/session-context.ts +84 -16
- package/src/core/agents/state/live-coordination-view.ts +73 -0
- package/src/core/agents/state/names.ts +117 -6
- package/src/core/artifacts/delivery-card.ts +10 -2
- package/src/core/artifacts/index.ts +697 -44
- package/src/core/config.ts +183 -4
- package/src/core/diagnostics/bundle.ts +2 -1
- package/src/core/events/v3/producers/recorder.ts +16 -0
- package/src/core/events/v3/projection.ts +11 -1
- package/src/core/events/v3/support-storage/pack-reader.ts +45 -19
- package/src/core/hooks/adapter/events.ts +19 -0
- package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
- package/src/core/hooks/adapter/wiring.ts +116 -8
- package/src/core/hooks/cli.ts +152 -22
- package/src/core/hooks/fork-lineage.ts +82 -0
- package/src/core/hooks/resolve/transcript.ts +88 -0
- package/src/core/hooks/run-markers.ts +132 -0
- package/src/core/hooks/shell-rm.ts +103 -0
- package/src/core/hooks/shell-tokens.ts +96 -0
- package/src/core/hooks/shell-waiter.ts +4 -91
- package/src/core/servers/index.ts +712 -0
- package/src/core/servers/net.ts +283 -0
- package/src/core/servers/tunnels.ts +39 -0
- package/src/core/storage/builtins.ts +1 -0
- package/src/core/storage/process-log.ts +17 -2
- package/src/core/workflow/engine.ts +38 -1
- package/src/core/workflow/sandbox-projection.ts +38 -2
- package/src/core/workflow/spawn-claude.ts +5 -1
- package/src/core/workflow/spawn-codex.ts +18 -1
- package/src/core/workflow/spawn-cursor.ts +5 -1
- package/src/core/workflow/spawn-opencode.ts +4 -1
- package/src/core/workflow/types.ts +62 -2
- package/src/core/workflow/worker-access.ts +525 -0
- package/src/lib/browser/batch-steps.ts +79 -0
- package/src/lib/browser/collect/collector.ts +306 -0
- package/src/lib/browser/collect/fields.ts +87 -0
- package/src/lib/browser/collect/index.ts +47 -0
- package/src/lib/browser/collect/page-driver.ts +327 -0
- package/src/lib/browser/collect/presets.ts +237 -0
- package/src/lib/browser/collect/scroll-plan.ts +179 -0
- package/src/lib/browser/index.ts +1 -0
- package/src/lib/browser/page-review-judge.ts +16 -0
- package/src/lib/browser/qa-run-contracts.ts +11 -0
- package/src/lib/browser/qa-run.ts +1 -0
- package/src/lib/browser/windows-chrome.ts +227 -0
- package/src/lib/claude-desktop-share.ts +511 -0
- package/src/lib/disk-usage.ts +347 -0
- package/src/lib/docs-lint.ts +8 -3
- package/src/lib/guarded-remove.ts +376 -0
- package/src/lib/instructions/templates.ts +8 -8
- package/src/lib/tunnel/allowlist.ts +208 -0
- package/src/lib/tunnel/current-address.ts +158 -0
- package/src/lib/tunnel/error-page.ts +25 -9
- package/src/lib/tunnel/gate.ts +38 -9
- package/src/lib/tunnel/path-scope.ts +73 -0
- package/src/lib/tunnel/processes.ts +149 -0
- package/src/lib/tunnel/state.ts +143 -30
|
@@ -318,6 +318,90 @@ const SESSION_FILE_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-
|
|
|
318
318
|
const FORK_SCAN_MAX_BYTES = 128 * 1024 * 1024;
|
|
319
319
|
/** How many message uuids to sample across the fork's copied prefix. */
|
|
320
320
|
const FORK_SAMPLE_SIZE = 24;
|
|
321
|
+
/** Tail window scanned for inherited rows; a fork's own rows are few when the
|
|
322
|
+
* scan runs (session start or the first tool call), so the copied history ends
|
|
323
|
+
* well inside it. */
|
|
324
|
+
const INHERITED_TAIL_BYTES = 4 * 1024 * 1024;
|
|
325
|
+
const INHERITED_HEAD_BYTES = 64 * 1024;
|
|
326
|
+
const SESSION_ID_FIELD_RE = /"sessionId":"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"/gi;
|
|
327
|
+
/**
|
|
328
|
+
* Detect inherited history from row session ids: the session whose rows were
|
|
329
|
+
* copied into this transcript, or undefined when every row is this session's.
|
|
330
|
+
*
|
|
331
|
+
* Empirical basis (verified 2026-10-07, Claude Code 2.1.283): the desktop
|
|
332
|
+
* app's "Fork from here" copies the parent's rows into the fork's transcript
|
|
333
|
+
* WITHOUT rewriting their `sessionId`, so the fork's file opens with thousands
|
|
334
|
+
* of rows stamped with the parent's id. The CLI `--fork-session` path rewrites
|
|
335
|
+
* the ids instead and is covered by {@link detectForkParent}.
|
|
336
|
+
*
|
|
337
|
+
* When the history is itself several generations deep, the LAST foreign row is
|
|
338
|
+
* the nearest ancestor, so the tail is scanned first. Whether the foreign
|
|
339
|
+
* session is a fork parent or a dead predecessor (a continuation) is the
|
|
340
|
+
* caller's call. Fail-soft: never throws, undefined on any read problem.
|
|
341
|
+
*/
|
|
342
|
+
export function detectInheritedSessionParent(transcriptPath, sessionId) {
|
|
343
|
+
try {
|
|
344
|
+
const readablePath = resolveTranscriptPath(transcriptPath);
|
|
345
|
+
if (!readablePath || !sessionId)
|
|
346
|
+
return undefined;
|
|
347
|
+
const size = statSync(readablePath).size;
|
|
348
|
+
const fromTail = lastForeignSessionId(readRange(readablePath, Math.max(0, size - INHERITED_TAIL_BYTES), size), sessionId);
|
|
349
|
+
if (fromTail || size <= INHERITED_TAIL_BYTES)
|
|
350
|
+
return fromTail;
|
|
351
|
+
return lastForeignSessionId(readRange(readablePath, 0, Math.min(size, INHERITED_HEAD_BYTES)), sessionId);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function lastForeignSessionId(text, sessionId) {
|
|
358
|
+
if (!text)
|
|
359
|
+
return undefined;
|
|
360
|
+
const own = sessionId.toLowerCase();
|
|
361
|
+
const lines = text.split("\n");
|
|
362
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
363
|
+
const line = lines[i];
|
|
364
|
+
if (!line.includes('"sessionId":"'))
|
|
365
|
+
continue;
|
|
366
|
+
let foreign = false;
|
|
367
|
+
for (const m of line.matchAll(SESSION_ID_FIELD_RE)) {
|
|
368
|
+
if (m[1].toLowerCase() !== own)
|
|
369
|
+
foreign = true;
|
|
370
|
+
}
|
|
371
|
+
if (!foreign)
|
|
372
|
+
continue;
|
|
373
|
+
// Confirm on the parsed row: a nested object can carry another session's
|
|
374
|
+
// id, but only the row's own top-level field says whose row it is. A
|
|
375
|
+
// partial line at a window edge fails to parse and is skipped.
|
|
376
|
+
try {
|
|
377
|
+
const row = JSON.parse(line);
|
|
378
|
+
if (typeof row.sessionId === "string" && row.sessionId.toLowerCase() !== own) {
|
|
379
|
+
return row.sessionId;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
/* torn line */
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
function readRange(path, start, end) {
|
|
389
|
+
try {
|
|
390
|
+
const fs = require("node:fs");
|
|
391
|
+
const fd = fs.openSync(path, "r");
|
|
392
|
+
try {
|
|
393
|
+
const buf = Buffer.alloc(Math.max(0, end - start));
|
|
394
|
+
fs.readSync(fd, buf, 0, buf.length, start);
|
|
395
|
+
return buf.toString("utf8");
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
fs.closeSync(fd);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
return undefined;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
321
405
|
/**
|
|
322
406
|
* Detect the parent session of a forked CC conversation from its transcript.
|
|
323
407
|
*
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start markers for hook runs on adapters that kill a hook at its `timeout`
|
|
3
|
+
* (ADR 0197). A Codex hook that times out has its process tree killed and the
|
|
4
|
+
* tool proceeds without that hook's checks, and the killed process never
|
|
5
|
+
* reaches the completion receipt in `health.ts`. A small marker written at
|
|
6
|
+
* start and removed at completion leaves evidence of that case for doctor.
|
|
7
|
+
*
|
|
8
|
+
* Observer-only: every write, read, and removal swallows its own errors so a
|
|
9
|
+
* marker can never change hook behavior.
|
|
10
|
+
*/
|
|
11
|
+
export interface HookRunMarker {
|
|
12
|
+
event: string;
|
|
13
|
+
adapter: string;
|
|
14
|
+
pid: number;
|
|
15
|
+
started_at: string;
|
|
16
|
+
timeout_sec: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function hookRunMarkerDir(coordRoot: string): string;
|
|
19
|
+
/** Write this run's marker; returns its path, or null when it could not be written. */
|
|
20
|
+
export declare function writeHookRunMarker(coordRoot: string, marker: Omit<HookRunMarker, "started_at">, now?: Date): string | null;
|
|
21
|
+
export declare function clearHookRunMarker(path: string | null): void;
|
|
22
|
+
export interface UnfinishedHookRuns {
|
|
23
|
+
count: number;
|
|
24
|
+
/** Count per event name, most frequent first. */
|
|
25
|
+
events: Array<{
|
|
26
|
+
event: string;
|
|
27
|
+
count: number;
|
|
28
|
+
}>;
|
|
29
|
+
latest: string | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Markers whose run outlived its timeout plus a grace period and whose
|
|
33
|
+
* process is gone, within the window. A live process past its timeout is
|
|
34
|
+
* still running and is not counted.
|
|
35
|
+
*/
|
|
36
|
+
export declare function findUnfinishedHookRuns(coordRoot: string, nowMs?: number, windowMs?: number, isAlive?: (pid: number) => boolean): UnfinishedHookRuns;
|
|
37
|
+
//# sourceMappingURL=run-markers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-markers.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/run-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACzC,GAAG,OAAa,GACf,MAAM,GAAG,IAAI,CAYf;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAO5D;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,KAAK,SAAa,EAClB,QAAQ,SAAsB,EAC9B,OAAO,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAsB,GAC/C,kBAAkB,CA+BpB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start markers for hook runs on adapters that kill a hook at its `timeout`
|
|
3
|
+
* (ADR 0197). A Codex hook that times out has its process tree killed and the
|
|
4
|
+
* tool proceeds without that hook's checks, and the killed process never
|
|
5
|
+
* reaches the completion receipt in `health.ts`. A small marker written at
|
|
6
|
+
* start and removed at completion leaves evidence of that case for doctor.
|
|
7
|
+
*
|
|
8
|
+
* Observer-only: every write, read, and removal swallows its own errors so a
|
|
9
|
+
* marker can never change hook behavior.
|
|
10
|
+
*/
|
|
11
|
+
import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
/** Markers older than this are pruned when a new one is written. */
|
|
14
|
+
const MARKER_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
15
|
+
/** A run is unfinished once its marker outlives the timeout by this much. */
|
|
16
|
+
const UNFINISHED_GRACE_MS = 30_000;
|
|
17
|
+
export function hookRunMarkerDir(coordRoot) {
|
|
18
|
+
return join(coordRoot, ".harnery", "active", "hook-runs");
|
|
19
|
+
}
|
|
20
|
+
/** Write this run's marker; returns its path, or null when it could not be written. */
|
|
21
|
+
export function writeHookRunMarker(coordRoot, marker, now = new Date()) {
|
|
22
|
+
try {
|
|
23
|
+
const dir = hookRunMarkerDir(coordRoot);
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
pruneHookRunMarkers(dir, now.getTime());
|
|
26
|
+
const path = join(dir, `${marker.pid}-${safeName(marker.event)}.json`);
|
|
27
|
+
const body = { ...marker, started_at: now.toISOString() };
|
|
28
|
+
writeFileSync(path, `${JSON.stringify(body)}\n`);
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function clearHookRunMarker(path) {
|
|
36
|
+
if (!path)
|
|
37
|
+
return;
|
|
38
|
+
try {
|
|
39
|
+
rmSync(path, { force: true });
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Observer-only.
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Markers whose run outlived its timeout plus a grace period and whose
|
|
47
|
+
* process is gone, within the window. A live process past its timeout is
|
|
48
|
+
* still running and is not counted.
|
|
49
|
+
*/
|
|
50
|
+
export function findUnfinishedHookRuns(coordRoot, nowMs = Date.now(), windowMs = 24 * 60 * 60 * 1000, isAlive = processAlive) {
|
|
51
|
+
const counts = new Map();
|
|
52
|
+
let latest = null;
|
|
53
|
+
let count = 0;
|
|
54
|
+
let names = [];
|
|
55
|
+
try {
|
|
56
|
+
names = readdirSync(hookRunMarkerDir(coordRoot)).filter((n) => n.endsWith(".json"));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { count: 0, events: [], latest: null };
|
|
60
|
+
}
|
|
61
|
+
for (const name of names) {
|
|
62
|
+
let marker;
|
|
63
|
+
try {
|
|
64
|
+
marker = JSON.parse(readFileSync(join(hookRunMarkerDir(coordRoot), name), "utf8"));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const started = Date.parse(marker.started_at);
|
|
70
|
+
if (!Number.isFinite(started) || nowMs - started > windowMs)
|
|
71
|
+
continue;
|
|
72
|
+
if (nowMs - started < marker.timeout_sec * 1000 + UNFINISHED_GRACE_MS)
|
|
73
|
+
continue;
|
|
74
|
+
if (isAlive(marker.pid))
|
|
75
|
+
continue;
|
|
76
|
+
count++;
|
|
77
|
+
counts.set(marker.event, (counts.get(marker.event) ?? 0) + 1);
|
|
78
|
+
if (!latest || marker.started_at > latest)
|
|
79
|
+
latest = marker.started_at;
|
|
80
|
+
}
|
|
81
|
+
const events = [...counts.entries()]
|
|
82
|
+
.map(([event, n]) => ({ event, count: n }))
|
|
83
|
+
.sort((a, b) => b.count - a.count || a.event.localeCompare(b.event));
|
|
84
|
+
return { count, events, latest };
|
|
85
|
+
}
|
|
86
|
+
function pruneHookRunMarkers(dir, nowMs) {
|
|
87
|
+
for (const name of readdirSync(dir)) {
|
|
88
|
+
const path = join(dir, name);
|
|
89
|
+
try {
|
|
90
|
+
if (nowMs - statSync(path).mtimeMs > MARKER_RETENTION_MS)
|
|
91
|
+
rmSync(path, { force: true });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Observer-only.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function processAlive(pid) {
|
|
99
|
+
try {
|
|
100
|
+
process.kill(pid, 0);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
return err.code === "EPERM";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function safeName(value) {
|
|
108
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 64) || "unknown";
|
|
109
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-rm.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/shell-rm.ts"],"names":[],"mappings":"AAsFA,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EACjB,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAYf"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { basename, shellCommandFromTool, tokenize } from "./shell-tokens.js";
|
|
2
|
+
/**
|
|
3
|
+
* Deny a shell `rm` and name the guarded replacement. The instructions have
|
|
4
|
+
* said "use <bin> rm instead of shell rm" since the command shipped, and
|
|
5
|
+
* agents still reached for bare rm several times a day, often on unquoted
|
|
6
|
+
* variable paths that Claude Code then stops on for a human prompt. A
|
|
7
|
+
* repeated, machine-detectable failure belongs in a gate, not more prose.
|
|
8
|
+
*
|
|
9
|
+
* This is a guardrail against the reflex, not a security boundary: it reads
|
|
10
|
+
* command positions only (so `git rm`, `docker rm`, and quoted examples pass),
|
|
11
|
+
* plus the `xargs rm`, `find -exec rm`, and `bash -c '...rm...'` shapes.
|
|
12
|
+
* HARNERY_ALLOW_RM=1 on the command is the deliberate override.
|
|
13
|
+
*/
|
|
14
|
+
const OVERRIDE = "HARNERY_ALLOW_RM=1";
|
|
15
|
+
const SEPARATORS = new Set([";", "|", "||", "&", "&&", "(", ")", "{", "}"]);
|
|
16
|
+
const KEYWORDS = new Set(["do", "if", "then", "elif", "else", "!", "while", "until", "time"]);
|
|
17
|
+
const WRAPPERS = new Set(["command", "exec", "builtin", "nohup", "sudo", "doas", "env", "nice"]);
|
|
18
|
+
// xargs options that consume the following token as their value.
|
|
19
|
+
const XARGS_VALUE_FLAGS = new Set(["-n", "-I", "-L", "-P", "-d", "-E", "-s", "-a"]);
|
|
20
|
+
function isRm(value, quoted) {
|
|
21
|
+
return !quoted && basename(value) === "rm";
|
|
22
|
+
}
|
|
23
|
+
function invokesRm(source, depth = 0) {
|
|
24
|
+
if (depth > 8)
|
|
25
|
+
return false;
|
|
26
|
+
const tokens = tokenize(source);
|
|
27
|
+
if (tokens.some((t) => !t.quoted && t.value === OVERRIDE))
|
|
28
|
+
return false;
|
|
29
|
+
let start = true;
|
|
30
|
+
let afterWrapper = false;
|
|
31
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
32
|
+
const token = tokens[i];
|
|
33
|
+
if (token.operator) {
|
|
34
|
+
if (SEPARATORS.has(token.value)) {
|
|
35
|
+
start = true;
|
|
36
|
+
afterWrapper = false;
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!start)
|
|
41
|
+
continue;
|
|
42
|
+
if (!token.quoted) {
|
|
43
|
+
if (KEYWORDS.has(token.value))
|
|
44
|
+
continue;
|
|
45
|
+
if (/^[A-Za-z_][A-Za-z_0-9]*=/.test(token.value))
|
|
46
|
+
continue;
|
|
47
|
+
if (afterWrapper && token.value.startsWith("-"))
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const name = basename(token.value);
|
|
51
|
+
if (WRAPPERS.has(name)) {
|
|
52
|
+
afterWrapper = true;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
start = false;
|
|
56
|
+
afterWrapper = false;
|
|
57
|
+
if (isRm(token.value, token.quoted))
|
|
58
|
+
return true;
|
|
59
|
+
if (name === "xargs") {
|
|
60
|
+
for (let j = i + 1; j < tokens.length && !tokens[j].operator; j++) {
|
|
61
|
+
const arg = tokens[j];
|
|
62
|
+
if (arg.value.startsWith("-")) {
|
|
63
|
+
if (XARGS_VALUE_FLAGS.has(arg.value))
|
|
64
|
+
j++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (isRm(arg.value, arg.quoted))
|
|
68
|
+
return true;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (name === "find") {
|
|
73
|
+
for (let j = i + 1; j < tokens.length && !tokens[j].operator; j++) {
|
|
74
|
+
if (!/^-(?:exec|execdir|ok|okdir)$/.test(tokens[j].value))
|
|
75
|
+
continue;
|
|
76
|
+
const next = tokens[j + 1];
|
|
77
|
+
if (next && !next.operator && isRm(next.value, next.quoted))
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (["bash", "sh", "dash", "zsh", "ksh"].includes(name)) {
|
|
82
|
+
for (let j = i + 1; j < tokens.length && !tokens[j].operator; j++) {
|
|
83
|
+
if (/^-[a-z]*c[a-z]*$/.test(tokens[j].value)) {
|
|
84
|
+
const script = tokens[j + 1];
|
|
85
|
+
if (script && !script.operator && invokesRm(script.value, depth + 1))
|
|
86
|
+
return true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
export function shellRmReason(toolName, toolInput, binName) {
|
|
95
|
+
const command = shellCommandFromTool(toolName, toolInput);
|
|
96
|
+
if (command === null || !invokesRm(command))
|
|
97
|
+
return null;
|
|
98
|
+
return (`Shell rm blocked: it skips the resolved-path, Git-ownership, and peer-claim checks, and an ` +
|
|
99
|
+
`unquoted variable path can expand to the wrong target. Use the guarded paths instead: ` +
|
|
100
|
+
`\`${binName} rm --root <directory> <paths...>\` previews removal of untracked files (add ` +
|
|
101
|
+
`--yes to delete, -r for a directory); \`git rm\` removes tracked source; ` +
|
|
102
|
+
`\`${binName} artifacts discard <id>\` retires managed evidence. To replace a file, overwrite ` +
|
|
103
|
+
`it in place or \`mv\` the new file over the old one, so nothing needs deleting. If none of ` +
|
|
104
|
+
`these fit, prefix the command with ${OVERRIDE}.`);
|
|
105
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Shared, deliberately small shell lexer for PreToolUse command guards. */
|
|
2
|
+
export interface Token {
|
|
3
|
+
value: string;
|
|
4
|
+
quoted: boolean;
|
|
5
|
+
operator?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Keep quoted arguments opaque, including examples and heredoc bodies. */
|
|
8
|
+
export declare function tokenize(source: string): Token[];
|
|
9
|
+
export declare function basename(value: string): string;
|
|
10
|
+
/** The command string of a Bash-style tool call, or null for any other tool. */
|
|
11
|
+
export declare function shellCommandFromTool(toolName: unknown, toolInput: unknown): string | null;
|
|
12
|
+
//# sourceMappingURL=shell-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-tokens.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/shell-tokens.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAE5E,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CA+DhD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAgBzF"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** Shared, deliberately small shell lexer for PreToolUse command guards. */
|
|
2
|
+
/** Keep quoted arguments opaque, including examples and heredoc bodies. */
|
|
3
|
+
export function tokenize(source) {
|
|
4
|
+
const out = [];
|
|
5
|
+
const heredocs = [];
|
|
6
|
+
for (let i = 0; i < source.length;) {
|
|
7
|
+
const ch = source[i];
|
|
8
|
+
if (ch === "\\" && source[i + 1] === "\n") {
|
|
9
|
+
i += 2;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (ch === "\n") {
|
|
13
|
+
out.push({ value: ";", quoted: false, operator: true });
|
|
14
|
+
i++;
|
|
15
|
+
for (const doc of heredocs.splice(0)) {
|
|
16
|
+
while (i < source.length) {
|
|
17
|
+
const end = source.indexOf("\n", i);
|
|
18
|
+
const stop = end === -1 ? source.length : end;
|
|
19
|
+
const line = source.slice(i, stop);
|
|
20
|
+
i = end === -1 ? source.length : end + 1;
|
|
21
|
+
if ((doc.tabs ? line.replace(/^\t+/, "") : line) === doc.end)
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (/\s/.test(ch)) {
|
|
28
|
+
i++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (ch === "#") {
|
|
32
|
+
const end = source.indexOf("\n", i);
|
|
33
|
+
i = end === -1 ? source.length : end;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (";|&(){}<>".includes(ch)) {
|
|
37
|
+
const op = source.slice(i).match(/^(?:<<-?|>>|&&|\|\||[;|&(){}<>])/)[0];
|
|
38
|
+
out.push({ value: op, quoted: false, operator: true });
|
|
39
|
+
i += op.length;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
let value = "";
|
|
43
|
+
let quoted = false;
|
|
44
|
+
while (i < source.length && !/[\s;|&(){}<>]/.test(source[i])) {
|
|
45
|
+
const next = source[i++];
|
|
46
|
+
if (next === "'" || next === '"' || next === "`") {
|
|
47
|
+
quoted = true;
|
|
48
|
+
while (i < source.length && source[i] !== next) {
|
|
49
|
+
if (source[i] === "\\" && next !== "'" && i + 1 < source.length)
|
|
50
|
+
i++;
|
|
51
|
+
value += source[i++];
|
|
52
|
+
}
|
|
53
|
+
if (source[i] === next)
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
else if (next === "\\" && i < source.length) {
|
|
57
|
+
const escaped = source[i++];
|
|
58
|
+
if (escaped !== "\n")
|
|
59
|
+
value += escaped;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
value += next;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const previous = out.at(-1)?.value;
|
|
66
|
+
if (previous === "<<" || previous === "<<-") {
|
|
67
|
+
heredocs.push({ end: value, tabs: previous === "<<-" });
|
|
68
|
+
}
|
|
69
|
+
out.push({ value, quoted });
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
export function basename(value) {
|
|
74
|
+
return value.split(/[\\/]/).at(-1) ?? value;
|
|
75
|
+
}
|
|
76
|
+
/** The command string of a Bash-style tool call, or null for any other tool. */
|
|
77
|
+
export function shellCommandFromTool(toolName, toolInput) {
|
|
78
|
+
if (typeof toolName !== "string")
|
|
79
|
+
return null;
|
|
80
|
+
const name = toolName.toLowerCase().replace(/^functions\./, "");
|
|
81
|
+
if (!["bash", "shell", "shell_command", "exec_command"].includes(name))
|
|
82
|
+
return null;
|
|
83
|
+
let input = toolInput;
|
|
84
|
+
if (typeof input === "string") {
|
|
85
|
+
try {
|
|
86
|
+
input = JSON.parse(input);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!input || typeof input !== "object")
|
|
93
|
+
return null;
|
|
94
|
+
const args = input;
|
|
95
|
+
const command = args.command ?? args.cmd;
|
|
96
|
+
return typeof command === "string" ? command : null;
|
|
97
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-waiter.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/shell-waiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shell-waiter.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/shell-waiter.ts"],"names":[],"mappings":"AA2EA,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAGtF"}
|
|
@@ -1,83 +1,10 @@
|
|
|
1
|
+
import { basename, shellCommandFromTool, tokenize } from "./shell-tokens.js";
|
|
1
2
|
/** Recognize inline waiters; this is not a proof of arbitrary shell termination. */
|
|
2
3
|
const REASON = "Inline shell waiter blocked: polling loops and delayed log-only checks can outlive the job " +
|
|
3
4
|
"and accumulate as background tasks. Read the existing task output now, or wait on its " +
|
|
4
5
|
"existing task handle. If polling is necessary, use a reviewed script with a hard deadline, " +
|
|
5
6
|
"nonzero timeout exit, and producer-exit/failure detection. Do not create another waiter " +
|
|
6
7
|
"to check progress or infer completion from a broad pgrep -f pattern.";
|
|
7
|
-
/** Keep quoted arguments opaque, including examples and heredoc bodies. */
|
|
8
|
-
function tokenize(source) {
|
|
9
|
-
const out = [];
|
|
10
|
-
const heredocs = [];
|
|
11
|
-
for (let i = 0; i < source.length;) {
|
|
12
|
-
const ch = source[i];
|
|
13
|
-
if (ch === "\\" && source[i + 1] === "\n") {
|
|
14
|
-
i += 2;
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
if (ch === "\n") {
|
|
18
|
-
out.push({ value: ";", quoted: false, operator: true });
|
|
19
|
-
i++;
|
|
20
|
-
for (const doc of heredocs.splice(0)) {
|
|
21
|
-
while (i < source.length) {
|
|
22
|
-
const end = source.indexOf("\n", i);
|
|
23
|
-
const stop = end === -1 ? source.length : end;
|
|
24
|
-
const line = source.slice(i, stop);
|
|
25
|
-
i = end === -1 ? source.length : end + 1;
|
|
26
|
-
if ((doc.tabs ? line.replace(/^\t+/, "") : line) === doc.end)
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (/\s/.test(ch)) {
|
|
33
|
-
i++;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (ch === "#") {
|
|
37
|
-
const end = source.indexOf("\n", i);
|
|
38
|
-
i = end === -1 ? source.length : end;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (";|&(){}<>".includes(ch)) {
|
|
42
|
-
const op = source.slice(i).match(/^(?:<<-?|>>|&&|\|\||[;|&(){}<>])/)[0];
|
|
43
|
-
out.push({ value: op, quoted: false, operator: true });
|
|
44
|
-
i += op.length;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
let value = "";
|
|
48
|
-
let quoted = false;
|
|
49
|
-
while (i < source.length && !/[\s;|&(){}<>]/.test(source[i])) {
|
|
50
|
-
const next = source[i++];
|
|
51
|
-
if (next === "'" || next === '"' || next === "`") {
|
|
52
|
-
quoted = true;
|
|
53
|
-
while (i < source.length && source[i] !== next) {
|
|
54
|
-
if (source[i] === "\\" && next !== "'" && i + 1 < source.length)
|
|
55
|
-
i++;
|
|
56
|
-
value += source[i++];
|
|
57
|
-
}
|
|
58
|
-
if (source[i] === next)
|
|
59
|
-
i++;
|
|
60
|
-
}
|
|
61
|
-
else if (next === "\\" && i < source.length) {
|
|
62
|
-
const escaped = source[i++];
|
|
63
|
-
if (escaped !== "\n")
|
|
64
|
-
value += escaped;
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
value += next;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const previous = out.at(-1)?.value;
|
|
71
|
-
if (previous === "<<" || previous === "<<-") {
|
|
72
|
-
heredocs.push({ end: value, tabs: previous === "<<-" });
|
|
73
|
-
}
|
|
74
|
-
out.push({ value, quoted });
|
|
75
|
-
}
|
|
76
|
-
return out;
|
|
77
|
-
}
|
|
78
|
-
function basename(value) {
|
|
79
|
-
return value.split(/[\\/]/).at(-1) ?? value;
|
|
80
|
-
}
|
|
81
8
|
function isWaiter(source, depth = 0) {
|
|
82
9
|
if (depth > 8)
|
|
83
10
|
return false;
|
|
@@ -151,23 +78,6 @@ function isWaiter(source, depth = 0) {
|
|
|
151
78
|
commands.every((name) => harmless.has(name)));
|
|
152
79
|
}
|
|
153
80
|
export function shellWaiterReason(toolName, toolInput) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const name = toolName.toLowerCase().replace(/^functions\./, "");
|
|
157
|
-
if (!["bash", "shell", "shell_command", "exec_command"].includes(name))
|
|
158
|
-
return null;
|
|
159
|
-
let input = toolInput;
|
|
160
|
-
if (typeof input === "string") {
|
|
161
|
-
try {
|
|
162
|
-
input = JSON.parse(input);
|
|
163
|
-
}
|
|
164
|
-
catch {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (!input || typeof input !== "object")
|
|
169
|
-
return null;
|
|
170
|
-
const args = input;
|
|
171
|
-
const command = args.command ?? args.cmd;
|
|
172
|
-
return typeof command === "string" && isWaiter(command) ? REASON : null;
|
|
81
|
+
const command = shellCommandFromTool(toolName, toolInput);
|
|
82
|
+
return command !== null && isWaiter(command) ? REASON : null;
|
|
173
83
|
}
|
|
@@ -152,7 +152,7 @@ export declare const SemanticUsageReceiptV1Schema: import("@sinclair/typebox").T
|
|
|
152
152
|
scope: import("@sinclair/typebox").TLiteral<"unreported">;
|
|
153
153
|
}>]>;
|
|
154
154
|
export declare const SemanticEvidenceObservationV1Schema: import("@sinclair/typebox").TObject<{
|
|
155
|
-
kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"
|
|
155
|
+
kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
|
|
156
156
|
event_id: import("@sinclair/typebox").TString;
|
|
157
157
|
observed_at: import("@sinclair/typebox").TString;
|
|
158
158
|
label: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
@@ -193,7 +193,7 @@ export declare const SemanticEvidenceV1Schema: import("@sinclair/typebox").TObje
|
|
|
193
193
|
started_at: import("@sinclair/typebox").TString;
|
|
194
194
|
}>>;
|
|
195
195
|
recent: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
196
|
-
kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"
|
|
196
|
+
kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
|
|
197
197
|
event_id: import("@sinclair/typebox").TString;
|
|
198
198
|
observed_at: import("@sinclair/typebox").TString;
|
|
199
199
|
label: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
@@ -201,7 +201,7 @@ export declare const SemanticEvidenceV1Schema: import("@sinclair/typebox").TObje
|
|
|
201
201
|
error_class: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
202
202
|
}>>;
|
|
203
203
|
attention: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
204
|
-
kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"
|
|
204
|
+
kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
|
|
205
205
|
event_id: import("@sinclair/typebox").TString;
|
|
206
206
|
observed_at: import("@sinclair/typebox").TString;
|
|
207
207
|
label: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
@@ -628,7 +628,7 @@ export declare const SemanticInvalidReadModelV2Schema: import("@sinclair/typebox
|
|
|
628
628
|
}>;
|
|
629
629
|
receipt: import("@sinclair/typebox").TObject<{
|
|
630
630
|
reason_code: import("@sinclair/typebox").TLiteral<"invalid_output">;
|
|
631
|
-
validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "
|
|
631
|
+
validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "confidence" | "schema" | "privacy" | "identity" | "basis" | "citation" | "unsupported_claim">[]>>;
|
|
632
632
|
usage: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
|
|
633
633
|
schema_version: import("@sinclair/typebox").TLiteral<1>;
|
|
634
634
|
source: import("@sinclair/typebox").TLiteral<"native">;
|
|
@@ -954,7 +954,7 @@ export declare const SemanticAgentReadModelV2Schema: import("@sinclair/typebox")
|
|
|
954
954
|
}>;
|
|
955
955
|
receipt: import("@sinclair/typebox").TObject<{
|
|
956
956
|
reason_code: import("@sinclair/typebox").TLiteral<"invalid_output">;
|
|
957
|
-
validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "
|
|
957
|
+
validation_issue_codes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"unknown" | "confidence" | "schema" | "privacy" | "identity" | "basis" | "citation" | "unsupported_claim">[]>>;
|
|
958
958
|
usage: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
|
|
959
959
|
schema_version: import("@sinclair/typebox").TLiteral<1>;
|
|
960
960
|
source: import("@sinclair/typebox").TLiteral<"native">;
|