harnery 0.9.0 → 0.10.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 (201) hide show
  1. package/README.md +1 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +6 -0
  4. package/dist/commands/agents.d.ts +24 -0
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +244 -13
  7. package/dist/commands/backup.d.ts +5 -4
  8. package/dist/commands/backup.d.ts.map +1 -1
  9. package/dist/commands/backup.js +15 -14
  10. package/dist/commands/browse.d.ts.map +1 -1
  11. package/dist/commands/browse.js +27 -1
  12. package/dist/commands/claude-desktop.d.ts +19 -0
  13. package/dist/commands/claude-desktop.d.ts.map +1 -0
  14. package/dist/commands/claude-desktop.js +168 -0
  15. package/dist/commands/context.d.ts.map +1 -1
  16. package/dist/commands/context.js +149 -1
  17. package/dist/commands/doctor.d.ts.map +1 -1
  18. package/dist/commands/doctor.js +2 -3
  19. package/dist/commands/eml.d.ts +32 -0
  20. package/dist/commands/eml.d.ts.map +1 -1
  21. package/dist/commands/eml.js +16 -3
  22. package/dist/commands/harness.d.ts +11 -0
  23. package/dist/commands/harness.d.ts.map +1 -0
  24. package/dist/commands/harness.js +118 -0
  25. package/dist/commands/presence.d.ts +9 -4
  26. package/dist/commands/presence.d.ts.map +1 -1
  27. package/dist/commands/presence.js +88 -5
  28. package/dist/commands/relay.d.ts +9 -0
  29. package/dist/commands/relay.d.ts.map +1 -0
  30. package/dist/commands/relay.js +143 -0
  31. package/dist/commands/sync.d.ts.map +1 -1
  32. package/dist/commands/sync.js +5 -0
  33. package/dist/commands/workflow.d.ts.map +1 -1
  34. package/dist/commands/workflow.js +8 -15
  35. package/dist/core/agents/canonical-emit.d.ts +15 -0
  36. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  37. package/dist/core/agents/canonical-emit.js +41 -3
  38. package/dist/core/agents/cli.js +4 -2
  39. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  40. package/dist/core/agents/render/prompt-context.js +44 -3
  41. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  42. package/dist/core/agents/render/session-context.js +26 -1
  43. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  44. package/dist/core/agents/rules/commit-conflict.js +4 -37
  45. package/dist/core/agents/session-events.d.ts.map +1 -1
  46. package/dist/core/agents/session-events.js +16 -43
  47. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  48. package/dist/core/agents/state/heartbeat-projector.js +11 -2
  49. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  50. package/dist/core/agents/state/heartbeat-writer.js +3 -3
  51. package/dist/core/agents/state/names.d.ts +38 -3
  52. package/dist/core/agents/state/names.d.ts.map +1 -1
  53. package/dist/core/agents/state/names.js +46 -7
  54. package/dist/core/agents/state/pidmap.d.ts +5 -1
  55. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  56. package/dist/core/agents/state/pidmap.js +58 -2
  57. package/dist/core/agents/state/stale-sweep.d.ts +3 -2
  58. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  59. package/dist/core/agents/state/stale-sweep.js +6 -6
  60. package/dist/core/config.d.ts +68 -11
  61. package/dist/core/config.d.ts.map +1 -1
  62. package/dist/core/config.js +191 -25
  63. package/dist/core/context/index.d.ts +144 -0
  64. package/dist/core/context/index.d.ts.map +1 -0
  65. package/dist/core/context/index.js +380 -0
  66. package/dist/core/harnesses/bench.d.ts +29 -0
  67. package/dist/core/harnesses/bench.d.ts.map +1 -0
  68. package/dist/core/harnesses/bench.js +151 -0
  69. package/dist/core/harnesses/index.d.ts +9 -0
  70. package/dist/core/harnesses/index.d.ts.map +1 -0
  71. package/dist/core/harnesses/index.js +4 -0
  72. package/dist/core/harnesses/profiles.d.ts +62 -0
  73. package/dist/core/harnesses/profiles.d.ts.map +1 -0
  74. package/dist/core/harnesses/profiles.js +115 -0
  75. package/dist/core/harnesses/registry.d.ts +15 -0
  76. package/dist/core/harnesses/registry.d.ts.map +1 -0
  77. package/dist/core/harnesses/registry.js +113 -0
  78. package/dist/core/harnesses/types.d.ts +64 -0
  79. package/dist/core/harnesses/types.d.ts.map +1 -0
  80. package/dist/core/harnesses/types.js +20 -0
  81. package/dist/core/hooks/cli.js +280 -63
  82. package/dist/core/hooks/events/schema.d.ts +38 -1
  83. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  84. package/dist/core/hooks/harness/events.d.ts.map +1 -1
  85. package/dist/core/hooks/harness/events.js +3 -0
  86. package/dist/core/hooks/harness/parse.d.ts +1 -1
  87. package/dist/core/hooks/harness/parse.d.ts.map +1 -1
  88. package/dist/core/hooks/harness/parse.js +4 -0
  89. package/dist/core/presence/blob.d.ts +40 -0
  90. package/dist/core/presence/blob.d.ts.map +1 -0
  91. package/dist/core/presence/blob.js +91 -0
  92. package/dist/core/presence/git.d.ts +68 -0
  93. package/dist/core/presence/git.d.ts.map +1 -0
  94. package/dist/core/presence/git.js +173 -0
  95. package/dist/core/presence/index.d.ts +74 -0
  96. package/dist/core/presence/index.d.ts.map +1 -0
  97. package/dist/core/presence/index.js +224 -0
  98. package/dist/core/presence/relay-client.d.ts +39 -0
  99. package/dist/core/presence/relay-client.d.ts.map +1 -0
  100. package/dist/core/presence/relay-client.js +295 -0
  101. package/dist/core/presence/relay-protocol.d.ts +94 -0
  102. package/dist/core/presence/relay-protocol.d.ts.map +1 -0
  103. package/dist/core/presence/relay-protocol.js +169 -0
  104. package/dist/core/workflow/billing.d.ts +1 -1
  105. package/dist/core/workflow/billing.d.ts.map +1 -1
  106. package/dist/core/workflow/billing.js +7 -1
  107. package/dist/core/workflow/engine.d.ts.map +1 -1
  108. package/dist/core/workflow/engine.js +11 -2
  109. package/dist/core/workflow/harnesses.d.ts +3 -3
  110. package/dist/core/workflow/harnesses.d.ts.map +1 -1
  111. package/dist/core/workflow/harnesses.js +8 -17
  112. package/dist/core/workflow/spawn-claude.d.ts +4 -1
  113. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  114. package/dist/core/workflow/spawn-claude.js +39 -18
  115. package/dist/core/workflow/spawn-codex.d.ts +4 -1
  116. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  117. package/dist/core/workflow/spawn-codex.js +44 -22
  118. package/dist/core/workflow/spawn-cursor.d.ts +4 -1
  119. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  120. package/dist/core/workflow/spawn-cursor.js +35 -16
  121. package/dist/core/workflow/types.d.ts +9 -2
  122. package/dist/core/workflow/types.d.ts.map +1 -1
  123. package/dist/lib/browser/client.d.ts +16 -0
  124. package/dist/lib/browser/client.d.ts.map +1 -1
  125. package/dist/lib/browser/client.js +22 -0
  126. package/dist/lib/browser/index.d.ts +1 -0
  127. package/dist/lib/browser/index.d.ts.map +1 -1
  128. package/dist/lib/browser/index.js +1 -0
  129. package/dist/lib/browser/launch-args.d.ts +21 -0
  130. package/dist/lib/browser/launch-args.d.ts.map +1 -0
  131. package/dist/lib/browser/launch-args.js +32 -0
  132. package/dist/lib/claude-desktop.d.ts +105 -0
  133. package/dist/lib/claude-desktop.d.ts.map +1 -0
  134. package/dist/lib/claude-desktop.js +217 -0
  135. package/dist/lib/docs-lint.d.ts.map +1 -1
  136. package/dist/lib/docs-lint.js +5 -2
  137. package/dist/lib/identities/assume.d.ts +51 -0
  138. package/dist/lib/identities/assume.d.ts.map +1 -0
  139. package/dist/lib/identities/assume.js +274 -0
  140. package/dist/lib/identities/index.d.ts +7 -7
  141. package/dist/lib/identities/index.d.ts.map +1 -1
  142. package/dist/lib/identities/index.js +24 -24
  143. package/dist/lib/instructions/templates.d.ts.map +1 -1
  144. package/dist/lib/instructions/templates.js +15 -2
  145. package/package.json +12 -1
  146. package/schemas/config.schema.json +88 -19
  147. package/src/commander.ts +6 -0
  148. package/src/commands/agents.ts +280 -14
  149. package/src/commands/backup.ts +17 -16
  150. package/src/commands/browse.ts +32 -0
  151. package/src/commands/claude-desktop.ts +215 -0
  152. package/src/commands/context.ts +174 -1
  153. package/src/commands/doctor.ts +2 -3
  154. package/src/commands/eml.ts +22 -4
  155. package/src/commands/harness.ts +147 -0
  156. package/src/commands/presence.ts +111 -5
  157. package/src/commands/relay.ts +160 -0
  158. package/src/commands/sync.ts +4 -0
  159. package/src/commands/workflow.ts +9 -18
  160. package/src/core/agents/canonical-emit.ts +41 -3
  161. package/src/core/agents/cli.ts +4 -2
  162. package/src/core/agents/render/prompt-context.ts +48 -3
  163. package/src/core/agents/render/session-context.ts +27 -1
  164. package/src/core/agents/rules/commit-conflict.ts +4 -33
  165. package/src/core/agents/session-events.ts +15 -39
  166. package/src/core/agents/state/heartbeat-projector.ts +10 -1
  167. package/src/core/agents/state/heartbeat-writer.ts +3 -2
  168. package/src/core/agents/state/names.ts +68 -9
  169. package/src/core/agents/state/pidmap.ts +59 -2
  170. package/src/core/agents/state/stale-sweep.ts +6 -11
  171. package/src/core/config.ts +248 -24
  172. package/src/core/context/index.ts +575 -0
  173. package/src/core/harnesses/bench.ts +214 -0
  174. package/src/core/harnesses/index.ts +31 -0
  175. package/src/core/harnesses/profiles.ts +141 -0
  176. package/src/core/harnesses/registry.ts +140 -0
  177. package/src/core/harnesses/types.ts +90 -0
  178. package/src/core/hooks/cli.ts +331 -62
  179. package/src/core/hooks/events/schema.ts +68 -0
  180. package/src/core/hooks/harness/events.ts +3 -0
  181. package/src/core/hooks/harness/parse.ts +7 -1
  182. package/src/core/presence/blob.ts +125 -0
  183. package/src/core/presence/git.ts +191 -0
  184. package/src/core/presence/index.ts +274 -0
  185. package/src/core/presence/relay-client.ts +322 -0
  186. package/src/core/presence/relay-protocol.ts +246 -0
  187. package/src/core/workflow/billing.ts +8 -2
  188. package/src/core/workflow/engine.ts +11 -2
  189. package/src/core/workflow/harnesses.ts +14 -19
  190. package/src/core/workflow/spawn-claude.ts +39 -20
  191. package/src/core/workflow/spawn-codex.ts +47 -28
  192. package/src/core/workflow/spawn-cursor.ts +37 -18
  193. package/src/core/workflow/types.ts +9 -2
  194. package/src/lib/browser/client.ts +37 -0
  195. package/src/lib/browser/index.ts +1 -0
  196. package/src/lib/browser/launch-args.ts +33 -0
  197. package/src/lib/claude-desktop.ts +293 -0
  198. package/src/lib/docs-lint.ts +5 -2
  199. package/src/lib/identities/assume.ts +363 -0
  200. package/src/lib/identities/index.ts +28 -24
  201. package/src/lib/instructions/templates.ts +16 -2
@@ -0,0 +1,118 @@
1
+ import { createBuiltinHarnessRegistry, runHarnessBench, } from "../core/harnesses/index.js";
2
+ const registry = createBuiltinHarnessRegistry();
3
+ export function registerHarnessCommand(program, emit) {
4
+ const command = program
5
+ .command("harness")
6
+ .description("Inspect registered harness capabilities and run their conformance bench.");
7
+ command
8
+ .command("list")
9
+ .description("List registered harness adapters and their high-signal capability claims.")
10
+ .option("--json", "Machine-readable profile catalog")
11
+ .action((opts) => {
12
+ const profiles = registry.list().map((adapter) => adapter.profile);
13
+ if (opts.json) {
14
+ emit.config({ format: "json" });
15
+ emit.data({ harnesses: profiles });
16
+ }
17
+ else {
18
+ emit.text(renderProfileTable(profiles));
19
+ }
20
+ });
21
+ command
22
+ .command("show <id>")
23
+ .description("Show one harness's complete capability declaration.")
24
+ .option("--json", "Machine-readable profile")
25
+ .action((id, opts) => {
26
+ const adapter = registry.get(id);
27
+ if (!adapter) {
28
+ emit.error({
29
+ code: "unknown_harness",
30
+ message: `unknown harness ${JSON.stringify(id)} (registered: ${registry.ids().join(", ")})`,
31
+ });
32
+ emit.setExitCode(1);
33
+ return;
34
+ }
35
+ if (opts.json) {
36
+ emit.config({ format: "json" });
37
+ emit.data(adapter.profile);
38
+ }
39
+ else {
40
+ emit.text(renderProfile(adapter.profile));
41
+ }
42
+ });
43
+ command
44
+ .command("bench [harnesses...]")
45
+ .description("Run the offline adapter-contract bench (no model calls); drift exits non-zero.")
46
+ .option("--require-installed", "Also fail when a registered vendor CLI is missing")
47
+ .option("--json", "Machine-readable conformance report")
48
+ .action((harnesses, opts) => {
49
+ try {
50
+ const report = runHarnessBench(registry, { harnesses });
51
+ if (opts.json) {
52
+ emit.config({ format: "json" });
53
+ emit.data(report);
54
+ }
55
+ else {
56
+ emit.text(renderBenchReport(report));
57
+ }
58
+ emit.setExitCode(report.drift || (opts.requireInstalled && report.skipped) ? 1 : 0);
59
+ }
60
+ catch (error) {
61
+ emit.error({ code: "harness_bench_failed", message: error.message });
62
+ emit.setExitCode(1);
63
+ }
64
+ });
65
+ }
66
+ export function renderProfileTable(profiles) {
67
+ const rows = profiles.map((profile) => [
68
+ profile.id,
69
+ profile.binary,
70
+ profile.capabilities.modelSelection.support,
71
+ profile.capabilities.effortSelection.support,
72
+ profile.capabilities.sessionId.support,
73
+ profile.capabilities.cost.support,
74
+ ]);
75
+ return renderTable(["HARNESS", "BINARY", "MODEL", "EFFORT", "SESSION", "COST"], rows);
76
+ }
77
+ export function renderProfile(profile) {
78
+ const lines = [
79
+ `${profile.displayName} (${profile.id})`,
80
+ `binary: ${profile.binary}`,
81
+ `integration: ${profile.integrationMode}`,
82
+ `auth: ${profile.authModel}`,
83
+ `model family: ${profile.modelFamily}`,
84
+ `effort values: ${profile.effortValues.join(", ") || "none"}`,
85
+ profile.verified
86
+ ? `verified: ${profile.verified.date} (${profile.verified.version})`
87
+ : "verified: not recorded",
88
+ "",
89
+ "capabilities:",
90
+ ];
91
+ for (const [dimension, claim] of Object.entries(profile.capabilities)) {
92
+ lines.push(` ${dimension.padEnd(18)} ${claim.support}${claim.note ? ` — ${claim.note}` : ""}`);
93
+ }
94
+ return lines.join("\n");
95
+ }
96
+ export function renderBenchReport(report) {
97
+ const rows = report.results.map((result) => [
98
+ result.harness,
99
+ result.dimension,
100
+ result.declared,
101
+ result.observed,
102
+ result.verdict,
103
+ ]);
104
+ const table = renderTable(["HARNESS", "DIMENSION", "DECLARED", "OBSERVED", "VERDICT"], rows);
105
+ const summary = Object.entries(report.summary)
106
+ .filter(([, count]) => count > 0)
107
+ .map(([verdict, count]) => `${verdict}=${count}`)
108
+ .join(", ");
109
+ return `${table}\n\nmode: ${report.mode} (no model calls)\n${summary}`;
110
+ }
111
+ function renderTable(headers, rows) {
112
+ const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index]?.length ?? 0)));
113
+ const line = (cells) => cells
114
+ .map((cell, index) => cell.padEnd(widths[index]))
115
+ .join(" ")
116
+ .trimEnd();
117
+ return [line(headers), line(widths.map((width) => "-".repeat(width))), ...rows.map(line)].join("\n");
118
+ }
@@ -1,12 +1,17 @@
1
1
  import type { Command } from "commander";
2
2
  import type { EmitContext } from "../commander.js";
3
3
  /**
4
- * `presence`: mobile/office state for agents (~/.claude/presence).
4
+ * `presence`: two related surfaces under one name.
5
+ *
6
+ * 1. Mobile/office state for agents (~/.claude/presence): get / set /
7
+ * clear / detect. The state file path is Claude-Code-specific but the
8
+ * logic is generic.
9
+ * 2. Cross-machine session presence (ADR 0016): publish / fetch / peers —
10
+ * the git-refs transport that lets sessions on other machines show up
11
+ * in the peer table via `refs/harnery/presence/<machine>` on origin.
5
12
  *
6
13
  * Uses the injected EmitContext so composed and standalone consumers share
7
- * one code path. The state file path is Claude-Code-specific (lives under
8
- * ~/.claude/) but the detect/set/clear logic is generic: any agent harness
9
- * can adopt the same convention.
14
+ * one code path.
10
15
  */
11
16
  export declare function registerPresenceCommand(program: Command, emit: EmitContext): void;
12
17
  //# sourceMappingURL=presence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/commands/presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAkBnD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAoFjF"}
1
+ {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/commands/presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA0BnD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAwKjF"}
@@ -1,17 +1,24 @@
1
+ import { monorepoRoot } from "../core/agents/coord-client.js";
1
2
  import { emitCanonical, normalizeHarness, readHeartbeat, resolveOwner, } from "../core/agents/index.js";
3
+ import { fetchPresence, publishPresence, readRemoteMachines, relayDaemonStatus, runRelayDaemon, } from "../core/presence/index.js";
2
4
  import { ageSeconds, applyDetection, clearPresence, formatAge, presenceFilePath, readPresence, writePresence, } from "../lib/presence.js";
3
5
  /**
4
- * `presence`: mobile/office state for agents (~/.claude/presence).
6
+ * `presence`: two related surfaces under one name.
7
+ *
8
+ * 1. Mobile/office state for agents (~/.claude/presence): get / set /
9
+ * clear / detect. The state file path is Claude-Code-specific but the
10
+ * logic is generic.
11
+ * 2. Cross-machine session presence (ADR 0016): publish / fetch / peers —
12
+ * the git-refs transport that lets sessions on other machines show up
13
+ * in the peer table via `refs/harnery/presence/<machine>` on origin.
5
14
  *
6
15
  * Uses the injected EmitContext so composed and standalone consumers share
7
- * one code path. The state file path is Claude-Code-specific (lives under
8
- * ~/.claude/) but the detect/set/clear logic is generic: any agent harness
9
- * can adopt the same convention.
16
+ * one code path.
10
17
  */
11
18
  export function registerPresenceCommand(program, emit) {
12
19
  const cmd = program
13
20
  .command("presence")
14
- .description("Mobile/office state for agents (~/.claude/presence)")
21
+ .description("Presence: mobile/office state (get/set/clear/detect) + cross-machine session presence (publish/fetch/peers)")
15
22
  .action(() => {
16
23
  printStatus(emit);
17
24
  });
@@ -62,6 +69,74 @@ export function registerPresenceCommand(program, emit) {
62
69
  path: presenceFilePath(),
63
70
  });
64
71
  });
72
+ // ---- cross-machine session presence (ADR 0016) ----
73
+ cmd
74
+ .command("publish")
75
+ .description("Publish this machine's live sessions to refs/harnery/presence/<machine> on origin. " +
76
+ "Change-batched with a keepalive; --force pushes regardless. Synchronous (errors report).")
77
+ .option("--force", "Push even if unchanged / within throttle windows")
78
+ .action((opts) => {
79
+ const root = requireRoot(emit);
80
+ const r = publishPresence(root, { force: opts.force, sync: true });
81
+ if (r.status === "error") {
82
+ emit.error({ code: "publish_failed", message: r.error });
83
+ process.exit(1);
84
+ }
85
+ emit.data(r);
86
+ });
87
+ cmd
88
+ .command("fetch")
89
+ .description("Fetch peer machines' presence refs from origin (throttled; --force bypasses). Synchronous.")
90
+ .option("--force", "Fetch even within the throttle interval")
91
+ .action((opts) => {
92
+ const root = requireRoot(emit);
93
+ const r = fetchPresence(root, { force: opts.force, sync: true });
94
+ if (r.status === "error") {
95
+ emit.error({ code: "fetch_failed", message: r.error });
96
+ process.exit(1);
97
+ }
98
+ emit.data(r);
99
+ });
100
+ cmd
101
+ .command("relay-daemon")
102
+ .description("Run the relay client daemon in the foreground (normally spawned detached by the hooks " +
103
+ "when presence.relay is configured; holds the WebSocket, publishes on heartbeat change, " +
104
+ "caches peers' blobs for the renderers, exits when the machine goes idle).")
105
+ .action(async () => {
106
+ const root = requireRoot(emit);
107
+ process.exit(await runRelayDaemon(root));
108
+ });
109
+ cmd
110
+ .command("peers")
111
+ .description("Show sessions on other machines (merged from presence refs + the relay daemon's cache)")
112
+ .option("--json", "JSON output")
113
+ .option("--stale", "Include machines past the stale window")
114
+ .action((opts) => {
115
+ const root = requireRoot(emit);
116
+ const machines = readRemoteMachines(root, { includeStale: opts.stale });
117
+ const daemon = relayDaemonStatus(root);
118
+ if (opts.json)
119
+ emit.config({ format: "json" });
120
+ if (opts.json || !process.stdout.isTTY) {
121
+ emit.data({ machines, relay_daemon: daemon });
122
+ return;
123
+ }
124
+ emit.text(daemon.running
125
+ ? `relay daemon: running (pid ${daemon.pid}, ${daemon.url})\n`
126
+ : "relay daemon: not running (git-refs transport only)\n");
127
+ if (machines.length === 0) {
128
+ emit.text("no remote sessions known (try `presence fetch --force`)\n");
129
+ return;
130
+ }
131
+ for (const m of machines) {
132
+ emit.text(`${m.machine} (published ${formatAge(m.age_secs)} ago)\n`);
133
+ for (const a of m.agents) {
134
+ const task = a.task ? ` "${a.task.slice(0, 70)}"` : "";
135
+ const files = a.files_touched?.length ? ` [${a.files_touched.length} files]` : "";
136
+ emit.text(` agent-${a.name ?? a.instance_id.slice(0, 8)}${task}${files}\n`);
137
+ }
138
+ }
139
+ });
65
140
  cmd
66
141
  .command("detect")
67
142
  .description("Internal: read prompt text from stdin, apply detection rules, update state if signal is clear")
@@ -85,6 +160,14 @@ export function registerPresenceCommand(program, emit) {
85
160
  }
86
161
  });
87
162
  }
163
+ function requireRoot(emit) {
164
+ const root = monorepoRoot();
165
+ if (!root) {
166
+ emit.error({ code: "not_in_repo", message: "coord root not found (no .harnery/ upward)" });
167
+ process.exit(1);
168
+ }
169
+ return root;
170
+ }
88
171
  function printStatus(emit) {
89
172
  const r = readPresence();
90
173
  emit.data({
@@ -0,0 +1,9 @@
1
+ import type { Command } from "commander";
2
+ import type { EmitContext } from "../commander.js";
3
+ /** Start the relay server. Exported for in-process integration tests. */
4
+ export declare function startRelayServer(port: number): {
5
+ port: number;
6
+ stop: () => void;
7
+ };
8
+ export declare function registerRelayCommand(program: Command, emit: EmitContext): void;
9
+ //# sourceMappingURL=relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../../src/commands/relay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAgCnD,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,IAAI,CAAA;CAAE,CAkGjF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAyB9E"}
@@ -0,0 +1,143 @@
1
+ import { MAX_FRAME_BYTES } from "../core/presence/relay-protocol.js";
2
+ /**
3
+ * `relay serve`: the Bun self-host of the presence relay (ADR 0016 phase 2).
4
+ * Same wire protocol as the Cloudflare Durable Objects host in relay/worker/ —
5
+ * one in-memory room per opaque room id, warm-join cache, fan-out, rate
6
+ * limits. For teams who'd rather run their own box than use the public
7
+ * relay.harnery.com; point clients at it via `.harnery/config.jsonc` →
8
+ * `{ "presence": { "relay": "ws://host:port" } }`.
9
+ *
10
+ * Bun-only (Bun.serve WebSockets), like `harn tunnel` — fails fast with a
11
+ * clear message on a Bun-free host.
12
+ */
13
+ const MAX_SOCKETS_PER_ROOM = 64;
14
+ const RATE_BURST = 10;
15
+ const RATE_REFILL_MS = 2000;
16
+ const CACHE_TTL_MS = 15 * 60 * 1000;
17
+ const ROOM_ID_RE = /^[0-9a-f]{32}$/;
18
+ /** Start the relay server. Exported for in-process integration tests. */
19
+ export function startRelayServer(port) {
20
+ if (typeof Bun === "undefined" || typeof Bun.serve !== "function") {
21
+ throw new Error("relay serve requires Bun (Bun.serve WebSockets)");
22
+ }
23
+ const rooms = new Map();
24
+ const roomOf = (roomId) => {
25
+ let r = rooms.get(roomId);
26
+ if (!r) {
27
+ r = { sockets: new Set(), cache: new Map() };
28
+ rooms.set(roomId, r);
29
+ }
30
+ return r;
31
+ };
32
+ const server = Bun.serve({
33
+ port,
34
+ fetch(req, srv) {
35
+ const url = new URL(req.url);
36
+ if (url.pathname === "/" || url.pathname === "/healthz") {
37
+ return Response.json({ ok: true, service: "harnery-presence-relay", v: 1 });
38
+ }
39
+ const m = /^\/v1\/room\/([0-9a-f]{32})$/.exec(url.pathname);
40
+ if (!m || !ROOM_ID_RE.test(m[1]))
41
+ return new Response("not found", { status: 404 });
42
+ const room = roomOf(m[1]);
43
+ if (room.sockets.size >= MAX_SOCKETS_PER_ROOM) {
44
+ return new Response("room full", { status: 429 });
45
+ }
46
+ const ok = srv.upgrade(req, {
47
+ data: { roomId: m[1], bucketTokens: RATE_BURST, bucketAt: Date.now() },
48
+ });
49
+ return ok ? undefined : new Response("expected websocket", { status: 426 });
50
+ },
51
+ websocket: {
52
+ open(ws) {
53
+ const room = roomOf(ws.data.roomId);
54
+ room.sockets.add(ws);
55
+ const now = Date.now();
56
+ for (const [sender, entry] of room.cache) {
57
+ if (now - entry.at > CACHE_TTL_MS) {
58
+ room.cache.delete(sender);
59
+ continue;
60
+ }
61
+ ws.send(entry.raw);
62
+ }
63
+ ws.send(JSON.stringify({ t: "hello", peers: room.sockets.size }));
64
+ },
65
+ message(ws, message) {
66
+ if (typeof message !== "string" || message.length > MAX_FRAME_BYTES)
67
+ return;
68
+ // Rate limit (token bucket per socket).
69
+ const d = ws.data;
70
+ const now = Date.now();
71
+ const refill = Math.floor((now - d.bucketAt) / RATE_REFILL_MS);
72
+ if (refill > 0) {
73
+ d.bucketTokens = Math.min(RATE_BURST, d.bucketTokens + refill);
74
+ d.bucketAt = now;
75
+ }
76
+ if (d.bucketTokens <= 0)
77
+ return;
78
+ d.bucketTokens -= 1;
79
+ let frame;
80
+ try {
81
+ frame = JSON.parse(message);
82
+ }
83
+ catch {
84
+ return;
85
+ }
86
+ if (frame.t !== "pub" ||
87
+ typeof frame.sender !== "string" ||
88
+ !/^[0-9a-f]+$/.test(frame.sender) ||
89
+ frame.sender.length > 64) {
90
+ return;
91
+ }
92
+ const room = roomOf(d.roomId);
93
+ room.cache.set(frame.sender, { raw: message, at: now });
94
+ for (const peer of room.sockets) {
95
+ if (peer === ws)
96
+ continue;
97
+ try {
98
+ peer.send(message);
99
+ }
100
+ catch {
101
+ /* dead socket */
102
+ }
103
+ }
104
+ },
105
+ close(ws) {
106
+ const room = rooms.get(ws.data.roomId);
107
+ if (!room)
108
+ return;
109
+ room.sockets.delete(ws);
110
+ if (room.sockets.size === 0 && room.cache.size === 0)
111
+ rooms.delete(ws.data.roomId);
112
+ },
113
+ },
114
+ });
115
+ return {
116
+ port: server.port ?? port,
117
+ stop: () => server.stop(true),
118
+ };
119
+ }
120
+ export function registerRelayCommand(program, emit) {
121
+ const cmd = program
122
+ .command("relay")
123
+ .description("Self-host the presence relay (see also relay/worker/ for the Cloudflare host)");
124
+ cmd
125
+ .command("serve")
126
+ .description("Run the presence relay on this machine (Bun-only; same protocol as relay/worker/)")
127
+ .option("--port <n>", "Port to listen on", "8787")
128
+ .action((opts) => {
129
+ let server;
130
+ try {
131
+ server = startRelayServer(Number.parseInt(opts.port, 10));
132
+ }
133
+ catch (e) {
134
+ emit.error({
135
+ code: "relay_serve_failed",
136
+ message: e instanceof Error ? e.message : String(e),
137
+ });
138
+ process.exit(1);
139
+ }
140
+ emit.log(`presence relay listening on :${server.port} (ws path: /v1/room/<room-id>)`, "info");
141
+ // Foreground service: run until interrupted.
142
+ });
143
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAgHnD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAuK7E"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAoHnD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAuK7E"}
@@ -22,6 +22,7 @@ import { spawnSync } from "node:child_process";
22
22
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
23
23
  import os from "node:os";
24
24
  import path from "node:path";
25
+ import { syncJsoncConfig } from "../core/config.js";
25
26
  // What we sync. Anything matching one of these (relative to .harnery/) is
26
27
  // considered part of the sync set. Everything else stays local-only.
27
28
  const SYNC_PATHS = ["identities/", "scratch/archived/", "councils/"];
@@ -29,10 +30,14 @@ function configPath() {
29
30
  return path.join(os.homedir(), ".config", "harnery", "sync.json");
30
31
  }
31
32
  function loadConfig() {
33
+ // Precedence: env → .harnery/config.jsonc `sync` → ~/.config/harnery/sync.json.
32
34
  const envRemote = process.env.HARNERY_SYNC_REMOTE;
33
35
  const envPrefix = process.env.HARNERY_SYNC_PREFIX ?? "harnery";
34
36
  if (envRemote)
35
37
  return { remote: envRemote, prefix: envPrefix };
38
+ const jsonc = syncJsoncConfig();
39
+ if (jsonc)
40
+ return jsonc;
36
41
  const p = configPath();
37
42
  if (!existsSync(p))
38
43
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA2BnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAuGjF"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA0BnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA+FjF"}
@@ -1,7 +1,9 @@
1
1
  import { workflowSubscriptionOnly } from "../core/config.js";
2
+ import { createBuiltinHarnessRegistry } from "../core/harnesses/index.js";
2
3
  import { findCoordRoot } from "../core/hooks/resolve/coord-root.js";
3
- const HARNESSES = ["claude-code", "codex", "cursor"];
4
4
  export function registerWorkflowCommand(program, emit) {
5
+ const registry = createBuiltinHarnessRegistry();
6
+ const harnesses = registry.ids();
5
7
  const workflow = program
6
8
  .command("workflow")
7
9
  .description("Run bounded, schema-gated multi-subagent workflow scripts.");
@@ -12,7 +14,7 @@ export function registerWorkflowCommand(program, emit) {
12
14
  .option("--max-agents <n>", "Total-agent ceiling for the run (default 50)")
13
15
  .option("--concurrency <n>", "Concurrent-subagent cap (default 4)")
14
16
  .option("--cwd <dir>", "Working directory children spawn in (default: coord root)")
15
- .option("--harness <name>", `Default harness for agent() calls: ${HARNESSES.join(" | ")} (default claude-code); scripts can override per agent via opts.harness`)
17
+ .option("--harness <name>", `Default harness for agent() calls: ${harnesses.join(" | ")} (default claude-code); scripts can override per agent via opts.harness`)
16
18
  .option("--resume-from <run-id>", "Reuse completed agent results from a prior run's journal; only changed/failed calls re-run")
17
19
  .option("--subscription-only", "Guarantee subscription billing: scrub API-key vars from child envs; fail loud when a " +
18
20
  "harness has no stored login (repo default via config.jsonc workflow.subscriptionOnly)")
@@ -28,10 +30,10 @@ export function registerWorkflowCommand(program, emit) {
28
30
  });
29
31
  process.exit(1);
30
32
  }
31
- if (opts.harness && !HARNESSES.includes(opts.harness)) {
33
+ if (opts.harness && !registry.get(opts.harness)) {
32
34
  emit.error({
33
35
  code: "bad_harness",
34
- message: `unknown harness "${opts.harness}" (expected: ${HARNESSES.join(" | ")})`,
36
+ message: `unknown harness "${opts.harness}" (expected: ${harnesses.join(" | ")})`,
35
37
  });
36
38
  process.exit(1);
37
39
  }
@@ -46,19 +48,10 @@ export function registerWorkflowCommand(program, emit) {
46
48
  process.exit(1);
47
49
  }
48
50
  try {
49
- const [{ runWorkflow }, { claudeCodeSpawner }, { codexSpawner }, { cursorSpawner }] = await Promise.all([
50
- import("../core/workflow/engine.js"),
51
- import("../core/workflow/spawn-claude.js"),
52
- import("../core/workflow/spawn-codex.js"),
53
- import("../core/workflow/spawn-cursor.js"),
54
- ]);
51
+ const { runWorkflow } = await import("../core/workflow/engine.js");
55
52
  const report = await runWorkflow(script, {
56
53
  coordRoot,
57
- spawners: {
58
- "claude-code": claudeCodeSpawner,
59
- codex: codexSpawner,
60
- cursor: cursorSpawner,
61
- },
54
+ spawners: registry.spawners(),
62
55
  defaultHarness: opts.harness,
63
56
  resumeFrom: opts.resumeFrom,
64
57
  subscriptionOnly,
@@ -18,6 +18,21 @@ export interface CanonicalEmitInput {
18
18
  parentSessionId?: string;
19
19
  parentTurnId?: string;
20
20
  }
21
+ /**
22
+ * Resolve the coord root for a canonical emit.
23
+ *
24
+ * Order: explicit `HARNERY_COORD_ROOT_OVERRIDE` → git-superproject-aware
25
+ * `monorepoRoot()` (must actually carry `.harnery/`) → cwd walk (non-git
26
+ * hosts). The git-aware step is load-bearing: a shell cd'd into a nested
27
+ * directory that carries its own `.harnery/` (e.g. an embedded harnery
28
+ * checkout) made the old cwd-walk resolve the NESTED root, from which
29
+ * `<root>/harnery/bin/agent-coord` doesn't exist — so `agents status` /
30
+ * `set-task` emits silently vanished and the Stop-hook's rule 1/3
31
+ * (`state.status_checked` in-turn) blocked turns that had done the ritual.
32
+ * Same bug class as the coordHelperOpts root-pin fix; this closes the
33
+ * emitCanonical instance of it.
34
+ */
35
+ export declare function resolveEmitRoot(start?: string): string | null;
21
36
  export declare function emitCanonical(input: CanonicalEmitInput): void;
22
37
  /**
23
38
  * Normalize the heartbeat's `platform` field (which uses snake_case
@@ -1 +1 @@
1
- {"version":3,"file":"canonical-emit.d.ts","sourceRoot":"","sources":["../../../src/core/agents/canonical-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CA8B7D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,CAIjG"}
1
+ {"version":3,"file":"canonical-emit.d.ts","sourceRoot":"","sources":["../../../src/core/agents/canonical-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAW5E;AAID,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAsC7D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,CAIjG"}
@@ -11,8 +11,38 @@
11
11
  import { spawnSync } from "node:child_process";
12
12
  import { existsSync } from "node:fs";
13
13
  import { dirname, join, resolve } from "node:path";
14
+ import { monorepoRoot } from "./coord-client.js";
15
+ /**
16
+ * Resolve the coord root for a canonical emit.
17
+ *
18
+ * Order: explicit `HARNERY_COORD_ROOT_OVERRIDE` → git-superproject-aware
19
+ * `monorepoRoot()` (must actually carry `.harnery/`) → cwd walk (non-git
20
+ * hosts). The git-aware step is load-bearing: a shell cd'd into a nested
21
+ * directory that carries its own `.harnery/` (e.g. an embedded harnery
22
+ * checkout) made the old cwd-walk resolve the NESTED root, from which
23
+ * `<root>/harnery/bin/agent-coord` doesn't exist — so `agents status` /
24
+ * `set-task` emits silently vanished and the Stop-hook's rule 1/3
25
+ * (`state.status_checked` in-turn) blocked turns that had done the ritual.
26
+ * Same bug class as the coordHelperOpts root-pin fix; this closes the
27
+ * emitCanonical instance of it.
28
+ */
29
+ export function resolveEmitRoot(start = process.cwd()) {
30
+ const override = process.env.HARNERY_COORD_ROOT_OVERRIDE;
31
+ if (override)
32
+ return override;
33
+ // Memoized per start-dir: the session-tee middleware resolves once per
34
+ // emitted event, and monorepoRoot() spawns git — cache so a streaming
35
+ // command doesn't pay 1-3 subprocess spawns per output line.
36
+ if (cachedRoot && cachedRoot.start === start)
37
+ return cachedRoot.root;
38
+ const gitRoot = monorepoRoot();
39
+ const root = gitRoot && existsSync(join(gitRoot, ".harnery")) ? gitRoot : findRepoRoot(start);
40
+ cachedRoot = { start, root };
41
+ return root;
42
+ }
43
+ let cachedRoot;
14
44
  export function emitCanonical(input) {
15
- const root = findRepoRoot();
45
+ const root = resolveEmitRoot();
16
46
  if (!root)
17
47
  return;
18
48
  const binary = resolve(root, "harnery", "bin", "agent-coord");
@@ -38,11 +68,19 @@ export function emitCanonical(input) {
38
68
  args.push("--parent-session-id", input.parentSessionId);
39
69
  if (input.parentTurnId)
40
70
  args.push("--parent-turn-id", input.parentTurnId);
41
- spawnSync(binary, args, {
71
+ // Pin cwd + HARNERY_COORD_ROOT_OVERRIDE so the child writes to the SAME
72
+ // stream we resolved, regardless of where the caller's shell is cd'd.
73
+ const result = spawnSync(binary, args, {
42
74
  encoding: "utf8",
43
75
  stdio: "ignore",
44
76
  timeout: 3000,
77
+ cwd: root,
78
+ env: { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: root },
45
79
  });
80
+ if (result.error || result.status !== 0) {
81
+ const why = result.error ? result.error.message : `exit ${result.status}`;
82
+ process.stderr.write(`emitCanonical: ${input.type} emit failed (${why})\n`); // lint-ok-emission: soft-fail diagnostic promised by the module doc; silent drops cost blocked turns
83
+ }
46
84
  }
47
85
  catch {
48
86
  /* never break the caller */
@@ -59,7 +97,7 @@ export function normalizeHarness(platform) {
59
97
  return "codex";
60
98
  return "claude-code";
61
99
  }
62
- function findRepoRoot(start = process.cwd()) {
100
+ function findRepoRoot(start) {
63
101
  let dir = resolve(start);
64
102
  while (true) {
65
103
  if (existsSync(join(dir, ".harnery")))
@@ -188,7 +188,9 @@ async function handleStateAction(root, action, rest) {
188
188
  const task = args.join(" ");
189
189
  const hb = writer.setTask(root, owner, task);
190
190
  if (!hb) {
191
- process.stderr.write(`agent-coord set-task: no heartbeat at .harnery/active/${owner}.json\n`);
191
+ // Name the RESOLVED root: when a nested .harnery/ shadows the real
192
+ // coordination home, the full path is what makes that diagnosable.
193
+ process.stderr.write(`agent-coord set-task: no heartbeat at ${root}/.harnery/active/${owner}.json\n`);
192
194
  return 1;
193
195
  }
194
196
  process.stdout.write(`${JSON.stringify({ instance_id: owner, task: hb.task ?? null, cleared: !task })}\n`);
@@ -664,7 +666,7 @@ async function handleResolveName(root, rest) {
664
666
  process.stdout.write(`${JSON.stringify({ instance_id: owner, name: null, kind: null })}\n`);
665
667
  return 0;
666
668
  }
667
- process.stdout.write(`${JSON.stringify({ instance_id: owner, name: resolved.name, kind: resolved.kind })}\n`);
669
+ process.stdout.write(`${JSON.stringify({ instance_id: owner, name: resolved.name, kind: resolved.kind, agent_id: resolved.agent_id ?? null })}\n`);
668
670
  return 0;
669
671
  }
670
672
  async function handleEmitEvent(root, rest) {
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAgCH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;iDAC6C;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAqBnE"}
1
+ {"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAiCH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;iDAC6C;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAqBnE"}