harnery 0.46.1 → 0.47.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 (176) hide show
  1. package/README.md +24 -0
  2. package/bin/README.md +10 -0
  3. package/bin/agent-coord.mjs +3 -0
  4. package/bin/agent-hook.mjs +3 -0
  5. package/bin/harn.mjs +3 -0
  6. package/bin/run.mjs +43 -0
  7. package/dist/commander.d.ts +18 -6
  8. package/dist/commander.d.ts.map +1 -1
  9. package/dist/commander.js +74 -2
  10. package/dist/commands/agents.d.ts +25 -0
  11. package/dist/commands/agents.d.ts.map +1 -1
  12. package/dist/commands/agents.js +70 -27
  13. package/dist/commands/autoupdate.d.ts +12 -0
  14. package/dist/commands/autoupdate.d.ts.map +1 -0
  15. package/dist/commands/autoupdate.js +190 -0
  16. package/dist/commands/computer.d.ts +45 -0
  17. package/dist/commands/computer.d.ts.map +1 -0
  18. package/dist/commands/computer.js +509 -0
  19. package/dist/commands/docs.js +3 -1
  20. package/dist/commands/fetch.js +11 -1
  21. package/dist/commands/grep.d.ts +72 -0
  22. package/dist/commands/grep.d.ts.map +1 -1
  23. package/dist/commands/grep.js +200 -47
  24. package/dist/commands/init.d.ts.map +1 -1
  25. package/dist/commands/init.js +15 -9
  26. package/dist/commands/rm.js +2 -2
  27. package/dist/commands/web.d.ts.map +1 -1
  28. package/dist/commands/web.js +2 -0
  29. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  30. package/dist/core/agents/canonical-emit.js +2 -3
  31. package/dist/core/agents/coord-bin.d.ts +3 -0
  32. package/dist/core/agents/coord-bin.d.ts.map +1 -1
  33. package/dist/core/agents/coord-bin.js +6 -0
  34. package/dist/core/agents/coord-client.d.ts.map +1 -1
  35. package/dist/core/agents/coord-client.js +6 -2
  36. package/dist/core/agents/render/prompt-context.d.ts +55 -7
  37. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  38. package/dist/core/agents/render/prompt-context.js +122 -48
  39. package/dist/core/agents/state/proc-start.d.ts +3 -1
  40. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  41. package/dist/core/agents/state/proc-start.js +26 -4
  42. package/dist/core/autoupdate/engine.d.ts +97 -0
  43. package/dist/core/autoupdate/engine.d.ts.map +1 -0
  44. package/dist/core/autoupdate/engine.js +548 -0
  45. package/dist/core/autoupdate/fingerprint.d.ts +16 -0
  46. package/dist/core/autoupdate/fingerprint.d.ts.map +1 -0
  47. package/dist/core/autoupdate/fingerprint.js +73 -0
  48. package/dist/core/autoupdate/git.d.ts +30 -0
  49. package/dist/core/autoupdate/git.d.ts.map +1 -0
  50. package/dist/core/autoupdate/git.js +82 -0
  51. package/dist/core/autoupdate/index.d.ts +6 -0
  52. package/dist/core/autoupdate/index.d.ts.map +1 -0
  53. package/dist/core/autoupdate/index.js +4 -0
  54. package/dist/core/autoupdate/state.d.ts +59 -0
  55. package/dist/core/autoupdate/state.d.ts.map +1 -0
  56. package/dist/core/autoupdate/state.js +214 -0
  57. package/dist/core/autoupdate/types.d.ts +113 -0
  58. package/dist/core/autoupdate/types.d.ts.map +1 -0
  59. package/dist/core/autoupdate/types.js +35 -0
  60. package/dist/core/config.d.ts +53 -1
  61. package/dist/core/config.d.ts.map +1 -1
  62. package/dist/core/config.js +114 -1
  63. package/dist/core/events/v3/bootstrap.js +1 -0
  64. package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
  65. package/dist/core/events/v3/runtime-build.js +1 -0
  66. package/dist/core/hooks/adapter/wiring.d.ts +12 -2
  67. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  68. package/dist/core/hooks/adapter/wiring.js +38 -8
  69. package/dist/core/hooks/autoupdate-cue.d.ts +29 -0
  70. package/dist/core/hooks/autoupdate-cue.d.ts.map +1 -0
  71. package/dist/core/hooks/autoupdate-cue.js +53 -0
  72. package/dist/core/hooks/cli.js +28 -3
  73. package/dist/core/hooks/effects/index.js +4 -4
  74. package/dist/core/hooks/resolve/coord-root.js +1 -0
  75. package/dist/core/semantic/contract.d.ts +3 -3
  76. package/dist/core/semantic/service.d.ts.map +1 -1
  77. package/dist/core/semantic/service.js +4 -2
  78. package/dist/core/storage/modes.d.ts +1 -1
  79. package/dist/core/storage/modes.d.ts.map +1 -1
  80. package/dist/core/storage/modes.js +5 -1
  81. package/dist/core/workflow/durable-record.d.ts +2 -0
  82. package/dist/core/workflow/durable-record.d.ts.map +1 -1
  83. package/dist/core/workflow/durable-record.js +13 -1
  84. package/dist/core/workflow/engine.js +4 -0
  85. package/dist/core/workflow/spawn-claude.d.ts +7 -0
  86. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  87. package/dist/core/workflow/spawn-claude.js +16 -4
  88. package/dist/core/workflow/specialists.d.ts.map +1 -1
  89. package/dist/core/workflow/specialists.js +1 -0
  90. package/dist/core/workflow/types.d.ts +10 -0
  91. package/dist/core/workflow/types.d.ts.map +1 -1
  92. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  93. package/dist/core/workflow/workspaces/leases.js +1 -9
  94. package/dist/lib/computer/cua.d.ts +50 -0
  95. package/dist/lib/computer/cua.d.ts.map +1 -0
  96. package/dist/lib/computer/cua.js +474 -0
  97. package/dist/lib/computer/host.d.ts +19 -0
  98. package/dist/lib/computer/host.d.ts.map +1 -0
  99. package/dist/lib/computer/host.js +72 -0
  100. package/dist/lib/computer/lease.d.ts +13 -0
  101. package/dist/lib/computer/lease.d.ts.map +1 -0
  102. package/dist/lib/computer/lease.js +269 -0
  103. package/dist/lib/computer/types.d.ts +163 -0
  104. package/dist/lib/computer/types.d.ts.map +1 -0
  105. package/dist/lib/computer/types.js +10 -0
  106. package/dist/lib/docs-metadata-v2.d.ts.map +1 -1
  107. package/dist/lib/docs-metadata-v2.js +6 -5
  108. package/dist/lib/exec.d.ts.map +1 -1
  109. package/dist/lib/exec.js +1 -0
  110. package/dist/lib/guarded-remove.d.ts.map +1 -1
  111. package/dist/lib/guarded-remove.js +5 -4
  112. package/dist/lib/headless/index.d.ts +33 -0
  113. package/dist/lib/headless/index.d.ts.map +1 -1
  114. package/dist/lib/headless/index.js +38 -2
  115. package/dist/lib/http/client.d.ts +2 -9
  116. package/dist/lib/http/client.d.ts.map +1 -1
  117. package/dist/lib/http/client.js +30 -9
  118. package/dist/lib/instructions/apply.d.ts +9 -0
  119. package/dist/lib/instructions/apply.d.ts.map +1 -1
  120. package/dist/lib/instructions/apply.js +92 -14
  121. package/dist/lib/instructions/git-hooks.d.ts.map +1 -1
  122. package/dist/lib/instructions/git-hooks.js +8 -4
  123. package/dist/lib/instructions/templates.d.ts +11 -0
  124. package/dist/lib/instructions/templates.d.ts.map +1 -1
  125. package/dist/lib/instructions/templates.js +81 -1
  126. package/package.json +5 -5
  127. package/schemas/config.schema.json +89 -1
  128. package/src/commander.ts +98 -8
  129. package/src/commands/agents.ts +100 -32
  130. package/src/commands/autoupdate.ts +250 -0
  131. package/src/commands/computer.ts +682 -0
  132. package/src/commands/docs.ts +3 -1
  133. package/src/commands/fetch.ts +11 -1
  134. package/src/commands/grep.ts +274 -59
  135. package/src/commands/init.ts +17 -7
  136. package/src/commands/rm.ts +2 -2
  137. package/src/commands/web.ts +2 -0
  138. package/src/core/agents/canonical-emit.ts +2 -3
  139. package/src/core/agents/coord-bin.ts +30 -0
  140. package/src/core/agents/coord-client.ts +6 -2
  141. package/src/core/agents/render/prompt-context.ts +152 -53
  142. package/src/core/agents/state/proc-start.ts +29 -5
  143. package/src/core/autoupdate/engine.ts +691 -0
  144. package/src/core/autoupdate/fingerprint.ts +89 -0
  145. package/src/core/autoupdate/git.ts +120 -0
  146. package/src/core/autoupdate/index.ts +29 -0
  147. package/src/core/autoupdate/state.ts +269 -0
  148. package/src/core/autoupdate/types.ts +135 -0
  149. package/src/core/config.ts +142 -1
  150. package/src/core/events/v3/bootstrap.ts +1 -0
  151. package/src/core/events/v3/runtime-build.ts +1 -0
  152. package/src/core/hooks/adapter/wiring.ts +51 -6
  153. package/src/core/hooks/autoupdate-cue.ts +72 -0
  154. package/src/core/hooks/cli.ts +27 -1
  155. package/src/core/hooks/effects/index.ts +4 -4
  156. package/src/core/hooks/resolve/coord-root.ts +1 -0
  157. package/src/core/semantic/service.ts +6 -2
  158. package/src/core/storage/modes.ts +7 -1
  159. package/src/core/workflow/durable-record.ts +15 -1
  160. package/src/core/workflow/engine.ts +3 -0
  161. package/src/core/workflow/spawn-claude.ts +16 -4
  162. package/src/core/workflow/specialists.ts +1 -0
  163. package/src/core/workflow/types.ts +10 -0
  164. package/src/core/workflow/workspaces/leases.ts +1 -9
  165. package/src/lib/computer/cua.ts +572 -0
  166. package/src/lib/computer/host.ts +87 -0
  167. package/src/lib/computer/lease.ts +299 -0
  168. package/src/lib/computer/types.ts +177 -0
  169. package/src/lib/docs-metadata-v2.ts +24 -5
  170. package/src/lib/exec.ts +1 -0
  171. package/src/lib/guarded-remove.ts +5 -3
  172. package/src/lib/headless/index.ts +66 -2
  173. package/src/lib/http/client.ts +33 -19
  174. package/src/lib/instructions/apply.ts +100 -12
  175. package/src/lib/instructions/git-hooks.ts +8 -4
  176. package/src/lib/instructions/templates.ts +95 -1
@@ -11,7 +11,8 @@
11
11
  * `coord` (heartbeat freshness), `logs` (structured-log storage budgets),
12
12
  * `artifacts` (working-file retention),
13
13
  * `backup` (restic repo/password/prune policy), `sync` (rclone
14
- * remote/prefix), and `web` (dashboard port) sections. The `files` deny/override section
14
+ * remote/prefix), `computer` (opt-in desktop control), `autoUpdate` (activity-fired
15
+ * project self-update), and `web` (dashboard port) sections. The `files` deny/override section
15
16
  * is parsed separately by `web/lib/files.ts`.
16
17
  *
17
18
  * Env vars and CLI flags override any config value per invocation (each accessor
@@ -23,6 +24,7 @@ import { homedir } from "node:os";
23
24
  import { isAbsolute, join, resolve } from "node:path";
24
25
  import { coordEnv } from "../lib/env.js";
25
26
  import { DEFAULT_WEB_PORT } from "../lib/local-file-url.js";
27
+ import { AUTOUPDATE_DEFAULTS } from "./autoupdate/types.js";
26
28
  import { resolveEventLedgerRotateActiveBytesV3 } from "./events/v3/rotation-config.js";
27
29
  import { parsePromptContextConfig, } from "./hooks/prompt-context/contract.js";
28
30
  import { findCoordRoot } from "./hooks/resolve/coord-root.js";
@@ -320,6 +322,17 @@ export function codexWindowsBridgeConfig(projectRoot) {
320
322
  }
321
323
  return { entryPoint, error: null };
322
324
  }
325
+ /**
326
+ * Per-project opt-in that makes `init` write the same adapter hook commands on
327
+ * every OS: `agent-hook` from PATH, plus a native Codex `commandWindows`. A
328
+ * project that commits its settings files and is cloned on Windows and POSIX
329
+ * needs it, because the default form depends on the OS that ran `init`. Read
330
+ * from the project config only, for the same reason as the Windows bridge.
331
+ * Anything but `true` keeps the default.
332
+ */
333
+ export function portableHookCommands(projectRoot) {
334
+ return readProjectConfig(projectRoot).hooks?.portableCommands === true;
335
+ }
323
336
  export function hostPromptReminder(coordRoot) {
324
337
  const root = coordRoot ?? findCoordRoot();
325
338
  if (!root)
@@ -480,6 +493,106 @@ export function readAdmissionConfig(coordRoot) {
480
493
  : {}),
481
494
  };
482
495
  }
496
+ /**
497
+ * Desktop-control settings from `.harnery/config.jsonc`:
498
+ *
499
+ * ```jsonc
500
+ * "computer": { "enabled": true, "idle_seconds": 120, "wait_seconds": 30 }
501
+ * ```
502
+ *
503
+ * Project config only: enabling desktop control is a per-project decision, so a
504
+ * user-global value never turns it on. Malformed values are dropped one by one;
505
+ * `enabled` is true only when the project sets it to exactly `true`.
506
+ */
507
+ export function readComputerConfig(coordRoot) {
508
+ const root = coordRoot ?? findCoordRoot();
509
+ if (!root)
510
+ return { enabled: false };
511
+ const raw = readProjectConfig(root).computer;
512
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
513
+ return { enabled: false };
514
+ const record = raw;
515
+ const idle = positiveNumber(record.idle_seconds, 86_400);
516
+ const wait = record.wait_seconds;
517
+ const waitOk = typeof wait === "number" && Number.isFinite(wait) && wait >= 0 && wait <= 3600;
518
+ return {
519
+ enabled: record.enabled === true,
520
+ ...(idle !== undefined ? { idle_seconds: idle } : {}),
521
+ ...(waitOk ? { wait_seconds: wait } : {}),
522
+ };
523
+ }
524
+ // Project-only parse cache keyed by the file's stat signature. Hooks call the
525
+ // auto-update reader on every prompt, so an unchanged file costs one stat.
526
+ let projectCache = null;
527
+ function readProjectConfigCached(root) {
528
+ const p = join(root, ".harnery", "config.jsonc");
529
+ const signature = statSignature(p);
530
+ if (signature === null)
531
+ return {};
532
+ if (projectCache && projectCache.path === p && projectCache.signature === signature) {
533
+ return projectCache.cfg;
534
+ }
535
+ const cfg = parseConfigFile(p);
536
+ projectCache = { path: p, signature, cfg };
537
+ return cfg;
538
+ }
539
+ function clampInteger(value, min, max, fallback) {
540
+ if (typeof value !== "number" || !Number.isFinite(value))
541
+ return fallback;
542
+ return Math.min(max, Math.max(min, Math.round(value)));
543
+ }
544
+ /** Keep each argv that is a non-empty array of non-empty strings; drop the rest. */
545
+ function argvList(value) {
546
+ if (!Array.isArray(value))
547
+ return [];
548
+ const out = [];
549
+ for (const entry of value) {
550
+ if (!Array.isArray(entry) || entry.length === 0)
551
+ continue;
552
+ if (!entry.every((part) => typeof part === "string" && part.length > 0))
553
+ continue;
554
+ out.push([...entry]);
555
+ }
556
+ return out;
557
+ }
558
+ /**
559
+ * Project self-update settings from `.harnery/config.jsonc` (ADR 0229):
560
+ *
561
+ * ```jsonc
562
+ * "autoUpdate": {
563
+ * "enabled": true,
564
+ * "steps": [["bin/pull-all"]],
565
+ * "dailySteps": [["bin/doctor", "--quiet"]],
566
+ * "watch": ["~/.local/share/example/hooks.json"],
567
+ * "checkMinutes": 15,
568
+ * "timeoutMinutes": 20,
569
+ * "cleanup": true
570
+ * }
571
+ * ```
572
+ *
573
+ * Project config only: running commands on activity is a per-project
574
+ * decision, so a user-global value never turns it on. Null when the project
575
+ * has no `autoUpdate` section. Malformed values are dropped one by one;
576
+ * `enabled` is true only when the project sets it to exactly `true`.
577
+ */
578
+ export function readAutoUpdateConfig(coordRoot) {
579
+ const raw = readProjectConfigCached(coordRoot).autoUpdate;
580
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
581
+ return null;
582
+ const record = raw;
583
+ const watch = Array.isArray(record.watch)
584
+ ? record.watch.filter((p) => typeof p === "string" && p.trim().length > 0)
585
+ : [];
586
+ return {
587
+ enabled: record.enabled === true,
588
+ checkMinutes: clampInteger(record.checkMinutes, 1, 1440, AUTOUPDATE_DEFAULTS.checkMinutes),
589
+ steps: argvList(record.steps),
590
+ dailySteps: argvList(record.dailySteps),
591
+ watch,
592
+ timeoutMinutes: clampInteger(record.timeoutMinutes, 1, 240, AUTOUPDATE_DEFAULTS.timeoutMinutes),
593
+ cleanup: record.cleanup !== false,
594
+ };
595
+ }
483
596
  /**
484
597
  * Whether this repo pins workflow runs to subscription billing:
485
598
  * `.harnery/config.jsonc` `{ "workflow": { "subscriptionOnly": true } }`.
@@ -443,6 +443,7 @@ function rootOfHarnery() {
443
443
  }
444
444
  function repositoryBuild(root) {
445
445
  const result = spawnSync("git", ["-C", root, "rev-parse", "HEAD"], {
446
+ windowsHide: true,
446
447
  encoding: "utf8",
447
448
  timeout: 5000,
448
449
  });
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-build.d.ts","sourceRoot":"","sources":["../../../../src/core/events/v3/runtime-build.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAuBnD;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qFAAqF;AACrF,wBAAgB,qBAAqB,IAAI,YAAY,CAkBpD;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,eAAe,EAAE,YAAY,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,kBAAkB,CA0BxE"}
1
+ {"version":3,"file":"runtime-build.d.ts","sourceRoot":"","sources":["../../../../src/core/events/v3/runtime-build.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE3E;;;;;;GAMG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAwBnD;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qFAAqF;AACrF,wBAAgB,qBAAqB,IAAI,YAAY,CAkBpD;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,eAAe,EAAE,YAAY,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,kBAAkB,CA0BxE"}
@@ -14,6 +14,7 @@ export function currentHarneryRuntimeBuild() {
14
14
  const root = harneryPackageRoot();
15
15
  if (existsSync(resolve(root, ".git"))) {
16
16
  const result = spawnSync("git", ["-C", root, "rev-parse", "HEAD"], {
17
+ windowsHide: true,
17
18
  encoding: "utf8",
18
19
  timeout: 5_000,
19
20
  });
@@ -65,7 +65,17 @@ export declare function hookCommandWindows(spec: AdapterSpec, agentHookPath: str
65
65
  export interface WiringOptions {
66
66
  /** Bridge entry point from `hooks.codexWindowsBridge`, or null when not opted in. */
67
67
  windowsBridgeEntryPoint?: string | null;
68
+ /**
69
+ * `hooks.portableCommands`: write the same `agent-hook` commands from PATH on
70
+ * every OS, plus a native Codex `commandWindows`, so one committed settings
71
+ * file is current everywhere.
72
+ */
73
+ portableCommands?: boolean;
74
+ /** The OS to write for. Defaults to the host; tests inject it. */
75
+ platform?: NodeJS.Platform;
68
76
  }
77
+ /** The wiring options a project's committed config selects. */
78
+ export declare function projectWiringOptions(projectRoot: string): WiringOptions;
69
79
  /** The `commandWindows` init writes for one event, or undefined for none. */
70
80
  export declare function expectedCommandWindows(spec: AdapterSpec, agentHookPath: string, subcommand: string, adapter: AdapterId, options?: WiringOptions): string | undefined;
71
81
  /** The `timeout` init writes on one event's Harnery hook, or undefined for none. */
@@ -83,14 +93,14 @@ export declare function commandWiresSubcommand(command: string, subcommand: stri
83
93
  /** Whether a command invokes Harnery's agent-hook binary at all. */
84
94
  export declare function isAgentHookCommand(command: string): boolean;
85
95
  /** The canonical command installed for one adapter event. */
86
- export declare function hookCommand(spec: AdapterSpec, agentHookPath: string, subcommand: string, adapter: AdapterId): string;
96
+ export declare function hookCommand(spec: AdapterSpec, agentHookPath: string, subcommand: string, adapter: AdapterId, options?: WiringOptions): string;
87
97
  /**
88
98
  * Pick a committed hook target that survives cloning the consumer elsewhere.
89
99
  * Projects that contain Harnery can use its repo-relative launcher. Standalone
90
100
  * consumers use the package's installed `agent-hook` executable from PATH
91
101
  * instead of recording a relative path back into one developer's package tree.
92
102
  */
93
- export declare function agentHookPathForProject(projectRoot: string, packageRoot: string): string;
103
+ export declare function agentHookPathForProject(projectRoot: string, packageRoot: string, options?: WiringOptions): string;
94
104
  export interface WiringDiff {
95
105
  /** Spec events not wired in the settings file. */
96
106
  missing: HookEvent[];
@@ -1 +1 @@
1
- {"version":3,"file":"wiring.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/adapter/wiring.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wiring.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/adapter/wiring.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@
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 { codexWindowsBridgeConfig, resolveBinName } from "../../config.js";
16
+ import { codexWindowsBridgeConfig, portableHookCommands, 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. */
@@ -63,10 +63,22 @@ export function hookCommandWindows(spec, agentHookPath, subcommand, adapter, ent
63
63
  return (`if ((Get-Command ${entryPoint} -ErrorAction Ignore) -and $PWD.ProviderPath -like '\\\\wsl*') ` +
64
64
  `{ ${entryPoint} ${subcommand} --adapter ${adapter} } else { ${direct} }; exit $LASTEXITCODE`);
65
65
  }
66
+ /** The wiring options a project's committed config selects. */
67
+ export function projectWiringOptions(projectRoot) {
68
+ return {
69
+ windowsBridgeEntryPoint: codexWindowsBridgeConfig(projectRoot).entryPoint,
70
+ portableCommands: portableHookCommands(projectRoot),
71
+ };
72
+ }
66
73
  /** The `commandWindows` init writes for one event, or undefined for none. */
67
74
  export function expectedCommandWindows(spec, agentHookPath, subcommand, adapter, options) {
68
- if (!spec.windowsBridgeHooks || !options?.windowsBridgeEntryPoint)
75
+ if (!spec.windowsBridgeHooks)
69
76
  return undefined;
77
+ if (!options?.windowsBridgeEntryPoint) {
78
+ return (options?.platform ?? process.platform) === "win32" || options?.portableCommands
79
+ ? nativeHookCommand(agentHookPath, subcommand, adapter)
80
+ : undefined;
81
+ }
70
82
  return hookCommandWindows(spec, agentHookPath, subcommand, adapter, options.windowsBridgeEntryPoint);
71
83
  }
72
84
  /** The `timeout` init writes on one event's Harnery hook, or undefined for none. */
@@ -114,20 +126,35 @@ export function isAgentHookCommand(command) {
114
126
  return commandSubcommand(command) !== null;
115
127
  }
116
128
  /** The canonical command installed for one adapter event. */
117
- export function hookCommand(spec, agentHookPath, subcommand, adapter) {
129
+ export function hookCommand(spec, agentHookPath, subcommand, adapter, options) {
130
+ if ((options?.platform ?? process.platform) === "win32") {
131
+ return nativeHookCommand(agentHookPath, subcommand, adapter);
132
+ }
118
133
  if (agentHookPath === "agent-hook") {
119
134
  return `agent-hook ${subcommand} --adapter ${adapter}`;
120
135
  }
121
136
  const anchor = spec.projectDirEnv ? `"\${${spec.projectDirEnv}:-.}"/` : "";
122
137
  return `bash ${anchor}${agentHookPath} ${subcommand} --adapter ${adapter}`;
123
138
  }
139
+ function nativeHookCommand(agentHookPath, subcommand, adapter) {
140
+ if (agentHookPath === "agent-hook")
141
+ return `agent-hook ${subcommand} --adapter ${adapter}`;
142
+ const runner = join(dirname(agentHookPath), "run.mjs").replaceAll("\\", "/");
143
+ return `node "${runner}" agent-hook ${subcommand} --adapter ${adapter}`;
144
+ }
124
145
  /**
125
146
  * Pick a committed hook target that survives cloning the consumer elsewhere.
126
147
  * Projects that contain Harnery can use its repo-relative launcher. Standalone
127
148
  * consumers use the package's installed `agent-hook` executable from PATH
128
149
  * instead of recording a relative path back into one developer's package tree.
129
150
  */
130
- export function agentHookPathForProject(projectRoot, packageRoot) {
151
+ export function agentHookPathForProject(projectRoot, packageRoot, options) {
152
+ // Native npm shims run the linked source checkout without Bash, and do not
153
+ // depend on a hook's working directory after an agent changes directories.
154
+ // A project that commits one settings file for every OS asks for the same.
155
+ if ((options?.platform ?? process.platform) === "win32" || options?.portableCommands) {
156
+ return "agent-hook";
157
+ }
131
158
  const hook = join(packageRoot, "bin", "agent-hook");
132
159
  const candidate = relative(projectRoot, hook);
133
160
  if (candidate === ".." || candidate.startsWith(`..${sep}`) || isAbsolute(candidate)) {
@@ -155,7 +182,7 @@ export function diffWiring(settings, spec, expected) {
155
182
  if (commands.length > 1)
156
183
  duplicates.push(event);
157
184
  if (expected && commands.length > 0) {
158
- const canonical = hookCommand(spec, expected.agentHookPath, event.subcommand, expected.adapter);
185
+ const canonical = hookCommand(spec, expected.agentHookPath, event.subcommand, expected.adapter, expected);
159
186
  const timeout = hookTimeoutFor(spec, event.subcommand);
160
187
  const commandWindows = expectedCommandWindows(spec, expected.agentHookPath, event.subcommand, expected.adapter, expected);
161
188
  const handlerDrift = groups.some((group) => "hooks" in group &&
@@ -232,7 +259,9 @@ function pluginDrift(projectRoot, id, spec) {
232
259
  const packageRoot = harneryPackageRoot();
233
260
  const check = checkOpenCodePlugin(projectRoot, {
234
261
  binName: resolveBinName(projectRoot),
235
- agentHook: packageRoot ? agentHookPathForProject(projectRoot, packageRoot) : "agent-hook",
262
+ agentHook: packageRoot
263
+ ? agentHookPathForProject(projectRoot, packageRoot, projectWiringOptions(projectRoot))
264
+ : "agent-hook",
236
265
  });
237
266
  if (check.status === "fresh")
238
267
  return null;
@@ -275,11 +304,12 @@ export function loadAdapterWiring(projectRoot) {
275
304
  continue;
276
305
  }
277
306
  const packageRoot = harneryPackageRoot();
307
+ const options = projectWiringOptions(projectRoot);
278
308
  const diff = diffWiring(settings, spec, packageRoot
279
309
  ? {
280
- agentHookPath: agentHookPathForProject(projectRoot, packageRoot),
310
+ agentHookPath: agentHookPathForProject(projectRoot, packageRoot, options),
281
311
  adapter: id,
282
- windowsBridgeEntryPoint: codexWindowsBridgeConfig(projectRoot).entryPoint,
312
+ ...options,
283
313
  }
284
314
  : undefined);
285
315
  if (diff.present.length === 0)
@@ -0,0 +1,29 @@
1
+ import type { AutoUpdateNotice, TickResult } from "../autoupdate/types.js";
2
+ /** Prefix on every self-update line injected into an agent's context. */
3
+ export declare const AUTOUPDATE_CUE_PREFIX = "Auto-update: ";
4
+ export interface AutoUpdateCueDeps {
5
+ tick: (coordRoot: string) => TickResult;
6
+ pendingNotices: (coordRoot: string, sessionKey: string) => AutoUpdateNotice[];
7
+ }
8
+ export interface AutoUpdateCueOptions {
9
+ /**
10
+ * Whether this hook will show context to the agent. When false, the tick
11
+ * still runs but notices stay queued for a session that can read them.
12
+ */
13
+ deliver?: boolean;
14
+ /** Test seam; production loads the engine lazily. */
15
+ deps?: AutoUpdateCueDeps;
16
+ /** Receives tick or delivery failures. The cue itself never throws. */
17
+ onError?: (err: unknown, phase: "autoupdate-tick" | "autoupdate-notices") => void;
18
+ }
19
+ /**
20
+ * Fire the activity-driven self-update check (ADR 0229) and collect the
21
+ * notices this session has not seen yet, one context line each.
22
+ *
23
+ * Projects without an `autoUpdate` section pay one config stat and never load
24
+ * the engine. With the section, the tick's hot path is one stamp stat; any
25
+ * real work runs in a detached child, so the hook never waits on it. Fails
26
+ * open: an error is reported through `onError` and the cue is empty.
27
+ */
28
+ export declare function autoUpdateHookContext(coordRoot: string, sessionKey: string, options?: AutoUpdateCueOptions): Promise<string>;
29
+ //# sourceMappingURL=autoupdate-cue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoupdate-cue.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/autoupdate-cue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAG3E,yEAAyE;AACzE,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,UAAU,CAAC;IACxC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,gBAAgB,EAAE,CAAC;CAC/E;AAED,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,uEAAuE;IACvE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,GAAG,oBAAoB,KAAK,IAAI,CAAC;CACnF;AAUD;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC,CA0BjB"}
@@ -0,0 +1,53 @@
1
+ import { coordEnv } from "../../lib/env.js";
2
+ import { readAutoUpdateConfig } from "../config.js";
3
+ /** Prefix on every self-update line injected into an agent's context. */
4
+ export const AUTOUPDATE_CUE_PREFIX = "Auto-update: ";
5
+ async function loadDeps() {
6
+ const engine = await import("../autoupdate/index.js");
7
+ return {
8
+ tick: (root) => engine.autoUpdateTick(root),
9
+ pendingNotices: (root, key) => engine.pendingNotices(root, key),
10
+ };
11
+ }
12
+ /**
13
+ * Fire the activity-driven self-update check (ADR 0229) and collect the
14
+ * notices this session has not seen yet, one context line each.
15
+ *
16
+ * Projects without an `autoUpdate` section pay one config stat and never load
17
+ * the engine. With the section, the tick's hot path is one stamp stat; any
18
+ * real work runs in a detached child, so the hook never waits on it. Fails
19
+ * open: an error is reported through `onError` and the cue is empty.
20
+ */
21
+ export async function autoUpdateHookContext(coordRoot, sessionKey, options = {}) {
22
+ if (coordEnv("AUTOUPDATE_CHILD") === "1")
23
+ return "";
24
+ let deps;
25
+ try {
26
+ if (!readAutoUpdateConfig(coordRoot))
27
+ return "";
28
+ deps = options.deps ?? (await loadDeps());
29
+ }
30
+ catch (err) {
31
+ options.onError?.(err, "autoupdate-tick");
32
+ return "";
33
+ }
34
+ try {
35
+ deps.tick(coordRoot);
36
+ }
37
+ catch (err) {
38
+ options.onError?.(err, "autoupdate-tick");
39
+ }
40
+ if (options.deliver === false || !sessionKey)
41
+ return "";
42
+ try {
43
+ return deps
44
+ .pendingNotices(coordRoot, sessionKey)
45
+ .map((notice) => `${AUTOUPDATE_CUE_PREFIX}${notice.text.trim()}`)
46
+ .filter((line) => line.length > AUTOUPDATE_CUE_PREFIX.length)
47
+ .join("\n");
48
+ }
49
+ catch (err) {
50
+ options.onError?.(err, "autoupdate-notices");
51
+ return "";
52
+ }
53
+ }
@@ -46,6 +46,7 @@ import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.js";
46
46
  import { ADAPTER_SPECS } from "./adapter/events.js";
47
47
  import { extractBashCommand, extractToolDescription, normalizeEventName, parsePayload, } from "./adapter/parse.js";
48
48
  import { hookTimeoutFor } from "./adapter/wiring.js";
49
+ import { autoUpdateHookContext } from "./autoupdate-cue.js";
49
50
  import { scheduleBackupSnapshot } from "./backup-schedule.js";
50
51
  import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
51
52
  import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
@@ -1050,6 +1051,13 @@ async function handleSessionStarted(run) {
1050
1051
  catch (err) {
1051
1052
  logError(coordRoot, err, { phase: "backup-schedule" });
1052
1053
  }
1054
+ // Project self-update (ADR 0229) fires on activity. The tick costs one stat
1055
+ // when fresh and otherwise launches a detached child; queued notices for
1056
+ // this session ride the same context as the backup cue.
1057
+ const autoUpdateCue = await autoUpdateHookContext(coordRoot, owner.instance_id, {
1058
+ deliver: coordEnv("WORKFLOW_CHILD") !== "1",
1059
+ onError: (err, phase) => logError(coordRoot, err, { phase }),
1060
+ });
1053
1061
  // Storage maintenance can walk large stores. It runs in a detached worker so
1054
1062
  // session start emits its context without waiting for it; an adapter that
1055
1063
  // stops a slow hook would otherwise drop that context entirely.
@@ -1072,7 +1080,7 @@ async function handleSessionStarted(run) {
1072
1080
  }
1073
1081
  }
1074
1082
  try {
1075
- const injected = await emitSessionStartSystemMessage(coordRoot, owner.instance_id, sessionId, data, adapter, recovery?.briefing ?? "", backupCue);
1083
+ const injected = await emitSessionStartSystemMessage(coordRoot, owner.instance_id, sessionId, data, adapter, recovery?.briefing ?? "", [backupCue, autoUpdateCue].filter(Boolean).join("\n\n"));
1076
1084
  if (injected && recovery) {
1077
1085
  completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
1078
1086
  }
@@ -1196,8 +1204,13 @@ async function handleTurnStarted(run) {
1196
1204
  logError(coordRoot, err, { phase: "prompt-context-user-prompt" });
1197
1205
  }
1198
1206
  }
1207
+ // Project self-update (ADR 0229): see handleSessionStarted.
1208
+ const autoUpdateCue = await autoUpdateHookContext(coordRoot, owner.instance_id, {
1209
+ deliver: coordEnv("WORKFLOW_CHILD") !== "1",
1210
+ onError: (err, phase) => logError(coordRoot, err, { phase }),
1211
+ });
1199
1212
  try {
1200
- const injected = await emitUserPromptSubmitSystemMessage(coordRoot, owner.instance_id, sessionId, adapter, payload?.cwd, recovery?.briefing ?? "", hostPromptContext);
1213
+ const injected = await emitUserPromptSubmitSystemMessage(coordRoot, owner.instance_id, sessionId, adapter, payload?.cwd, recovery?.briefing ?? "", hostPromptContext, autoUpdateCue);
1201
1214
  if (injected && recovery) {
1202
1215
  completeRecoveryInjection(coordRoot, owner.instance_id, sessionId, recovery);
1203
1216
  }
@@ -1253,6 +1266,15 @@ async function handleStop(run) {
1253
1266
  });
1254
1267
  }
1255
1268
  if (!verdict.allow) {
1269
+ // Re-arm the prompt-time ritual reminder; it is otherwise shown only on a
1270
+ // session's first prompt so the per-prompt context stays small.
1271
+ try {
1272
+ const { markTurnRitualBounced } = await import("../agents/render/prompt-context.js");
1273
+ markTurnRitualBounced(coordRoot, owner.instance_id);
1274
+ }
1275
+ catch (err) {
1276
+ logError(coordRoot, err, { phase: "stop-ritual-rearm" });
1277
+ }
1256
1278
  // Backstop for sessions whose evidence can never land (e.g. a headless
1257
1279
  // child whose status command resolves to a different owner): after
1258
1280
  // `cap` consecutive blocked stops in one cycle, allow the stop instead
@@ -2058,7 +2080,7 @@ function cleanupSessionEnd(coordRoot, instanceId) {
2058
2080
  }
2059
2081
  }
2060
2082
  }
2061
- async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "", hostPromptContext = "") {
2083
+ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "", hostPromptContext = "", autoUpdateCue = "") {
2062
2084
  // Workflow children get no prompt-time coordination context, matching
2063
2085
  // SessionStart. The turn ritual is not enforced for them, the naming nudge
2064
2086
  // would register them as named peers, and rendering drains peer mail.
@@ -2087,6 +2109,9 @@ async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionI
2087
2109
  if (hostPromptContext) {
2088
2110
  additionalContext = [additionalContext, hostPromptContext].filter(Boolean).join("\n\n");
2089
2111
  }
2112
+ if (autoUpdateCue) {
2113
+ additionalContext = [additionalContext, autoUpdateCue].filter(Boolean).join("\n\n");
2114
+ }
2090
2115
  if (!additionalContext)
2091
2116
  return false;
2092
2117
  const { emitContext } = await import("./adapter/output.js");
@@ -16,7 +16,7 @@ import { existsSync, readdirSync, rmSync } from "node:fs";
16
16
  import os from "node:os";
17
17
  import { join } from "node:path";
18
18
  import { applyDetection } from "../../../lib/presence.js";
19
- import { coordBinPath } from "../../agents/coord-bin.js";
19
+ import { coordBinPath, spawnCoordBinSync } from "../../agents/coord-bin.js";
20
20
  export { captureImages, imageJanitor, recordImageArtifactsV3 } from "./image-capture.js";
21
21
  /**
22
22
  * Play a notification sound via the cross-platform utility
@@ -62,7 +62,7 @@ export function journalJanitor(repoRoot) {
62
62
  const bin = harnBin(repoRoot);
63
63
  if (!bin)
64
64
  return;
65
- spawnSync("bash", [bin, "journal", "janitor", "--quiet"], {
65
+ spawnCoordBinSync(bin, ["journal", "janitor", "--quiet"], {
66
66
  env: { ...process.env, HARNERY_OUTPUT_SESSION_TEE: "0" },
67
67
  timeout: 5000,
68
68
  stdio: "ignore",
@@ -82,7 +82,7 @@ export function journalRecoveryCue(repoRoot) {
82
82
  const bin = harnBin(repoRoot);
83
83
  if (!bin)
84
84
  return "";
85
- const r = spawnSync("bash", [bin, "journal", "recovery-cue"], {
85
+ const r = spawnCoordBinSync(bin, ["journal", "recovery-cue"], {
86
86
  env: { ...process.env, HARNERY_OUTPUT_SESSION_TEE: "0" },
87
87
  timeout: 5000,
88
88
  encoding: "utf8",
@@ -99,7 +99,7 @@ export function journalArchive(repoRoot, owner) {
99
99
  const bin = harnBin(repoRoot);
100
100
  if (!bin || !owner)
101
101
  return;
102
- spawnSync("bash", [bin, "journal", "archive", "--owner", owner], {
102
+ spawnCoordBinSync(bin, ["journal", "archive", "--owner", owner], {
103
103
  env: { ...process.env, HARNERY_OUTPUT_SESSION_TEE: "0" },
104
104
  timeout: 5000,
105
105
  stdio: "ignore",
@@ -90,6 +90,7 @@ function gitRevParse(start, flag) {
90
90
  // A stale adapter project directory may no longer exist. Treat spawn
91
91
  // failure as no path evidence instead of turning discovery into an error.
92
92
  const result = spawnSync("git", ["rev-parse", flag], {
93
+ windowsHide: true,
93
94
  cwd: start,
94
95
  encoding: "utf8",
95
96
  });
@@ -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<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
155
+ kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"error" | "recovery" | "artifact" | "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<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
196
+ kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"error" | "recovery" | "artifact" | "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<"recovery" | "artifact" | "error" | "operation" | "action" | "terminal" | "task" | "progress" | "intent" | "wait" | "lifecycle" | "claim-conflict">[]>;
204
+ kind: import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"error" | "recovery" | "artifact" | "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>;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/semantic/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/semantic/service.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync, } from "node:fs";
4
4
  import { hostname } from "node:os";
5
5
  import { dirname, join, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
6
7
  import { coordEnv } from "../../lib/env.js";
7
8
  import { readEventV3ControlState, readLedgerV3Since, } from "../events/v3/index.js";
8
9
  import { eventV3ActiveWatchPath } from "../events/v3/reader.js";
@@ -53,7 +54,7 @@ export async function spawnSemanticService(coordRootRaw, options = {}) {
53
54
  const logFd = sharedLogs ? undefined : openSync(paths.log, "a", stateFileMode());
54
55
  if (logFd !== undefined)
55
56
  chmodSync(paths.log, stateFileMode());
56
- const harnBin = new URL("../../../bin/harn", import.meta.url).pathname;
57
+ const harnBin = fileURLToPath(new URL(import.meta.url.endsWith(".ts") ? "../../cli.ts" : "../../cli.js", import.meta.url));
57
58
  if (!existsSync(harnBin)) {
58
59
  if (logFd !== undefined)
59
60
  closeSync(logFd);
@@ -64,7 +65,8 @@ export async function spawnSemanticService(coordRootRaw, options = {}) {
64
65
  args.push("--calls-per-hour", String(options.callsPerHour));
65
66
  }
66
67
  let spawnError;
67
- const child = spawn(harnBin, args, {
68
+ const child = spawn(process.execPath, [harnBin, ...args], {
69
+ windowsHide: true,
68
70
  cwd: coordRoot,
69
71
  detached: true,
70
72
  stdio: logFd === undefined ? ["ignore", "ignore", "ignore"] : ["ignore", logFd, logFd],
@@ -17,5 +17,5 @@ export declare function stateFileMode(): number;
17
17
  * True when a state file or directory is readable or writable by someone it should not be: anyone
18
18
  * but the owner in private mode, anyone outside the owner and group in group mode.
19
19
  */
20
- export declare function stateModeTooOpen(mode: number): boolean;
20
+ export declare function stateModeTooOpen(mode: number, platform?: NodeJS.Platform): boolean;
21
21
  //# sourceMappingURL=modes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../src/core/storage/modes.ts"],"names":[],"mappings":"AAgBA,mGAAmG;AACnG,wBAAgB,YAAY,IAAI,SAAS,GAAG,OAAO,CASlD;AAED,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,MAAQ,CAAC;AAEvC,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,MAAQ,CAAC;AAExC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wCAAwC;AACxC,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}
1
+ {"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../src/core/storage/modes.ts"],"names":[],"mappings":"AAgBA,mGAAmG;AACnG,wBAAgB,YAAY,IAAI,SAAS,GAAG,OAAO,CASlD;AAED,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,MAAQ,CAAC;AAEvC,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,MAAQ,CAAC;AAExC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wCAAwC;AACxC,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAKT"}
@@ -46,6 +46,10 @@ export function stateFileMode() {
46
46
  * True when a state file or directory is readable or writable by someone it should not be: anyone
47
47
  * but the owner in private mode, anyone outside the owner and group in group mode.
48
48
  */
49
- export function stateModeTooOpen(mode) {
49
+ export function stateModeTooOpen(mode, platform = process.platform) {
50
+ // Windows synthesizes POSIX mode bits; access is controlled by inherited
51
+ // NTFS ACLs, which chmod and stat.mode cannot inspect or tighten.
52
+ if (platform === "win32")
53
+ return false;
50
54
  return (mode & (stateSharing() === "group" ? 0o007 : 0o077)) !== 0;
51
55
  }