harnery 0.46.0 → 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 (187) 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/rules/claim-conflict.d.ts.map +1 -1
  40. package/dist/core/agents/rules/claim-conflict.js +36 -16
  41. package/dist/core/agents/rules/stop-hook.d.ts +6 -0
  42. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  43. package/dist/core/agents/rules/stop-hook.js +20 -1
  44. package/dist/core/agents/state/proc-start.d.ts +3 -1
  45. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  46. package/dist/core/agents/state/proc-start.js +26 -4
  47. package/dist/core/autoupdate/engine.d.ts +97 -0
  48. package/dist/core/autoupdate/engine.d.ts.map +1 -0
  49. package/dist/core/autoupdate/engine.js +548 -0
  50. package/dist/core/autoupdate/fingerprint.d.ts +16 -0
  51. package/dist/core/autoupdate/fingerprint.d.ts.map +1 -0
  52. package/dist/core/autoupdate/fingerprint.js +73 -0
  53. package/dist/core/autoupdate/git.d.ts +30 -0
  54. package/dist/core/autoupdate/git.d.ts.map +1 -0
  55. package/dist/core/autoupdate/git.js +82 -0
  56. package/dist/core/autoupdate/index.d.ts +6 -0
  57. package/dist/core/autoupdate/index.d.ts.map +1 -0
  58. package/dist/core/autoupdate/index.js +4 -0
  59. package/dist/core/autoupdate/state.d.ts +59 -0
  60. package/dist/core/autoupdate/state.d.ts.map +1 -0
  61. package/dist/core/autoupdate/state.js +214 -0
  62. package/dist/core/autoupdate/types.d.ts +113 -0
  63. package/dist/core/autoupdate/types.d.ts.map +1 -0
  64. package/dist/core/autoupdate/types.js +35 -0
  65. package/dist/core/config.d.ts +53 -1
  66. package/dist/core/config.d.ts.map +1 -1
  67. package/dist/core/config.js +114 -1
  68. package/dist/core/events/v3/bootstrap.js +1 -0
  69. package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
  70. package/dist/core/events/v3/runtime-build.js +1 -0
  71. package/dist/core/hooks/adapter/wiring.d.ts +12 -2
  72. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  73. package/dist/core/hooks/adapter/wiring.js +38 -8
  74. package/dist/core/hooks/autoupdate-cue.d.ts +29 -0
  75. package/dist/core/hooks/autoupdate-cue.d.ts.map +1 -0
  76. package/dist/core/hooks/autoupdate-cue.js +53 -0
  77. package/dist/core/hooks/cli.js +52 -4
  78. package/dist/core/hooks/effects/index.js +4 -4
  79. package/dist/core/hooks/resolve/coord-root.js +1 -0
  80. package/dist/core/hooks/resolve/transcript.d.ts +12 -0
  81. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  82. package/dist/core/hooks/resolve/transcript.js +54 -0
  83. package/dist/core/semantic/contract.d.ts +3 -3
  84. package/dist/core/semantic/service.d.ts.map +1 -1
  85. package/dist/core/semantic/service.js +4 -2
  86. package/dist/core/storage/modes.d.ts +1 -1
  87. package/dist/core/storage/modes.d.ts.map +1 -1
  88. package/dist/core/storage/modes.js +5 -1
  89. package/dist/core/workflow/durable-record.d.ts +2 -0
  90. package/dist/core/workflow/durable-record.d.ts.map +1 -1
  91. package/dist/core/workflow/durable-record.js +13 -1
  92. package/dist/core/workflow/engine.js +4 -0
  93. package/dist/core/workflow/spawn-claude.d.ts +7 -0
  94. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  95. package/dist/core/workflow/spawn-claude.js +16 -4
  96. package/dist/core/workflow/specialists.d.ts.map +1 -1
  97. package/dist/core/workflow/specialists.js +1 -0
  98. package/dist/core/workflow/types.d.ts +10 -0
  99. package/dist/core/workflow/types.d.ts.map +1 -1
  100. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  101. package/dist/core/workflow/workspaces/leases.js +1 -9
  102. package/dist/lib/computer/cua.d.ts +50 -0
  103. package/dist/lib/computer/cua.d.ts.map +1 -0
  104. package/dist/lib/computer/cua.js +474 -0
  105. package/dist/lib/computer/host.d.ts +19 -0
  106. package/dist/lib/computer/host.d.ts.map +1 -0
  107. package/dist/lib/computer/host.js +72 -0
  108. package/dist/lib/computer/lease.d.ts +13 -0
  109. package/dist/lib/computer/lease.d.ts.map +1 -0
  110. package/dist/lib/computer/lease.js +269 -0
  111. package/dist/lib/computer/types.d.ts +163 -0
  112. package/dist/lib/computer/types.d.ts.map +1 -0
  113. package/dist/lib/computer/types.js +10 -0
  114. package/dist/lib/docs-metadata-v2.d.ts.map +1 -1
  115. package/dist/lib/docs-metadata-v2.js +6 -5
  116. package/dist/lib/exec.d.ts.map +1 -1
  117. package/dist/lib/exec.js +1 -0
  118. package/dist/lib/guarded-remove.d.ts.map +1 -1
  119. package/dist/lib/guarded-remove.js +5 -4
  120. package/dist/lib/headless/index.d.ts +33 -0
  121. package/dist/lib/headless/index.d.ts.map +1 -1
  122. package/dist/lib/headless/index.js +38 -2
  123. package/dist/lib/http/client.d.ts +2 -9
  124. package/dist/lib/http/client.d.ts.map +1 -1
  125. package/dist/lib/http/client.js +30 -9
  126. package/dist/lib/instructions/apply.d.ts +9 -0
  127. package/dist/lib/instructions/apply.d.ts.map +1 -1
  128. package/dist/lib/instructions/apply.js +92 -14
  129. package/dist/lib/instructions/git-hooks.d.ts.map +1 -1
  130. package/dist/lib/instructions/git-hooks.js +8 -4
  131. package/dist/lib/instructions/templates.d.ts +11 -0
  132. package/dist/lib/instructions/templates.d.ts.map +1 -1
  133. package/dist/lib/instructions/templates.js +81 -1
  134. package/package.json +5 -5
  135. package/schemas/config.schema.json +89 -1
  136. package/src/commander.ts +98 -8
  137. package/src/commands/agents.ts +100 -32
  138. package/src/commands/autoupdate.ts +250 -0
  139. package/src/commands/computer.ts +682 -0
  140. package/src/commands/docs.ts +3 -1
  141. package/src/commands/fetch.ts +11 -1
  142. package/src/commands/grep.ts +274 -59
  143. package/src/commands/init.ts +17 -7
  144. package/src/commands/rm.ts +2 -2
  145. package/src/commands/web.ts +2 -0
  146. package/src/core/agents/canonical-emit.ts +2 -3
  147. package/src/core/agents/coord-bin.ts +30 -0
  148. package/src/core/agents/coord-client.ts +6 -2
  149. package/src/core/agents/render/prompt-context.ts +152 -53
  150. package/src/core/agents/rules/claim-conflict.ts +50 -18
  151. package/src/core/agents/rules/stop-hook.ts +28 -1
  152. package/src/core/agents/state/proc-start.ts +29 -5
  153. package/src/core/autoupdate/engine.ts +691 -0
  154. package/src/core/autoupdate/fingerprint.ts +89 -0
  155. package/src/core/autoupdate/git.ts +120 -0
  156. package/src/core/autoupdate/index.ts +29 -0
  157. package/src/core/autoupdate/state.ts +269 -0
  158. package/src/core/autoupdate/types.ts +135 -0
  159. package/src/core/config.ts +142 -1
  160. package/src/core/events/v3/bootstrap.ts +1 -0
  161. package/src/core/events/v3/runtime-build.ts +1 -0
  162. package/src/core/hooks/adapter/wiring.ts +51 -6
  163. package/src/core/hooks/autoupdate-cue.ts +72 -0
  164. package/src/core/hooks/cli.ts +54 -1
  165. package/src/core/hooks/effects/index.ts +4 -4
  166. package/src/core/hooks/resolve/coord-root.ts +1 -0
  167. package/src/core/hooks/resolve/transcript.ts +61 -0
  168. package/src/core/semantic/service.ts +6 -2
  169. package/src/core/storage/modes.ts +7 -1
  170. package/src/core/workflow/durable-record.ts +15 -1
  171. package/src/core/workflow/engine.ts +3 -0
  172. package/src/core/workflow/spawn-claude.ts +16 -4
  173. package/src/core/workflow/specialists.ts +1 -0
  174. package/src/core/workflow/types.ts +10 -0
  175. package/src/core/workflow/workspaces/leases.ts +1 -9
  176. package/src/lib/computer/cua.ts +572 -0
  177. package/src/lib/computer/host.ts +87 -0
  178. package/src/lib/computer/lease.ts +299 -0
  179. package/src/lib/computer/types.ts +177 -0
  180. package/src/lib/docs-metadata-v2.ts +24 -5
  181. package/src/lib/exec.ts +1 -0
  182. package/src/lib/guarded-remove.ts +5 -3
  183. package/src/lib/headless/index.ts +66 -2
  184. package/src/lib/http/client.ts +33 -19
  185. package/src/lib/instructions/apply.ts +100 -12
  186. package/src/lib/instructions/git-hooks.ts +8 -4
  187. package/src/lib/instructions/templates.ts +95 -1
@@ -54,6 +54,12 @@ export function stateFileMode(): number {
54
54
  * True when a state file or directory is readable or writable by someone it should not be: anyone
55
55
  * but the owner in private mode, anyone outside the owner and group in group mode.
56
56
  */
57
- export function stateModeTooOpen(mode: number): boolean {
57
+ export function stateModeTooOpen(
58
+ mode: number,
59
+ platform: NodeJS.Platform = process.platform,
60
+ ): boolean {
61
+ // Windows synthesizes POSIX mode bits; access is controlled by inherited
62
+ // NTFS ACLs, which chmod and stat.mode cannot inspect or tighten.
63
+ if (platform === "win32") return false;
58
64
  return (mode & (stateSharing() === "group" ? 0o007 : 0o077)) !== 0;
59
65
  }
@@ -61,10 +61,24 @@ export function writeImmutableJson(path: string, value: unknown): boolean {
61
61
  }
62
62
 
63
63
  export function fsyncParentDirectory(path: string): void {
64
- const directory = dirname(path);
64
+ fsyncDirectory(dirname(path));
65
+ }
66
+
67
+ /** Flush directory entries where the runtime supports it; file fsync remains mandatory. */
68
+ export function fsyncDirectory(directory: string): void {
65
69
  const fd = openSync(directory, "r");
66
70
  try {
67
71
  fsyncSync(fd);
72
+ } catch (error) {
73
+ // Windows does not expose directory FlushFileBuffers through node:fs.
74
+ // Keep atomic links/renames and file flushes, but do not claim POSIX
75
+ // directory-entry durability on that platform. Other errors still fail.
76
+ if (
77
+ process.platform !== "win32" ||
78
+ !["EPERM", "EINVAL", "ENOTSUP"].includes((error as NodeJS.ErrnoException).code ?? "")
79
+ ) {
80
+ throw error;
81
+ }
68
82
  } finally {
69
83
  closeSync(fd);
70
84
  }
@@ -1101,6 +1101,7 @@ async function executeWorkflow(
1101
1101
  runId,
1102
1102
  agentId: id,
1103
1103
  subscriptionOnly,
1104
+ ...(agentOpts.mcp ? { mcp: agentOpts.mcp } : {}),
1104
1105
  // A full-access launch has no sandbox to project a policy into.
1105
1106
  ...(access.mode === "full-access"
1106
1107
  ? { access: "full-access" as const }
@@ -1505,6 +1506,8 @@ function agentCallKey(
1505
1506
  agentOpts.schema ?? null,
1506
1507
  ];
1507
1508
  if (agentOpts.specialist) parts.push(agentOpts.specialist);
1509
+ // Appended only when set so existing resume keys stay stable.
1510
+ if (agentOpts.mcp) parts.push({ mcp: agentOpts.mcp });
1508
1511
  parts.push(prompt);
1509
1512
  const basis = JSON.stringify(parts);
1510
1513
  return createHash("sha256").update(basis).digest("hex").slice(0, 16);
@@ -16,9 +16,17 @@
16
16
  * visible to peers — the point of running them under harnery. So the child
17
17
  * gets HARNERY_WORKFLOW_CHILD=1 and the stop-hook rule exempts it
18
18
  * (stop-hook.ts), keeping heartbeats + events on.
19
+ * 3. **No MCP servers or claude.ai connectors unless the spawn asks.** A
20
+ * headless child otherwise loads every connector on the account (about 130
21
+ * tool schemas) into every turn. `req.mcp` defaults to `"none"` (strict
22
+ * empty MCP config plus ENABLE_CLAUDEAI_MCP_SERVERS=false); `"inherit"` or
23
+ * a server map is the per-spawn opt-in. Project settings, hooks, and
24
+ * CLAUDE.md still load: workflow children need them, so this adapter never
25
+ * passes `--setting-sources ""`.
19
26
  */
20
27
 
21
28
  import { exec } from "../../lib/exec.ts";
29
+ import { claudeMcpLaunch } from "../../lib/headless/index.ts";
22
30
  import { builtinAdapterProfile, validateAdapterEffort } from "../adapters/profiles.ts";
23
31
  import type { AdapterInvocation, AdapterRawResult } from "../adapters/types.ts";
24
32
  import { notFoundError } from "./adapters.ts";
@@ -62,6 +70,7 @@ export function buildClaudeInvocation(req: SpawnRequest): AdapterInvocation {
62
70
  if (fullAccess) argv.push(...fullAccess);
63
71
  if (req.model) argv.push("--model", req.model);
64
72
  if (req.effort) argv.push("--effort", req.effort);
73
+ argv.push(...claudeMcpLaunch(req.mcp).args);
65
74
  return { argv };
66
75
  }
67
76
 
@@ -152,10 +161,13 @@ export const claudeCodeSpawner: Spawner = async (req: SpawnRequest): Promise<Spa
152
161
  }
153
162
  const r = await exec(invocation.argv, {
154
163
  cwd: req.cwd,
155
- env: buildChildEnv(req.runId, {
156
- subscriptionOnly: req.subscriptionOnly,
157
- agentId: req.agentId,
158
- }),
164
+ env: {
165
+ ...buildChildEnv(req.runId, {
166
+ subscriptionOnly: req.subscriptionOnly,
167
+ agentId: req.agentId,
168
+ }),
169
+ ...claudeMcpLaunch(req.mcp).env,
170
+ },
159
171
  timeout: req.timeoutMs,
160
172
  });
161
173
  return normalizeClaudeResult({ ...r, durationMs: Date.now() - t0 });
@@ -72,6 +72,7 @@ export function resolveSpecialistAssignment(
72
72
  maxTurns: opts.maxTurns ?? profile.maxTurns,
73
73
  label: opts.label,
74
74
  schema: opts.schema,
75
+ ...(opts.mcp ? { mcp: opts.mcp } : {}),
75
76
  },
76
77
  };
77
78
  }
@@ -7,6 +7,7 @@
7
7
  * Design record: decision 0015 (portable coordination-aware workflows).
8
8
  */
9
9
 
10
+ import type { ClaudeMcpMode } from "../../lib/headless/index.ts";
10
11
  import type { DiagnosticAdvice } from "../diagnostics/contract.ts";
11
12
  import type {
12
13
  DispatchCostEstimator,
@@ -342,6 +343,11 @@ export interface AgentOpts {
342
343
  * Mixed-adapter workflows are legal: triage on one CLI, deep work on
343
344
  * another. */
344
345
  adapter?: AdapterName;
346
+ /** MCP servers a Claude Code child may load (default `"none"`: no MCP
347
+ * servers, no claude.ai connectors). Set `"inherit"` for a stage that really
348
+ * calls a connector, or `{ mcpServers: {...} }` for exactly those servers.
349
+ * Other adapters ignore it. */
350
+ mcp?: ClaudeMcpMode;
345
351
  }
346
352
 
347
353
  /** Open registry key. The built-in catalog currently contains Claude Code,
@@ -436,6 +442,10 @@ export interface SpawnRequest {
436
442
  * spawner refuses it when its adapter declares no full-access rendering, and
437
443
  * refuses it alongside a filesystem policy, which it would contradict. */
438
444
  access?: WorkerAccessMode;
445
+ /** MCP servers a Claude Code child may load. Absent means `"none"`: no MCP
446
+ * servers and no claude.ai connectors. `"inherit"` or an explicit server map
447
+ * is the per-spawn opt-in (see `ClaudeMcpMode`). Other adapters ignore it. */
448
+ mcp?: ClaudeMcpMode;
439
449
  }
440
450
 
441
451
  /**
@@ -17,6 +17,7 @@ import {
17
17
  import { hostname } from "node:os";
18
18
  import { basename, dirname, join, resolve } from "node:path";
19
19
  import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
20
+ import { fsyncDirectory } from "../durable-record.ts";
20
21
 
21
22
  const MAX_LEASE_BYTES = 8 * 1024;
22
23
  const MAX_CONTENTION_OBSERVATION_ATTEMPTS = 8;
@@ -568,15 +569,6 @@ function digestBytes(bytes: string): string {
568
569
  return createHash("sha256").update(bytes).digest("hex");
569
570
  }
570
571
 
571
- function fsyncDirectory(path: string): void {
572
- const fd = openSync(path, "r");
573
- try {
574
- fsyncSync(fd);
575
- } finally {
576
- closeSync(fd);
577
- }
578
- }
579
-
580
572
  function safeUnlink(path: string): void {
581
573
  try {
582
574
  unlinkSync(path);