harnery 0.41.0 → 0.42.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.
Files changed (212) hide show
  1. package/dist/commander.js +3 -0
  2. package/dist/commands/agents.d.ts.map +1 -1
  3. package/dist/commands/agents.js +9 -1
  4. package/dist/commands/artifacts.d.ts.map +1 -1
  5. package/dist/commands/artifacts.js +113 -12
  6. package/dist/commands/browse.d.ts +164 -0
  7. package/dist/commands/browse.d.ts.map +1 -1
  8. package/dist/commands/browse.js +263 -51
  9. package/dist/commands/claude-desktop.d.ts.map +1 -1
  10. package/dist/commands/claude-desktop.js +52 -0
  11. package/dist/commands/disk.d.ts +6 -0
  12. package/dist/commands/disk.d.ts.map +1 -0
  13. package/dist/commands/disk.js +80 -0
  14. package/dist/commands/doctor.d.ts +8 -0
  15. package/dist/commands/doctor.d.ts.map +1 -1
  16. package/dist/commands/doctor.js +48 -1
  17. package/dist/commands/files.d.ts +16 -0
  18. package/dist/commands/files.d.ts.map +1 -1
  19. package/dist/commands/files.js +54 -2
  20. package/dist/commands/init.d.ts +2 -2
  21. package/dist/commands/init.d.ts.map +1 -1
  22. package/dist/commands/init.js +43 -9
  23. package/dist/commands/rm.d.ts +4 -0
  24. package/dist/commands/rm.d.ts.map +1 -0
  25. package/dist/commands/rm.js +87 -0
  26. package/dist/commands/servers.d.ts +7 -0
  27. package/dist/commands/servers.d.ts.map +1 -0
  28. package/dist/commands/servers.js +394 -0
  29. package/dist/commands/tunnel.d.ts.map +1 -1
  30. package/dist/commands/tunnel.js +82 -2
  31. package/dist/core/adapters/profiles.d.ts +9 -0
  32. package/dist/core/adapters/profiles.d.ts.map +1 -1
  33. package/dist/core/adapters/profiles.js +22 -1
  34. package/dist/core/adapters/types.d.ts +9 -0
  35. package/dist/core/adapters/types.d.ts.map +1 -1
  36. package/dist/core/agents/cli.js +13 -6
  37. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  38. package/dist/core/agents/render/session-context.js +2 -0
  39. package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
  40. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  41. package/dist/core/agents/state/live-coordination-view.js +60 -0
  42. package/dist/core/artifacts/delivery-card.js +9 -2
  43. package/dist/core/artifacts/index.d.ts +140 -4
  44. package/dist/core/artifacts/index.d.ts.map +1 -1
  45. package/dist/core/artifacts/index.js +522 -49
  46. package/dist/core/config.d.ts +68 -11
  47. package/dist/core/config.d.ts.map +1 -1
  48. package/dist/core/config.js +128 -3
  49. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  50. package/dist/core/diagnostics/bundle.js +2 -2
  51. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  52. package/dist/core/events/v3/producers/recorder.js +16 -0
  53. package/dist/core/events/v3/projection.js +9 -1
  54. package/dist/core/hooks/adapter/events.d.ts +17 -0
  55. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  56. package/dist/core/hooks/adapter/events.js +5 -0
  57. package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
  58. package/dist/core/hooks/adapter/wiring.d.ts +37 -7
  59. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  60. package/dist/core/hooks/adapter/wiring.js +62 -4
  61. package/dist/core/hooks/cli.js +45 -6
  62. package/dist/core/hooks/run-markers.d.ts +37 -0
  63. package/dist/core/hooks/run-markers.d.ts.map +1 -0
  64. package/dist/core/hooks/run-markers.js +109 -0
  65. package/dist/core/servers/index.d.ts +211 -0
  66. package/dist/core/servers/index.d.ts.map +1 -0
  67. package/dist/core/servers/index.js +560 -0
  68. package/dist/core/servers/net.d.ts +58 -0
  69. package/dist/core/servers/net.d.ts.map +1 -0
  70. package/dist/core/servers/net.js +270 -0
  71. package/dist/core/servers/tunnels.d.ts +14 -0
  72. package/dist/core/servers/tunnels.d.ts.map +1 -0
  73. package/dist/core/servers/tunnels.js +33 -0
  74. package/dist/core/storage/builtins.js +1 -0
  75. package/dist/core/workflow/engine.d.ts.map +1 -1
  76. package/dist/core/workflow/engine.js +30 -1
  77. package/dist/core/workflow/sandbox-projection.d.ts +13 -2
  78. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
  79. package/dist/core/workflow/sandbox-projection.js +23 -0
  80. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  81. package/dist/core/workflow/spawn-claude.js +6 -1
  82. package/dist/core/workflow/spawn-codex.d.ts +2 -0
  83. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  84. package/dist/core/workflow/spawn-codex.js +20 -1
  85. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  86. package/dist/core/workflow/spawn-cursor.js +6 -1
  87. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  88. package/dist/core/workflow/spawn-opencode.js +4 -1
  89. package/dist/core/workflow/types.d.ts +47 -2
  90. package/dist/core/workflow/types.d.ts.map +1 -1
  91. package/dist/core/workflow/worker-access.d.ts +92 -0
  92. package/dist/core/workflow/worker-access.d.ts.map +1 -0
  93. package/dist/core/workflow/worker-access.js +450 -0
  94. package/dist/lib/browser/batch-steps.d.ts +18 -0
  95. package/dist/lib/browser/batch-steps.d.ts.map +1 -0
  96. package/dist/lib/browser/batch-steps.js +82 -0
  97. package/dist/lib/browser/collect/collector.d.ts +99 -0
  98. package/dist/lib/browser/collect/collector.d.ts.map +1 -0
  99. package/dist/lib/browser/collect/collector.js +202 -0
  100. package/dist/lib/browser/collect/fields.d.ts +31 -0
  101. package/dist/lib/browser/collect/fields.d.ts.map +1 -0
  102. package/dist/lib/browser/collect/fields.js +75 -0
  103. package/dist/lib/browser/collect/index.d.ts +6 -0
  104. package/dist/lib/browser/collect/index.d.ts.map +1 -0
  105. package/dist/lib/browser/collect/index.js +5 -0
  106. package/dist/lib/browser/collect/page-driver.d.ts +14 -0
  107. package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
  108. package/dist/lib/browser/collect/page-driver.js +306 -0
  109. package/dist/lib/browser/collect/presets.d.ts +52 -0
  110. package/dist/lib/browser/collect/presets.d.ts.map +1 -0
  111. package/dist/lib/browser/collect/presets.js +176 -0
  112. package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
  113. package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
  114. package/dist/lib/browser/collect/scroll-plan.js +118 -0
  115. package/dist/lib/browser/index.d.ts +1 -1
  116. package/dist/lib/browser/index.d.ts.map +1 -1
  117. package/dist/lib/browser/page-review-judge.d.ts +2 -0
  118. package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
  119. package/dist/lib/browser/page-review-judge.js +13 -0
  120. package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
  121. package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
  122. package/dist/lib/browser/qa-run.d.ts.map +1 -1
  123. package/dist/lib/browser/qa-run.js +1 -0
  124. package/dist/lib/browser/windows-chrome.d.ts +59 -0
  125. package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
  126. package/dist/lib/browser/windows-chrome.js +179 -0
  127. package/dist/lib/claude-desktop-share.d.ts +136 -0
  128. package/dist/lib/claude-desktop-share.d.ts.map +1 -0
  129. package/dist/lib/claude-desktop-share.js +394 -0
  130. package/dist/lib/disk-usage.d.ts +50 -0
  131. package/dist/lib/disk-usage.d.ts.map +1 -0
  132. package/dist/lib/disk-usage.js +303 -0
  133. package/dist/lib/docs-lint.d.ts +6 -0
  134. package/dist/lib/docs-lint.d.ts.map +1 -1
  135. package/dist/lib/docs-lint.js +8 -3
  136. package/dist/lib/guarded-remove.d.ts +34 -0
  137. package/dist/lib/guarded-remove.d.ts.map +1 -0
  138. package/dist/lib/guarded-remove.js +310 -0
  139. package/dist/lib/instructions/templates.js +8 -8
  140. package/dist/lib/tunnel/error-page.d.ts +1 -1
  141. package/dist/lib/tunnel/error-page.d.ts.map +1 -1
  142. package/dist/lib/tunnel/error-page.js +19 -8
  143. package/dist/lib/tunnel/gate.js +20 -1
  144. package/dist/lib/tunnel/path-scope.d.ts +22 -0
  145. package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
  146. package/dist/lib/tunnel/path-scope.js +62 -0
  147. package/dist/lib/tunnel/state.d.ts +5 -0
  148. package/dist/lib/tunnel/state.d.ts.map +1 -1
  149. package/dist/lib/tunnel/state.js +17 -3
  150. package/package.json +6 -1
  151. package/schemas/config.schema.json +91 -0
  152. package/src/commander.ts +19 -0
  153. package/src/commands/agents.ts +14 -0
  154. package/src/commands/artifacts.ts +171 -19
  155. package/src/commands/browse.ts +400 -57
  156. package/src/commands/claude-desktop.ts +61 -0
  157. package/src/commands/disk.ts +114 -0
  158. package/src/commands/doctor.ts +52 -1
  159. package/src/commands/files.ts +93 -2
  160. package/src/commands/init.ts +55 -5
  161. package/src/commands/rm.ts +115 -0
  162. package/src/commands/servers.ts +506 -0
  163. package/src/commands/tunnel.ts +102 -2
  164. package/src/core/adapters/profiles.ts +22 -1
  165. package/src/core/adapters/types.ts +10 -0
  166. package/src/core/agents/cli.ts +14 -6
  167. package/src/core/agents/render/session-context.ts +1 -0
  168. package/src/core/agents/state/live-coordination-view.ts +73 -0
  169. package/src/core/artifacts/delivery-card.ts +10 -2
  170. package/src/core/artifacts/index.ts +697 -44
  171. package/src/core/config.ts +183 -4
  172. package/src/core/diagnostics/bundle.ts +2 -1
  173. package/src/core/events/v3/producers/recorder.ts +16 -0
  174. package/src/core/events/v3/projection.ts +11 -1
  175. package/src/core/hooks/adapter/events.ts +19 -0
  176. package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
  177. package/src/core/hooks/adapter/wiring.ts +116 -8
  178. package/src/core/hooks/cli.ts +47 -6
  179. package/src/core/hooks/run-markers.ts +132 -0
  180. package/src/core/servers/index.ts +712 -0
  181. package/src/core/servers/net.ts +283 -0
  182. package/src/core/servers/tunnels.ts +39 -0
  183. package/src/core/storage/builtins.ts +1 -0
  184. package/src/core/workflow/engine.ts +38 -1
  185. package/src/core/workflow/sandbox-projection.ts +38 -2
  186. package/src/core/workflow/spawn-claude.ts +5 -1
  187. package/src/core/workflow/spawn-codex.ts +18 -1
  188. package/src/core/workflow/spawn-cursor.ts +5 -1
  189. package/src/core/workflow/spawn-opencode.ts +4 -1
  190. package/src/core/workflow/types.ts +62 -2
  191. package/src/core/workflow/worker-access.ts +525 -0
  192. package/src/lib/browser/batch-steps.ts +79 -0
  193. package/src/lib/browser/collect/collector.ts +306 -0
  194. package/src/lib/browser/collect/fields.ts +87 -0
  195. package/src/lib/browser/collect/index.ts +47 -0
  196. package/src/lib/browser/collect/page-driver.ts +327 -0
  197. package/src/lib/browser/collect/presets.ts +237 -0
  198. package/src/lib/browser/collect/scroll-plan.ts +179 -0
  199. package/src/lib/browser/index.ts +1 -0
  200. package/src/lib/browser/page-review-judge.ts +16 -0
  201. package/src/lib/browser/qa-run-contracts.ts +11 -0
  202. package/src/lib/browser/qa-run.ts +1 -0
  203. package/src/lib/browser/windows-chrome.ts +227 -0
  204. package/src/lib/claude-desktop-share.ts +511 -0
  205. package/src/lib/disk-usage.ts +347 -0
  206. package/src/lib/docs-lint.ts +8 -3
  207. package/src/lib/guarded-remove.ts +376 -0
  208. package/src/lib/instructions/templates.ts +8 -8
  209. package/src/lib/tunnel/error-page.ts +25 -9
  210. package/src/lib/tunnel/gate.ts +22 -1
  211. package/src/lib/tunnel/path-scope.ts +73 -0
  212. package/src/lib/tunnel/state.ts +20 -3
@@ -13,19 +13,68 @@
13
13
  import { existsSync, readFileSync } from "node:fs";
14
14
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
15
15
  import { fileURLToPath } from "node:url";
16
- import { resolveBinName } from "../../config.js";
16
+ import { codexWindowsBridgeConfig, resolveBinName } from "../../config.js";
17
17
  import { ADAPTER_SPECS, } from "./events.js";
18
18
  import { checkOpenCodePlugin, isOwnedOpenCodePlugin } from "./opencode-plugin.js";
19
19
  /** Bound Cursor Stop `followup_message` retries so a missed ritual cannot loop 5 times. */
20
20
  export const CURSOR_STOP_FOLLOWUP_LOOP_LIMIT = 2;
21
21
  /** Build a hook entry in the adapter's shape. */
22
22
  export function makeEntry(shape, command, extras) {
23
- if (shape !== "cursor")
24
- return { hooks: [{ type: "command", command }] };
23
+ if (shape !== "cursor") {
24
+ return {
25
+ hooks: [
26
+ orderHandlerKeys({
27
+ type: "command",
28
+ command,
29
+ commandWindows: extras?.commandWindows,
30
+ timeout: extras?.timeout,
31
+ }),
32
+ ],
33
+ };
34
+ }
25
35
  return extras?.loop_limit !== undefined
26
36
  ? { command, loop_limit: extras.loop_limit }
27
37
  : { command };
28
38
  }
39
+ /**
40
+ * Rebuild a handler with Harnery's fields first in a fixed order, dropping
41
+ * undefined ones and keeping any other fields after them.
42
+ */
43
+ export function orderHandlerKeys(handler) {
44
+ const { type, command, commandWindows, timeout, ...rest } = handler;
45
+ return {
46
+ type,
47
+ command,
48
+ ...(commandWindows !== undefined ? { commandWindows } : {}),
49
+ ...(timeout !== undefined ? { timeout } : {}),
50
+ ...rest,
51
+ };
52
+ }
53
+ /**
54
+ * The Windows command for an opted-in project (ADR 0197). It hands the hook to
55
+ * the bridge entry point when that is on PATH and the working directory is a
56
+ * WSL UNC path, and otherwise runs the usual command. `ProviderPath` drops the
57
+ * provider prefix that `Path` carries. The trailing `exit $LASTEXITCODE` is
58
+ * required: without it PowerShell reports any non-zero exit from inside the
59
+ * `if` block as 1, which would turn a hook's blocking exit 2 into a failure.
60
+ */
61
+ export function hookCommandWindows(spec, agentHookPath, subcommand, adapter, entryPoint) {
62
+ const direct = hookCommand(spec, agentHookPath, subcommand, adapter);
63
+ return (`if ((Get-Command ${entryPoint} -ErrorAction Ignore) -and $PWD.ProviderPath -like '\\\\wsl*') ` +
64
+ `{ ${entryPoint} ${subcommand} --adapter ${adapter} } else { ${direct} }; exit $LASTEXITCODE`);
65
+ }
66
+ /** The `commandWindows` init writes for one event, or undefined for none. */
67
+ export function expectedCommandWindows(spec, agentHookPath, subcommand, adapter, options) {
68
+ if (!spec.windowsBridgeHooks || !options?.windowsBridgeEntryPoint)
69
+ return undefined;
70
+ return hookCommandWindows(spec, agentHookPath, subcommand, adapter, options.windowsBridgeEntryPoint);
71
+ }
72
+ /** The `timeout` init writes on one event's Harnery hook, or undefined for none. */
73
+ export function hookTimeoutFor(spec, subcommand) {
74
+ if (!spec.hookTimeouts)
75
+ return undefined;
76
+ return spec.hookTimeouts.bySubcommand?.[subcommand] ?? spec.hookTimeouts.default;
77
+ }
29
78
  /** Pin Cursor Stop followups to Harnery's loop cap. Returns true when the entry changed. */
30
79
  export function applyCursorStopLoopLimit(group, subcommand) {
31
80
  if (subcommand !== "stop")
@@ -107,7 +156,15 @@ export function diffWiring(settings, spec, expected) {
107
156
  duplicates.push(event);
108
157
  if (expected && commands.length > 0) {
109
158
  const canonical = hookCommand(spec, expected.agentHookPath, event.subcommand, expected.adapter);
110
- if (commands.some((command) => command !== canonical))
159
+ const timeout = hookTimeoutFor(spec, event.subcommand);
160
+ const commandWindows = expectedCommandWindows(spec, expected.agentHookPath, event.subcommand, expected.adapter, expected);
161
+ const handlerDrift = groups.some((group) => "hooks" in group &&
162
+ Array.isArray(group.hooks) &&
163
+ group.hooks.some((hook) => typeof hook.command === "string" &&
164
+ commandWiresSubcommand(hook.command, event.subcommand) &&
165
+ ((timeout !== undefined && hook.timeout !== timeout) ||
166
+ (spec.windowsBridgeHooks === true && hook.commandWindows !== commandWindows))));
167
+ if (handlerDrift || commands.some((command) => command !== canonical))
111
168
  stale.push(event);
112
169
  }
113
170
  const wrongKey = Object.entries(hooks).some(([key, otherGroups]) => key !== event.settingsKey &&
@@ -222,6 +279,7 @@ export function loadAdapterWiring(projectRoot) {
222
279
  ? {
223
280
  agentHookPath: agentHookPathForProject(projectRoot, packageRoot),
224
281
  adapter: id,
282
+ windowsBridgeEntryPoint: codexWindowsBridgeConfig(projectRoot).entryPoint,
225
283
  }
226
284
  : undefined);
227
285
  if (diff.present.length === 0)
@@ -42,7 +42,9 @@ import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/
42
42
  import { stableScopeId } from "../workflow/scope-id.js";
43
43
  import { adapterBehavior } from "./adapter/behaviors/index.js";
44
44
  import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.js";
45
+ import { ADAPTER_SPECS } from "./adapter/events.js";
45
46
  import { extractBashCommand, extractToolDescription, normalizeEventName, parsePayload, } from "./adapter/parse.js";
47
+ import { hookTimeoutFor } from "./adapter/wiring.js";
46
48
  import { scheduleBackupSnapshot } from "./backup-schedule.js";
47
49
  import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
48
50
  import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
@@ -52,9 +54,11 @@ import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/
52
54
  import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
53
55
  import { resolveOwner } from "./resolve/owner.js";
54
56
  import { inspectSessionNameDisplayImmediately, scanAssistantStatusBoxPresent, scanStatusBoxPresent, scanTranscriptRuntime, } from "./resolve/transcript.js";
57
+ import { clearHookRunMarker, writeHookRunMarker } from "./run-markers.js";
55
58
  import { sessionNamePresence } from "./session-name-presence.js";
56
59
  import { shellWaiterReason } from "./shell-waiter.js";
57
60
  let hookHealthState;
61
+ let hookRunMarkerPath = null;
58
62
  function parseArgv(argv) {
59
63
  const out = { eventName: null, extra: [] };
60
64
  for (let i = 0; i < argv.length; i++) {
@@ -236,7 +240,11 @@ function buildEventData(eventType, ctx) {
236
240
  const forkedFrom = undefined;
237
241
  // Assign (or recover) name + kind in-process. Idempotent: resume
238
242
  // returns the original name; new owner consumes a counter slot.
239
- const assigned = assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
243
+ // Workflow children stay unnamed, as on the tool path: a pool name
244
+ // would make them the addressee of mail held for that name.
245
+ const assigned = coordEnv("WORKFLOW_CHILD") === "1"
246
+ ? undefined
247
+ : assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
240
248
  // Write the adapter pid-map row so `harn agents whoami` ppid-walks find
241
249
  // this owner. Prefer the payload pid (the actual claude binary), then the
242
250
  // anchor walk (the `node` ancestor for Cursor, which has no payload pid),
@@ -299,7 +307,8 @@ function buildEventData(eventType, ctx) {
299
307
  // SessionStart. Mint durable display identity here so a later
300
308
  // SessionStart remains idempotent and Codec is not stuck on the
301
309
  // 8-character native id.
302
- if (!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
310
+ if (coordEnv("WORKFLOW_CHILD") !== "1" &&
311
+ !readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
303
312
  assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session");
304
313
  }
305
314
  const prompt = p?.prompt ?? "";
@@ -475,6 +484,23 @@ async function main() {
475
484
  const { eventName, extra } = parseArgv(process.argv.slice(2));
476
485
  const adapter = detectAdapter(process.argv.slice(2));
477
486
  const behavior = adapter ? adapterBehavior(adapter) : null;
487
+ // Adapters that kill a hook at its timeout get a start marker, so a killed
488
+ // run leaves evidence for doctor (ADR 0197). Written before stdin is read.
489
+ const markerSpec = adapter ? ADAPTER_SPECS[adapter] : undefined;
490
+ const markerTimeout = markerSpec && eventName && markerSpec.events.some((e) => e.subcommand === eventName)
491
+ ? hookTimeoutFor(markerSpec, eventName)
492
+ : undefined;
493
+ if (markerTimeout !== undefined && adapter && eventName && coordEnv("AGENT_COORD_OFF") !== "1") {
494
+ const markerRoot = resolveCoordRoot(process.cwd());
495
+ if (markerRoot) {
496
+ hookRunMarkerPath = writeHookRunMarker(markerRoot, {
497
+ event: eventName,
498
+ adapter,
499
+ pid: process.pid,
500
+ timeout_sec: markerTimeout,
501
+ });
502
+ }
503
+ }
478
504
  const raw = await readStdin();
479
505
  hookHealthState = beginHookHealth({
480
506
  started_at_ms: hookStartedAt,
@@ -941,9 +967,12 @@ async function handleSessionStarted(run) {
941
967
  const { autoCleanArtifacts } = await import("../artifacts/index.js");
942
968
  const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
943
969
  const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
944
- // Effect: throttled daily sweep of expired artifact workspaces (guarded,
945
- // managed-expired only; see autoCleanArtifacts). Best-effort like every
946
- // janitor here: a failure logs and never blocks session start.
970
+ // Effect: throttled hourly artifact sweep (see autoCleanArtifacts). It
971
+ // deletes expired workspaces, plus unheld workspaces over the per-bundle
972
+ // ceiling or repository budget once they have been idle past
973
+ // artifacts.idle_grace_hours. Each deletion is rechecked first and recorded
974
+ // in .harnery/artifact-deletions.jsonl. Best-effort like every janitor
975
+ // here: a failure logs and never blocks session start.
947
976
  try {
948
977
  autoCleanArtifacts(coordRoot);
949
978
  }
@@ -1291,7 +1320,8 @@ async function handlePostToolUse(run) {
1291
1320
  // status is the wrong place to ask.
1292
1321
  try {
1293
1322
  const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
1294
- const name = sessionNameDisplayPending(row);
1323
+ // A workflow child has no user-visible title to set.
1324
+ const name = coordEnv("WORKFLOW_CHILD") === "1" ? undefined : sessionNameDisplayPending(row);
1295
1325
  if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
1296
1326
  // Record the title before asking for it. The suggestion can still
1297
1327
  // change afterwards (an assigned-name rewrite, a lifecycle re-mint, a
@@ -1469,6 +1499,8 @@ function reportSessionNameObservationUnavailable(coordRoot, instanceId, adapter,
1469
1499
  process.stderr.write(`Harnery: session-name observation unavailable (${reason}); allowing this ${eventName} without display enforcement. The name remains pending.\n`);
1470
1500
  }
1471
1501
  async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter, payload, coordination) {
1502
+ if (coordEnv("WORKFLOW_CHILD") === "1")
1503
+ return true;
1472
1504
  const name = sessionNameDisplayPending(coordination);
1473
1505
  if (!name)
1474
1506
  return true;
@@ -1830,6 +1862,11 @@ function cleanupSessionEnd(coordRoot, instanceId) {
1830
1862
  }
1831
1863
  }
1832
1864
  async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "", hostPromptContext = "") {
1865
+ // Workflow children get no prompt-time coordination context, matching
1866
+ // SessionStart. The turn ritual is not enforced for them, the naming nudge
1867
+ // would register them as named peers, and rendering drains peer mail.
1868
+ if (coordEnv("WORKFLOW_CHILD") === "1")
1869
+ return false;
1833
1870
  const behavior = adapterBehavior(adapter);
1834
1871
  const { renderPromptContext } = await import("../agents/render/prompt-context.js");
1835
1872
  let additionalContext = renderPromptContext({
@@ -1943,6 +1980,7 @@ function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
1943
1980
  }
1944
1981
  main()
1945
1982
  .then(async (code) => {
1983
+ clearHookRunMarker(hookRunMarkerPath);
1946
1984
  writeHookHealthCompletion(hookHealthState, {
1947
1985
  finished_at_ms: performance.now(),
1948
1986
  finished_rss_bytes: process.memoryUsage().rss,
@@ -1957,6 +1995,7 @@ main()
1957
1995
  pid: process.pid,
1958
1996
  phase: "top-level",
1959
1997
  });
1998
+ clearHookRunMarker(hookRunMarkerPath);
1960
1999
  writeHookHealthCompletion(hookHealthState, {
1961
2000
  finished_at_ms: performance.now(),
1962
2001
  finished_rss_bytes: process.memoryUsage().rss,
@@ -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,211 @@
1
+ /**
2
+ * Local server registry.
3
+ *
4
+ * Every command that starts a long-lived local server (a preview UI, a tuning
5
+ * panel, a tunnel gate, a dashboard) writes one record under
6
+ * `.harnery/servers/<id>.json` and removes it when the server stops. One
7
+ * reader can then list every running server, stop any of them, and clean up
8
+ * session servers whose starting agent is gone.
9
+ *
10
+ * Two kinds:
11
+ * - `service`: one long-running copy (a dashboard, a tunnel). Listed and
12
+ * stoppable by hand; never stopped automatically.
13
+ * - `session`: one copy per piece of work (a preview for one scene). Eligible
14
+ * for `gc` once its starting agent has ended or been unobserved for a long
15
+ * time AND nobody has used the server for the idle window.
16
+ *
17
+ * Fail-closed rules: a record whose pid was recycled is treated as dead, never
18
+ * signalled; an unreadable coordination view makes every owner `unknown`,
19
+ * which `gc` never stops; a record without an owner is never stopped
20
+ * automatically.
21
+ */
22
+ import { type ScanSupport } from "./net.js";
23
+ export { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, lsofConnectionCounts, lsofListeningSockets, parseElapsed, parseLsofListen, parseProcNetTcp, processStartedAt, psProcessInfo, readProcessInfo, scanSupport, } from "./net.js";
24
+ export { tunnelServerId, tunnelServerInput } from "./tunnels.js";
25
+ export declare const SERVER_RECORD_SCHEMA_VERSION = 1;
26
+ export declare const SERVER_DEFAULT_IDLE_HOURS = 2;
27
+ export declare const SERVER_DEFAULT_OWNER_STALE_HOURS = 24;
28
+ export type ServerKind = "service" | "session";
29
+ export interface ServerRecord {
30
+ schema_version: typeof SERVER_RECORD_SCHEMA_VERSION;
31
+ id: string;
32
+ kind: ServerKind;
33
+ /** Free-form family name: `preview`, `tunnel`, `dashboard`, ... */
34
+ type: string;
35
+ label: string;
36
+ url?: string;
37
+ port?: number;
38
+ /** The process that owns the server's lifetime (signalled on stop). */
39
+ pid: number;
40
+ start_token?: string;
41
+ /** Further processes that belong to this server (helpers, children). */
42
+ pids?: number[];
43
+ host: string;
44
+ /** What the server serves, usually a directory. */
45
+ scope?: string;
46
+ cwd?: string;
47
+ log?: string;
48
+ /** Graceful stop command; signals are the fallback. */
49
+ stop_argv?: string[];
50
+ owner?: {
51
+ instance_id: string;
52
+ name?: string;
53
+ };
54
+ started_at: string;
55
+ registered_at: string;
56
+ last_active_at?: string;
57
+ }
58
+ export interface RegisterServerInput {
59
+ id?: string;
60
+ kind: ServerKind;
61
+ type: string;
62
+ label?: string;
63
+ url?: string;
64
+ port?: number;
65
+ pid?: number;
66
+ pids?: number[];
67
+ scope?: string;
68
+ cwd?: string;
69
+ log?: string;
70
+ stop_argv?: string[];
71
+ started_at?: string;
72
+ /** `null` records no owner; omitted resolves the calling agent session. */
73
+ owner?: {
74
+ instance_id: string;
75
+ name?: string;
76
+ } | null;
77
+ }
78
+ export interface ServerOptions {
79
+ coordRoot?: string | null;
80
+ }
81
+ export type ServerState = "running" | "dead" | "other-host";
82
+ export type OwnerState = "live" | "abandoned" | "ended" | "unknown" | "none";
83
+ export interface ServerView {
84
+ record: ServerRecord;
85
+ state: ServerState;
86
+ owner_state: OwnerState;
87
+ connections: number | null;
88
+ last_active_at: string;
89
+ idle_ms: number;
90
+ }
91
+ export interface UnregisteredListener {
92
+ pid: number;
93
+ port: number;
94
+ address: string;
95
+ command: string;
96
+ cwd: string | null;
97
+ }
98
+ export interface ServersReport {
99
+ schema_version: 1;
100
+ coord_root: string;
101
+ scan: ScanSupport;
102
+ servers: ServerView[];
103
+ unregistered: UnregisteredListener[];
104
+ pruned: string[];
105
+ }
106
+ export declare function serversDir(coordRoot: string): string;
107
+ /** A stable id for one server family serving one scope. */
108
+ export declare function serverId(type: string, scope?: string): string;
109
+ export declare function isValidServerId(id: string): boolean;
110
+ /**
111
+ * Record a running server. Returns null when no coordination root resolves
112
+ * (a tool running outside any Harnery project), so callers never fail
113
+ * because registration was impossible.
114
+ */
115
+ export declare function registerServer(input: RegisterServerInput, options?: ServerOptions): ServerRecord | null;
116
+ /** Best-effort registration that never throws (for server start paths). */
117
+ export declare function tryRegisterServer(input: RegisterServerInput, options?: ServerOptions): ServerRecord | null;
118
+ /**
119
+ * Remove a server's record. With `pid`, only a record still naming that pid
120
+ * is removed, so a replacement that re-registered the same id survives.
121
+ */
122
+ export declare function unregisterServer(id: string, options?: ServerOptions & {
123
+ pid?: number;
124
+ }): boolean;
125
+ export declare function readServer(id: string, options?: ServerOptions): ServerRecord | null;
126
+ /** Mark a server as used now. */
127
+ export declare function touchServer(id: string, options?: ServerOptions & {
128
+ now?: Date;
129
+ }): void;
130
+ /**
131
+ * A request hook for servers that want precise idle tracking: call the
132
+ * returned function on every request; it writes at most once a minute.
133
+ */
134
+ export declare function createServerToucher(id: string, options?: ServerOptions): () => void;
135
+ export declare function readServerRecords(options?: ServerOptions): ServerRecord[];
136
+ export declare function pidAlive(pid: number): boolean;
137
+ export declare function serverState(record: ServerRecord): ServerState;
138
+ export interface ListServersOptions extends ServerOptions {
139
+ /** Look for listening processes inside the project that never registered. */
140
+ scan?: boolean;
141
+ /** Record observed connections as activity (default true). */
142
+ sample?: boolean;
143
+ /** Delete records of servers that are no longer running (default true). */
144
+ prune?: boolean;
145
+ ownerStaleHours?: number;
146
+ now?: Date;
147
+ }
148
+ /** Read every record, observe it, and optionally find unregistered listeners. */
149
+ export declare function listServers(options?: ListServersOptions): ServersReport;
150
+ /** Register running tunnels that have no current record. Returns adopted ids. */
151
+ export declare function adoptRunningTunnels(coordRoot: string): string[];
152
+ /**
153
+ * Listening processes that run inside the project (working directory or
154
+ * command line within the coordination root) but are not covered by any
155
+ * registered server: not a recorded pid, not a descendant of one, and not on
156
+ * a recorded port.
157
+ */
158
+ export declare function findUnregisteredListeners(coordRoot: string, servers: Array<Pick<ServerView, "record" | "state">>): UnregisteredListener[];
159
+ export interface GcPolicy {
160
+ idleHours?: number;
161
+ ownerStaleHours?: number;
162
+ }
163
+ export interface GcDecision {
164
+ id: string;
165
+ action: "stop" | "keep";
166
+ reason: string;
167
+ }
168
+ /**
169
+ * Decide which session servers to stop. Pure: the caller supplies the views.
170
+ * A server is stopped only when all hold: kind `session`, running on this
171
+ * host, its owner ended or went unobserved past the stale window, and it has
172
+ * been idle for the idle window.
173
+ */
174
+ export declare function planServerGc(views: ServerView[], policy?: GcPolicy): GcDecision[];
175
+ export interface StopResult {
176
+ id: string;
177
+ stopped: boolean;
178
+ method: "not-running" | "stop-command" | "signal" | "refused";
179
+ detail?: string;
180
+ }
181
+ /**
182
+ * Stop one server: its graceful stop command first when recorded, then
183
+ * SIGTERM to its process group, then SIGKILL after the grace period. A pid
184
+ * whose start token no longer matches is never signalled.
185
+ */
186
+ export declare function stopServer(record: ServerRecord, options?: ServerOptions & {
187
+ graceMs?: number;
188
+ }): Promise<StopResult>;
189
+ /**
190
+ * The first free port in the configured range for `type`
191
+ * (`.harnery/config.jsonc` `servers.port_ranges`). Skips ports any process is
192
+ * listening on and ports a registered server claims. Returns null when the
193
+ * type has no configured range; throws when the range is exhausted.
194
+ */
195
+ export declare function allocateServerPort(type: string, options?: ServerOptions): number | null;
196
+ export interface AdoptOptions extends ServerOptions {
197
+ type?: string;
198
+ kind?: ServerKind;
199
+ label?: string;
200
+ scope?: string;
201
+ /** `self` (default) records the adopting agent as owner; `none` records no owner. */
202
+ owner?: "self" | "none";
203
+ }
204
+ /**
205
+ * Register a running server that never registered itself. Only a pid the scan
206
+ * reports as an unregistered listener inside the project can be adopted.
207
+ */
208
+ export declare function adoptServer(pid: number, options?: AdoptOptions): ServerRecord;
209
+ /** Ensure the registry directory exists (for hosts that write records directly). */
210
+ export declare function ensureServersDir(coordRoot: string): string;
211
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/servers/index.ts"],"names":[],"mappings":"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"}