harnery 0.8.0 → 0.9.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 (90) hide show
  1. package/README.md +1 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +2 -0
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +38 -0
  6. package/dist/commands/grep.d.ts +35 -2
  7. package/dist/commands/grep.d.ts.map +1 -1
  8. package/dist/commands/grep.js +427 -139
  9. package/dist/commands/init.d.ts +15 -5
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +125 -14
  12. package/dist/commands/workflow.d.ts +4 -0
  13. package/dist/commands/workflow.d.ts.map +1 -0
  14. package/dist/commands/workflow.js +89 -0
  15. package/dist/core/agents/cli.js +10 -3
  16. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  17. package/dist/core/agents/rules/claim-conflict.js +26 -1
  18. package/dist/core/agents/rules/stop-hook.d.ts +8 -0
  19. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  20. package/dist/core/agents/rules/stop-hook.js +8 -0
  21. package/dist/core/agents/state/heartbeat-projector.d.ts +2 -0
  22. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  23. package/dist/core/agents/state/heartbeat-projector.js +13 -2
  24. package/dist/core/agents/state/heartbeat-writer.d.ts +16 -2
  25. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  26. package/dist/core/agents/state/heartbeat-writer.js +21 -4
  27. package/dist/core/config.d.ts +9 -0
  28. package/dist/core/config.d.ts.map +1 -1
  29. package/dist/core/config.js +19 -0
  30. package/dist/core/hooks/cli.js +29 -3
  31. package/dist/core/hooks/events/schema.d.ts +4 -0
  32. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  33. package/dist/core/hooks/harness/events.d.ts +7 -0
  34. package/dist/core/hooks/harness/events.d.ts.map +1 -1
  35. package/dist/core/hooks/harness/events.js +1 -0
  36. package/dist/core/hooks/resolve/coord-root.d.ts +11 -0
  37. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -1
  38. package/dist/core/hooks/resolve/coord-root.js +28 -6
  39. package/dist/core/workflow/billing.d.ts +48 -0
  40. package/dist/core/workflow/billing.d.ts.map +1 -0
  41. package/dist/core/workflow/billing.js +102 -0
  42. package/dist/core/workflow/child-env.d.ts +30 -0
  43. package/dist/core/workflow/child-env.d.ts.map +1 -0
  44. package/dist/core/workflow/child-env.js +43 -0
  45. package/dist/core/workflow/engine.d.ts +21 -0
  46. package/dist/core/workflow/engine.d.ts.map +1 -0
  47. package/dist/core/workflow/engine.js +340 -0
  48. package/dist/core/workflow/harnesses.d.ts +17 -0
  49. package/dist/core/workflow/harnesses.d.ts.map +1 -0
  50. package/dist/core/workflow/harnesses.js +30 -0
  51. package/dist/core/workflow/spawn-claude.d.ts +22 -0
  52. package/dist/core/workflow/spawn-claude.d.ts.map +1 -0
  53. package/dist/core/workflow/spawn-claude.js +82 -0
  54. package/dist/core/workflow/spawn-codex.d.ts +19 -0
  55. package/dist/core/workflow/spawn-codex.d.ts.map +1 -0
  56. package/dist/core/workflow/spawn-codex.js +66 -0
  57. package/dist/core/workflow/spawn-cursor.d.ts +26 -0
  58. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -0
  59. package/dist/core/workflow/spawn-cursor.js +72 -0
  60. package/dist/core/workflow/types.d.ts +145 -0
  61. package/dist/core/workflow/types.d.ts.map +1 -0
  62. package/dist/core/workflow/types.js +9 -0
  63. package/dist/core/workflow/validate.d.ts +15 -0
  64. package/dist/core/workflow/validate.d.ts.map +1 -0
  65. package/dist/core/workflow/validate.js +70 -0
  66. package/package.json +1 -1
  67. package/src/commander.ts +2 -0
  68. package/src/commands/doctor.ts +45 -0
  69. package/src/commands/grep.ts +535 -142
  70. package/src/commands/init.ts +138 -17
  71. package/src/commands/workflow.ts +132 -0
  72. package/src/core/agents/cli.ts +11 -4
  73. package/src/core/agents/rules/claim-conflict.ts +26 -1
  74. package/src/core/agents/rules/stop-hook.ts +17 -0
  75. package/src/core/agents/state/heartbeat-projector.ts +13 -1
  76. package/src/core/agents/state/heartbeat-writer.ts +30 -5
  77. package/src/core/config.ts +23 -0
  78. package/src/core/hooks/cli.ts +30 -3
  79. package/src/core/hooks/events/schema.ts +4 -0
  80. package/src/core/hooks/harness/events.ts +8 -0
  81. package/src/core/hooks/resolve/coord-root.ts +28 -6
  82. package/src/core/workflow/billing.ts +146 -0
  83. package/src/core/workflow/child-env.ts +47 -0
  84. package/src/core/workflow/engine.ts +394 -0
  85. package/src/core/workflow/harnesses.ts +38 -0
  86. package/src/core/workflow/spawn-claude.ts +99 -0
  87. package/src/core/workflow/spawn-codex.ts +74 -0
  88. package/src/core/workflow/spawn-cursor.ts +89 -0
  89. package/src/core/workflow/types.ts +153 -0
  90. package/src/core/workflow/validate.ts +75 -0
@@ -52,4 +52,13 @@ export declare function resolveHooksSetupHint(coordRoot?: string | null): string
52
52
  * `coordRoot` is resolved via `findCoordRoot()` when not passed.
53
53
  */
54
54
  export declare function ripgrepAutoInstall(coordRoot?: string | null): boolean;
55
+ /**
56
+ * Whether this repo pins workflow runs to subscription billing:
57
+ * `.harnery/config.jsonc` `{ "workflow": { "subscriptionOnly": true } }`.
58
+ * The `workflow run --subscription-only` flag turns it on per invocation;
59
+ * `HARNERY_WORKFLOW_SUBSCRIPTION_ONLY=1|0` overrides per process (the `0`
60
+ * escape hatch exists for a key-only CI job inside a pinned repo).
61
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
62
+ */
63
+ export declare function workflowSubscriptionOnly(coordRoot?: string | null): boolean;
55
64
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAuBvC,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqCvD;AAyBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAShE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAK9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAOrE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAS,CAAC;AA6BvC,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqCvD;AAyBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAShE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAK9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAOrE;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAO3E"}
@@ -151,3 +151,22 @@ export function ripgrepAutoInstall(coordRoot) {
151
151
  return false;
152
152
  return readConfig(root).tools?.ripgrep?.autoInstall === true;
153
153
  }
154
+ /**
155
+ * Whether this repo pins workflow runs to subscription billing:
156
+ * `.harnery/config.jsonc` `{ "workflow": { "subscriptionOnly": true } }`.
157
+ * The `workflow run --subscription-only` flag turns it on per invocation;
158
+ * `HARNERY_WORKFLOW_SUBSCRIPTION_ONLY=1|0` overrides per process (the `0`
159
+ * escape hatch exists for a key-only CI job inside a pinned repo).
160
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
161
+ */
162
+ export function workflowSubscriptionOnly(coordRoot) {
163
+ const env = coordEnv("WORKFLOW_SUBSCRIPTION_ONLY");
164
+ if (env === "1")
165
+ return true;
166
+ if (env === "0")
167
+ return false;
168
+ const root = coordRoot ?? findCoordRoot();
169
+ if (!root)
170
+ return false;
171
+ return readConfig(root).workflow?.subscriptionOnly === true;
172
+ }
@@ -69,6 +69,16 @@ async function readStdin() {
69
69
  return "";
70
70
  }
71
71
  }
72
+ /**
73
+ * Env for child agent-coord spawns, with the coord root pinned. The hook
74
+ * process may be running with the session shell's cwd (inside a submodule or
75
+ * scratch dir), and agent-coord resolves its root from cwd unless overridden —
76
+ * without the pin a child could write state to a different .harnery than the
77
+ * one this hook resolved.
78
+ */
79
+ function childEnv(coordRoot) {
80
+ return { ...process.env, HARNERY_COORD_ROOT_OVERRIDE: coordRoot };
81
+ }
72
82
  function appendDebug(coordRoot, entry) {
73
83
  const path = join(coordRoot, ".harnery", "debug", "agent-hook.ndjson");
74
84
  try {
@@ -112,6 +122,7 @@ function assignNameViaAgentCoord(coordRoot, instanceId, kind) {
112
122
  const result = spawnSync(binary, ["assign-name", instanceId, kind], {
113
123
  encoding: "utf8",
114
124
  timeout: 2000,
125
+ env: childEnv(coordRoot),
115
126
  });
116
127
  if (result.status !== 0 || !result.stdout)
117
128
  return null;
@@ -206,6 +217,12 @@ function buildEventData(eventType, ctx) {
206
217
  name: assigned?.name,
207
218
  kind: "session",
208
219
  agent_id: ctx.instanceId,
220
+ // Workflow-child linkage: `workflow run` children carry the run id in
221
+ // env (spawn adapters set it via buildChildEnv), so their sessions and
222
+ // heartbeats join back to the run journal + /workflows web view.
223
+ ...(coordEnv("WORKFLOW_CHILD") === "1" && coordEnv("WORKFLOW_RUN_ID")
224
+ ? { workflow_run_id: coordEnv("WORKFLOW_RUN_ID") }
225
+ : {}),
209
226
  };
210
227
  }
211
228
  case "session.end":
@@ -451,13 +468,17 @@ async function main() {
451
468
  try {
452
469
  const agentCoordBin = join(coordRoot, "harnery", "bin", "agent-coord");
453
470
  if (existsSync(agentCoordBin)) {
454
- spawnSync(agentCoordBin, ["project"], { encoding: "utf8", timeout: 3000 });
471
+ spawnSync(agentCoordBin, ["project"], {
472
+ encoding: "utf8",
473
+ timeout: 3000,
474
+ env: childEnv(coordRoot),
475
+ });
455
476
  spawnSync(agentCoordBin, [
456
477
  "log",
457
478
  `SUBAGENT_START agent_type=${data.agent_type ?? "unknown"} agent_id=${owner.instance_id.slice(0, 8)} platform=${harnessPlatform(harness)}`,
458
479
  "--instance",
459
480
  owner.instance_id,
460
- ], { encoding: "utf8", timeout: 2000 });
481
+ ], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
461
482
  }
462
483
  emitSubagentStartContext(coordRoot, owner.instance_id, sessionId, data, harness);
463
484
  }
@@ -476,7 +497,7 @@ async function main() {
476
497
  `SUBAGENT_STOP agent_id=${owner.instance_id.slice(0, 8)} platform=${harnessPlatform(harness)}`,
477
498
  "--instance",
478
499
  owner.instance_id,
479
- ], { encoding: "utf8", timeout: 2000 });
500
+ ], { encoding: "utf8", timeout: 2000, env: childEnv(coordRoot) });
480
501
  }
481
502
  }
482
503
  catch (err) {
@@ -557,6 +578,7 @@ async function main() {
557
578
  session_id: sessionId,
558
579
  harness,
559
580
  bypass: coordEnv("AGENT_COORD_BYPASS_STOP") === "1",
581
+ workflow_child: coordEnv("WORKFLOW_CHILD") === "1",
560
582
  });
561
583
  if (!verdict.allow) {
562
584
  // Harness-aware enforcement channel: Claude Code / Codex honor exit-2 +
@@ -710,6 +732,7 @@ async function runPreToolUseGuard(coordRoot, instanceId, sessionId, data, harnes
710
732
  input: verdictReq,
711
733
  encoding: "utf8",
712
734
  timeout: 3000,
735
+ env: childEnv(coordRoot),
713
736
  });
714
737
  if (result.status !== 0 || !result.stdout)
715
738
  continue;
@@ -804,6 +827,7 @@ function healHeartbeatViaCli(coordRoot, instanceId, sessionId, harness) {
804
827
  spawnSync(agentCoordBin, ["heal-heartbeat", instanceId, sessionId, `--harness=${harness}`], {
805
828
  encoding: "utf8",
806
829
  timeout: 2000,
830
+ env: childEnv(coordRoot),
807
831
  });
808
832
  }
809
833
  /**
@@ -908,6 +932,7 @@ function stampToolActivity(coordRoot, instanceId, data) {
908
932
  spawnSync(agentCoordBin, ["stamp-tool-activity", instanceId, toolName, target], {
909
933
  encoding: "utf8",
910
934
  timeout: 2000,
935
+ env: childEnv(coordRoot),
911
936
  });
912
937
  }
913
938
  function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
@@ -955,6 +980,7 @@ function releaseClaimOnFailure(coordRoot, instanceId, data, rawPayload) {
955
980
  spawnSync(agentCoordBin, ["release-claim", instanceId, canonical], {
956
981
  encoding: "utf8",
957
982
  timeout: 2000,
983
+ env: childEnv(coordRoot),
958
984
  });
959
985
  }
960
986
  function cleanupSessionEnd(coordRoot, instanceId, reason) {
@@ -37,6 +37,10 @@ export type SessionStart = EventEnvelope<"session.start", {
37
37
  cwd: string;
38
38
  model?: string;
39
39
  pid: number;
40
+ /** Present iff this session is a `workflow run` child: the run id whose
41
+ * journal owns it (child env HARNERY_WORKFLOW_RUN_ID). Optional-field
42
+ * addition per the schema evolution rules (minor bump). */
43
+ workflow_run_id?: string;
40
44
  }>;
41
45
  export type SessionEnd = EventEnvelope<"session.end", {
42
46
  ended_at: string;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/events/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/events/schema.ts"],"names":[],"mappings":"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"}
@@ -36,6 +36,13 @@ export interface HarnessSpec {
36
36
  allowedTopLevelKeys?: string[];
37
37
  /** Strict hook event keys accepted by this harness, including events harnery does not wire. */
38
38
  allowedEventKeys?: string[];
39
+ /**
40
+ * Env var the harness exports to hook processes carrying the project root
41
+ * (e.g. Claude Code's CLAUDE_PROJECT_DIR). When set, `init` anchors the
42
+ * agent-hook path on it so the hook still spawns when the process cwd has
43
+ * wandered (the session shell `cd`ing into a subdirectory or off-repo).
44
+ */
45
+ projectDirEnv?: string;
39
46
  }
40
47
  /** Claude Code: `.claude/settings.json`. */
41
48
  export declare const CLAUDE_CODE_EVENTS: HookEvent[];
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/harness/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,6FAA6F;IAC7F,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,cAAc,CAAC;IAC3B,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B,0FAA0F;IAC1F,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,+FAA+F;IAC/F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,EAAE,SAAS,EAWzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,EAWpC,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,YAAY,EAAE,SAAS,EAQnC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,EAAE,SAAS,EAI1C,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,wBAAwB,UAWpC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,SAAS,EAAE,WAAW,CAoBxD,CAAC"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/harness/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,6FAA6F;IAC7F,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,cAAc,CAAC;IAC3B,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B,0FAA0F;IAC1F,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,+FAA+F;IAC/F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,EAAE,SAAS,EAWzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,EAWpC,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,YAAY,EAAE,SAAS,EAQnC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,EAAE,SAAS,EAI1C,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,wBAAwB,UAWpC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,SAAS,EAAE,WAAW,CAqBxD,CAAC"}
@@ -72,6 +72,7 @@ export const HARNESS_SPECS = {
72
72
  settingsFile: ".claude/settings.json",
73
73
  events: CLAUDE_CODE_EVENTS,
74
74
  entryShape: "claude",
75
+ projectDirEnv: "CLAUDE_PROJECT_DIR",
75
76
  },
76
77
  cursor: {
77
78
  settingsFile: ".cursor/hooks.json",
@@ -1,6 +1,17 @@
1
1
  /**
2
2
  * Walk up from `start` looking for a directory containing `.harnery/`. The
3
3
  * single coord-root resolution so every adapter agrees on the same root.
4
+ *
5
+ * Resolution precedence:
6
+ * 1. HARNERY_COORD_ROOT_OVERRIDE — explicit pin (tests, git hooks).
7
+ * 2. The harness's project dir (CLAUDE_PROJECT_DIR) — hook processes
8
+ * inherit the session's *shell* cwd, which follows `cd` into
9
+ * subdirectories/submodules that may carry a `.harnery/` of their own
10
+ * (or none at all, e.g. a scratch dir under /tmp). The session's
11
+ * coordination home is the project the harness opened, not wherever the
12
+ * shell happens to sit, so a project dir that resolves to a coord root
13
+ * wins over the cwd walk.
14
+ * 3. Walk up from `start` (default cwd).
4
15
  */
5
16
  export declare function findCoordRoot(start?: string): string | null;
6
17
  //# sourceMappingURL=coord-root.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"coord-root.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/resolve/coord-root.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAgB1E"}
1
+ {"version":3,"file":"coord-root.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/resolve/coord-root.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAiB1E"}
@@ -4,17 +4,39 @@ import { coordEnv } from "../../../lib/env.js";
4
4
  /**
5
5
  * Walk up from `start` looking for a directory containing `.harnery/`. The
6
6
  * single coord-root resolution so every adapter agrees on the same root.
7
+ *
8
+ * Resolution precedence:
9
+ * 1. HARNERY_COORD_ROOT_OVERRIDE — explicit pin (tests, git hooks).
10
+ * 2. The harness's project dir (CLAUDE_PROJECT_DIR) — hook processes
11
+ * inherit the session's *shell* cwd, which follows `cd` into
12
+ * subdirectories/submodules that may carry a `.harnery/` of their own
13
+ * (or none at all, e.g. a scratch dir under /tmp). The session's
14
+ * coordination home is the project the harness opened, not wherever the
15
+ * shell happens to sit, so a project dir that resolves to a coord root
16
+ * wins over the cwd walk.
17
+ * 3. Walk up from `start` (default cwd).
7
18
  */
8
19
  export function findCoordRoot(start = process.cwd()) {
9
- // HARNERY_COORD_ROOT_OVERRIDE: test-mode escape hatch matched by agent-coord
10
- // and the bash side. Lets the sandboxed coord-test suite run against a
11
- // temp directory rather than the real monorepo root. agent-hook's
12
- // session.start handler may be the FIRST thing to create .harnery/ in a
13
- // fresh sandbox, so the override is honored unconditionally (we don't
14
- // require .harnery/ to pre-exist).
20
+ // HARNERY_COORD_ROOT_OVERRIDE: explicit pin matched by agent-coord and the
21
+ // bash side. Lets the sandboxed coord-test suite run against a temp
22
+ // directory rather than the real monorepo root. agent-hook's session.start
23
+ // handler may be the FIRST thing to create .harnery/ in a fresh sandbox,
24
+ // so the override is honored unconditionally (we don't require .harnery/
25
+ // to pre-exist).
15
26
  const override = coordEnv("COORD_ROOT_OVERRIDE");
16
27
  if (override)
17
28
  return override;
29
+ // Claude Code exports CLAUDE_PROJECT_DIR to every hook process; other
30
+ // harnesses don't set it, so this is inert outside claude-code hooks.
31
+ const projectDir = process.env.CLAUDE_PROJECT_DIR;
32
+ if (projectDir) {
33
+ const fromProject = walkUp(projectDir);
34
+ if (fromProject)
35
+ return fromProject;
36
+ }
37
+ return walkUp(start);
38
+ }
39
+ function walkUp(start) {
18
40
  let dir = resolve(start);
19
41
  while (true) {
20
42
  if (existsSync(join(dir, ".harnery")))
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Billing-mode probe: which auth a headless harness child will actually use.
3
+ *
4
+ * The engine never handles credentials — children are plain harness CLIs and
5
+ * authenticate however that CLI does. But the CLIs prefer an exported API key
6
+ * over a stored (subscription) login when both are present, and that override
7
+ * is almost always an accident: a sourced .env or a leftover export silently
8
+ * moves the run from subsidized subscription billing to per-token API billing.
9
+ * This probe classifies the state per harness so the engine can refuse the
10
+ * silent-override case (see decision 0015 addendum) while leaving deliberate
11
+ * key-only hosts (CI boxes with no login) working.
12
+ *
13
+ * Detection is a heuristic over well-known credential locations; when a
14
+ * location can't prove presence or absence (e.g. Claude Code stores its OAuth
15
+ * token in the macOS keychain, not a file), the state is "unknown" and the
16
+ * engine never hard-fails on it — the harness CLI itself is the final
17
+ * authority and will error loudly if truly unauthenticated.
18
+ */
19
+ import type { HarnessName } from "./types.js";
20
+ /** The env var each harness CLI reads as an API key (preferred over a stored
21
+ * login when set). Shared with the child-env builder's subscription-only
22
+ * scrub. */
23
+ export declare const API_KEY_VARS: Record<HarnessName, string>;
24
+ export type LoginState = "present" | "absent" | "unknown";
25
+ export type BillingMode =
26
+ /** No API key exported; the child rides the stored (subscription) login. */
27
+ "subscription"
28
+ /** API key present, no stored login detected: a deliberate key-only host. */
29
+ | "api-key"
30
+ /** API key present AND a stored login exists: the key silently overrides
31
+ * subsidized auth. The engine refuses this unless explicitly allowed. */
32
+ | "api-key-override";
33
+ export interface BillingProbe {
34
+ harness: HarnessName;
35
+ /** The env var checked (from API_KEY_VARS), or a note when the key was
36
+ * found in a credential file instead of the environment. */
37
+ apiKeySource: string | null;
38
+ apiKeyPresent: boolean;
39
+ login: LoginState;
40
+ mode: BillingMode;
41
+ }
42
+ export interface ProbeIo {
43
+ env?: NodeJS.ProcessEnv;
44
+ home?: string;
45
+ }
46
+ export type BillingProber = (harness: HarnessName) => BillingProbe;
47
+ export declare function probeBilling(harness: HarnessName, io?: ProbeIo): BillingProbe;
48
+ //# sourceMappingURL=billing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/billing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;YAEY;AACZ,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAIpD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE1D,MAAM,MAAM,WAAW;AACrB,4EAA4E;AAC1E,cAAc;AAChB,6EAA6E;GAC3E,SAAS;AACX;yEACyE;GACvE,kBAAkB,CAAC;AAEvB,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB;gEAC4D;IAC5D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,YAAY,CAAC;AAEnE,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,GAAE,OAAY,GAAG,YAAY,CAyCjF"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Billing-mode probe: which auth a headless harness child will actually use.
3
+ *
4
+ * The engine never handles credentials — children are plain harness CLIs and
5
+ * authenticate however that CLI does. But the CLIs prefer an exported API key
6
+ * over a stored (subscription) login when both are present, and that override
7
+ * is almost always an accident: a sourced .env or a leftover export silently
8
+ * moves the run from subsidized subscription billing to per-token API billing.
9
+ * This probe classifies the state per harness so the engine can refuse the
10
+ * silent-override case (see decision 0015 addendum) while leaving deliberate
11
+ * key-only hosts (CI boxes with no login) working.
12
+ *
13
+ * Detection is a heuristic over well-known credential locations; when a
14
+ * location can't prove presence or absence (e.g. Claude Code stores its OAuth
15
+ * token in the macOS keychain, not a file), the state is "unknown" and the
16
+ * engine never hard-fails on it — the harness CLI itself is the final
17
+ * authority and will error loudly if truly unauthenticated.
18
+ */
19
+ import { existsSync, readFileSync } from "node:fs";
20
+ import { homedir } from "node:os";
21
+ import { join } from "node:path";
22
+ /** The env var each harness CLI reads as an API key (preferred over a stored
23
+ * login when set). Shared with the child-env builder's subscription-only
24
+ * scrub. */
25
+ export const API_KEY_VARS = {
26
+ "claude-code": "ANTHROPIC_API_KEY",
27
+ codex: "OPENAI_API_KEY",
28
+ cursor: "CURSOR_API_KEY",
29
+ };
30
+ export function probeBilling(harness, io = {}) {
31
+ const env = io.env ?? process.env;
32
+ const home = io.home ?? homedir();
33
+ const keyVar = API_KEY_VARS[harness];
34
+ const envKey = Boolean(env[keyVar]?.trim());
35
+ let login;
36
+ let apiKeyPresent = envKey;
37
+ let apiKeySource = envKey ? keyVar : null;
38
+ switch (harness) {
39
+ case "claude-code":
40
+ login = probeClaudeLogin(home);
41
+ break;
42
+ case "codex": {
43
+ const codex = probeCodexAuth(env, home);
44
+ login = codex.login;
45
+ // `codex login --api-key` stores the key in auth.json rather than the
46
+ // env; that is still API-key billing and should surface as such.
47
+ if (!envKey && codex.storedApiKey) {
48
+ apiKeyPresent = true;
49
+ apiKeySource = codex.storedApiKeyNote;
50
+ }
51
+ break;
52
+ }
53
+ case "cursor":
54
+ // cursor-agent's stored-login location is not yet verified against a
55
+ // live install (adapter itself is pending verification); never claim
56
+ // presence or absence we can't prove.
57
+ login = "unknown";
58
+ break;
59
+ }
60
+ const mode = apiKeyPresent && login === "present"
61
+ ? "api-key-override"
62
+ : apiKeyPresent
63
+ ? "api-key"
64
+ : "subscription";
65
+ return { harness, apiKeySource, apiKeyPresent, login, mode };
66
+ }
67
+ /** Claude Code: OAuth login lives at ~/.claude/.credentials.json on Linux;
68
+ * macOS stores it in the keychain (dir exists, file doesn't → unknown). */
69
+ function probeClaudeLogin(home) {
70
+ const dir = join(home, ".claude");
71
+ const credFile = join(dir, ".credentials.json");
72
+ if (existsSync(credFile)) {
73
+ try {
74
+ const parsed = JSON.parse(readFileSync(credFile, "utf8"));
75
+ return parsed.claudeAiOauth?.accessToken ? "present" : "absent";
76
+ }
77
+ catch {
78
+ return "unknown";
79
+ }
80
+ }
81
+ return existsSync(dir) ? "unknown" : "absent";
82
+ }
83
+ /** codex: auth lives at $CODEX_HOME/auth.json (default ~/.codex). A `tokens`
84
+ * object means a ChatGPT (subscription) login; an OPENAI_API_KEY field means
85
+ * a stored API key (`codex login --api-key`). */
86
+ function probeCodexAuth(env, home) {
87
+ const authPath = join(env.CODEX_HOME?.trim() || join(home, ".codex"), "auth.json");
88
+ const storedApiKeyNote = `${authPath} (stored key)`;
89
+ if (!existsSync(authPath))
90
+ return { login: "absent", storedApiKey: false, storedApiKeyNote };
91
+ try {
92
+ const parsed = JSON.parse(readFileSync(authPath, "utf8"));
93
+ return {
94
+ login: parsed.tokens ? "present" : "absent",
95
+ storedApiKey: !parsed.tokens && Boolean(parsed.OPENAI_API_KEY),
96
+ storedApiKeyNote,
97
+ };
98
+ }
99
+ catch {
100
+ return { login: "unknown", storedApiKey: false, storedApiKeyNote };
101
+ }
102
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Child-process environment builder shared by every spawn adapter.
3
+ *
4
+ * Rules (the first is a hard-won Phase 1 spike finding):
5
+ * 1. **Delete inherited harness-session vars, never blank them.** A nested
6
+ * harness CLI under a live session inherits vars that make it exit 1 with
7
+ * empty output; an empty-string var still reads as set. Scrub all three
8
+ * families (CLAUDE*, CODEX*, CURSOR*) regardless of which adapter spawns —
9
+ * a codex child launched from inside a Claude Code session must not
10
+ * inherit CLAUDE* either.
11
+ * 2. **The scrub targets SESSION vars, not credentials.** CURSOR_API_KEY
12
+ * matches the CURSOR* prefix but is an auth credential, so it is carved
13
+ * out and re-added — a key-only cursor host must keep working. (The other
14
+ * two key vars, ANTHROPIC_API_KEY and OPENAI_API_KEY, don't collide with
15
+ * the scrub prefixes.)
16
+ * 3. **subscriptionOnly deletes every API-key var** (see billing.ts) so the
17
+ * child can only authenticate via its stored subscription login — the
18
+ * guarantee behind `workflow run --subscription-only`.
19
+ * 4. **Mark the child as a workflow child** (HARNERY_WORKFLOW_CHILD=1): the
20
+ * stop-hook rule exempts it from the human-facing end-of-turn ritual while
21
+ * hooks stay on, keeping heartbeat + event capture.
22
+ * 5. **Stamp the run id** (HARNERY_WORKFLOW_RUN_ID) so the coord layer and
23
+ * web UI can associate child sessions with their workflow run.
24
+ */
25
+ export interface ChildEnvOpts {
26
+ /** Delete all API-key vars so children can only use stored logins. */
27
+ subscriptionOnly?: boolean;
28
+ }
29
+ export declare function buildChildEnv(runId?: string, opts?: ChildEnvOpts): Record<string, string>;
30
+ //# sourceMappingURL=child-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-env.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/child-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAY7F"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Child-process environment builder shared by every spawn adapter.
3
+ *
4
+ * Rules (the first is a hard-won Phase 1 spike finding):
5
+ * 1. **Delete inherited harness-session vars, never blank them.** A nested
6
+ * harness CLI under a live session inherits vars that make it exit 1 with
7
+ * empty output; an empty-string var still reads as set. Scrub all three
8
+ * families (CLAUDE*, CODEX*, CURSOR*) regardless of which adapter spawns —
9
+ * a codex child launched from inside a Claude Code session must not
10
+ * inherit CLAUDE* either.
11
+ * 2. **The scrub targets SESSION vars, not credentials.** CURSOR_API_KEY
12
+ * matches the CURSOR* prefix but is an auth credential, so it is carved
13
+ * out and re-added — a key-only cursor host must keep working. (The other
14
+ * two key vars, ANTHROPIC_API_KEY and OPENAI_API_KEY, don't collide with
15
+ * the scrub prefixes.)
16
+ * 3. **subscriptionOnly deletes every API-key var** (see billing.ts) so the
17
+ * child can only authenticate via its stored subscription login — the
18
+ * guarantee behind `workflow run --subscription-only`.
19
+ * 4. **Mark the child as a workflow child** (HARNERY_WORKFLOW_CHILD=1): the
20
+ * stop-hook rule exempts it from the human-facing end-of-turn ritual while
21
+ * hooks stay on, keeping heartbeat + event capture.
22
+ * 5. **Stamp the run id** (HARNERY_WORKFLOW_RUN_ID) so the coord layer and
23
+ * web UI can associate child sessions with their workflow run.
24
+ */
25
+ import { API_KEY_VARS } from "./billing.js";
26
+ const SCRUB_PREFIXES = ["CLAUDE", "CODEX", "CURSOR"];
27
+ export function buildChildEnv(runId, opts = {}) {
28
+ const env = {};
29
+ const keyVars = new Set(Object.values(API_KEY_VARS));
30
+ for (const [k, v] of Object.entries(process.env)) {
31
+ if (v === undefined)
32
+ continue;
33
+ if (opts.subscriptionOnly && keyVars.has(k))
34
+ continue;
35
+ if (SCRUB_PREFIXES.some((p) => k.startsWith(p)) && !keyVars.has(k))
36
+ continue;
37
+ env[k] = v;
38
+ }
39
+ env.HARNERY_WORKFLOW_CHILD = "1";
40
+ if (runId)
41
+ env.HARNERY_WORKFLOW_RUN_ID = runId;
42
+ return env;
43
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Workflow engine: loads a workflow script (plain JS, `export default
3
+ * async (ctx) => …`), injects the ctx API, enforces the caps, journals every
4
+ * step to `.harnery/workflows/<run-id>/journal.jsonl`, and returns a RunReport.
5
+ *
6
+ * Guarantees the engine makes (the pitch, in code):
7
+ * - **Bounded**: hard total-agent ceiling + bounded parallel() concurrency.
8
+ * A runaway loop hits `maxAgents` and the run fails loud, not silently.
9
+ * - **Terminating**: the run is over when the script's default export
10
+ * returns. There is no recursive self-spawning path: subagents are leaf
11
+ * processes; only the top-level script can spawn.
12
+ * - **Schema-gated**: with `schema`, an agent's reply must strict-parse and
13
+ * validate; failures re-prompt with the validation errors appended, up to
14
+ * `maxAttempts`, then throw. Routing decisions read validated fields, so
15
+ * the deterministic script — not a model — decides what runs next.
16
+ * - **Journaled**: every stage/agent start+end lands in the run journal with
17
+ * cost, duration, and child session id (the resume + web-UI substrate).
18
+ */
19
+ import type { EngineOpts, RunReport } from "./types.js";
20
+ export declare function runWorkflow(scriptPath: string, opts: EngineOpts): Promise<RunReport>;
21
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,OAAO,KAAK,EAEV,UAAU,EAEV,SAAS,EAIV,MAAM,YAAY,CAAC;AASpB,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAwR1F"}