harnery 0.9.0 → 0.11.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 (211) 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 +53 -17
  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 +5 -0
  108. package/dist/core/workflow/engine.d.ts.map +1 -1
  109. package/dist/core/workflow/engine.js +164 -16
  110. package/dist/core/workflow/harnesses.d.ts +3 -3
  111. package/dist/core/workflow/harnesses.d.ts.map +1 -1
  112. package/dist/core/workflow/harnesses.js +8 -17
  113. package/dist/core/workflow/index.d.ts +5 -0
  114. package/dist/core/workflow/index.d.ts.map +1 -0
  115. package/dist/core/workflow/index.js +3 -0
  116. package/dist/core/workflow/proof.d.ts +42 -0
  117. package/dist/core/workflow/proof.d.ts.map +1 -0
  118. package/dist/core/workflow/proof.js +342 -0
  119. package/dist/core/workflow/spawn-claude.d.ts +4 -1
  120. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  121. package/dist/core/workflow/spawn-claude.js +39 -18
  122. package/dist/core/workflow/spawn-codex.d.ts +4 -1
  123. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  124. package/dist/core/workflow/spawn-codex.js +44 -22
  125. package/dist/core/workflow/spawn-cursor.d.ts +4 -1
  126. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  127. package/dist/core/workflow/spawn-cursor.js +35 -16
  128. package/dist/core/workflow/types.d.ts +156 -2
  129. package/dist/core/workflow/types.d.ts.map +1 -1
  130. package/dist/core/workflow/types.js +1 -1
  131. package/dist/lib/browser/client.d.ts +16 -0
  132. package/dist/lib/browser/client.d.ts.map +1 -1
  133. package/dist/lib/browser/client.js +22 -0
  134. package/dist/lib/browser/index.d.ts +1 -0
  135. package/dist/lib/browser/index.d.ts.map +1 -1
  136. package/dist/lib/browser/index.js +1 -0
  137. package/dist/lib/browser/launch-args.d.ts +21 -0
  138. package/dist/lib/browser/launch-args.d.ts.map +1 -0
  139. package/dist/lib/browser/launch-args.js +32 -0
  140. package/dist/lib/claude-desktop.d.ts +105 -0
  141. package/dist/lib/claude-desktop.d.ts.map +1 -0
  142. package/dist/lib/claude-desktop.js +217 -0
  143. package/dist/lib/docs-lint.d.ts.map +1 -1
  144. package/dist/lib/docs-lint.js +5 -2
  145. package/dist/lib/identities/assume.d.ts +51 -0
  146. package/dist/lib/identities/assume.d.ts.map +1 -0
  147. package/dist/lib/identities/assume.js +274 -0
  148. package/dist/lib/identities/index.d.ts +7 -7
  149. package/dist/lib/identities/index.d.ts.map +1 -1
  150. package/dist/lib/identities/index.js +24 -24
  151. package/dist/lib/instructions/templates.d.ts.map +1 -1
  152. package/dist/lib/instructions/templates.js +15 -2
  153. package/package.json +22 -6
  154. package/schemas/config.schema.json +88 -19
  155. package/src/commander.ts +6 -0
  156. package/src/commands/agents.ts +280 -14
  157. package/src/commands/backup.ts +17 -16
  158. package/src/commands/browse.ts +32 -0
  159. package/src/commands/claude-desktop.ts +215 -0
  160. package/src/commands/context.ts +174 -1
  161. package/src/commands/doctor.ts +2 -3
  162. package/src/commands/eml.ts +22 -4
  163. package/src/commands/harness.ts +147 -0
  164. package/src/commands/presence.ts +111 -5
  165. package/src/commands/relay.ts +160 -0
  166. package/src/commands/sync.ts +4 -0
  167. package/src/commands/workflow.ts +62 -19
  168. package/src/core/agents/canonical-emit.ts +41 -3
  169. package/src/core/agents/cli.ts +4 -2
  170. package/src/core/agents/render/prompt-context.ts +48 -3
  171. package/src/core/agents/render/session-context.ts +27 -1
  172. package/src/core/agents/rules/commit-conflict.ts +4 -33
  173. package/src/core/agents/session-events.ts +15 -39
  174. package/src/core/agents/state/heartbeat-projector.ts +10 -1
  175. package/src/core/agents/state/heartbeat-writer.ts +3 -2
  176. package/src/core/agents/state/names.ts +68 -9
  177. package/src/core/agents/state/pidmap.ts +59 -2
  178. package/src/core/agents/state/stale-sweep.ts +6 -11
  179. package/src/core/config.ts +248 -24
  180. package/src/core/context/index.ts +575 -0
  181. package/src/core/harnesses/bench.ts +214 -0
  182. package/src/core/harnesses/index.ts +31 -0
  183. package/src/core/harnesses/profiles.ts +141 -0
  184. package/src/core/harnesses/registry.ts +140 -0
  185. package/src/core/harnesses/types.ts +90 -0
  186. package/src/core/hooks/cli.ts +331 -62
  187. package/src/core/hooks/events/schema.ts +68 -0
  188. package/src/core/hooks/harness/events.ts +3 -0
  189. package/src/core/hooks/harness/parse.ts +7 -1
  190. package/src/core/presence/blob.ts +125 -0
  191. package/src/core/presence/git.ts +191 -0
  192. package/src/core/presence/index.ts +274 -0
  193. package/src/core/presence/relay-client.ts +322 -0
  194. package/src/core/presence/relay-protocol.ts +246 -0
  195. package/src/core/workflow/billing.ts +8 -2
  196. package/src/core/workflow/engine.ts +182 -16
  197. package/src/core/workflow/harnesses.ts +14 -19
  198. package/src/core/workflow/index.ts +43 -0
  199. package/src/core/workflow/proof.ts +434 -0
  200. package/src/core/workflow/spawn-claude.ts +39 -20
  201. package/src/core/workflow/spawn-codex.ts +47 -28
  202. package/src/core/workflow/spawn-cursor.ts +37 -18
  203. package/src/core/workflow/types.ts +176 -2
  204. package/src/lib/browser/client.ts +37 -0
  205. package/src/lib/browser/index.ts +1 -0
  206. package/src/lib/browser/launch-args.ts +33 -0
  207. package/src/lib/claude-desktop.ts +293 -0
  208. package/src/lib/docs-lint.ts +5 -2
  209. package/src/lib/identities/assume.ts +363 -0
  210. package/src/lib/identities/index.ts +28 -24
  211. package/src/lib/instructions/templates.ts +16 -2
@@ -28,29 +28,46 @@ import { createHash, randomBytes } from "node:crypto";
28
28
  import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
29
29
  import { isAbsolute, join, resolve } from "node:path";
30
30
  import { pathToFileURL } from "node:url";
31
+ import { snapshotRepo } from "../context/index.js";
31
32
  import { probeBilling } from "./billing.js";
33
+ import { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, writeWorkflowProof, } from "./proof.js";
34
+ import { WORKFLOW_PROOF_SCHEMA_VERSION } from "./types.js";
32
35
  import { parseStageOutput, validateAgainstSchema } from "./validate.js";
33
36
  const DEFAULT_MAX_AGENTS = 50;
34
37
  const DEFAULT_CONCURRENCY = 4;
35
38
  const DEFAULT_MAX_ATTEMPTS = 2;
36
39
  const DEFAULT_TIMEOUT_MS = 300_000;
37
40
  const DEFAULT_MAX_TURNS = 25;
41
+ export class WorkflowRunError extends Error {
42
+ runId;
43
+ proofPath;
44
+ constructor(message, runId, proofPath, cause) {
45
+ super(message, cause === undefined ? undefined : { cause });
46
+ this.name = "WorkflowRunError";
47
+ this.runId = runId;
48
+ this.proofPath = proofPath;
49
+ }
50
+ }
38
51
  export async function runWorkflow(scriptPath, opts) {
39
52
  const absScript = isAbsolute(scriptPath) ? scriptPath : resolve(process.cwd(), scriptPath);
40
53
  const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(absScript).href)));
41
54
  if (typeof mod.default !== "function") {
42
55
  throw new Error(`${scriptPath}: workflow script must \`export default async (ctx) => …\``);
43
56
  }
44
- const name = mod.meta?.name ?? scriptPath.replace(/^.*\//, "").replace(/\.[cm]?js$/, "");
57
+ const fallbackName = scriptPath.replace(/^.*\//, "").replace(/\.[cm]?js$/, "");
58
+ const meta = normalizeWorkflowMeta(mod.meta, fallbackName);
59
+ const name = meta.name;
45
60
  const runId = `wf-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomBytes(3).toString("hex")}`;
46
61
  const runDir = join(opts.coordRoot, ".harnery", "workflows", runId);
47
62
  mkdirSync(runDir, { recursive: true });
48
63
  const journalPath = join(runDir, "journal.jsonl");
64
+ const proofPath = join(runDir, "proof.json");
49
65
  const maxAgents = opts.maxAgents ?? DEFAULT_MAX_AGENTS;
50
66
  const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
51
67
  const cwd = opts.cwd ?? opts.coordRoot;
52
68
  const log = opts.onLog ?? ((line) => process.stderr.write(`${line}\n`));
53
69
  const defaultHarness = opts.defaultHarness ?? "claude-code";
70
+ const repoBefore = snapshotRepo(cwd);
54
71
  // Per-child fixed context overhead: children spawn in `cwd` and load its
55
72
  // repo-instructions file into their system prompt, cache-writing it once
56
73
  // per child. A fan-out multiplies this, so surface it BEFORE the burn.
@@ -68,9 +85,15 @@ export async function runWorkflow(scriptPath, opts) {
68
85
  let costUsd = 0;
69
86
  let currentStage = "";
70
87
  let agentSeq = 0;
88
+ let evidenceSeq = 0;
71
89
  const billingProbed = new Map();
90
+ const agentProofs = new Map();
91
+ const evidenceRecords = [];
92
+ const acceptanceIds = new Set(meta.acceptance.map((criterion) => criterion.id));
72
93
  const journal = (event, data) => {
73
94
  const line = JSON.stringify({
95
+ schema_version: WORKFLOW_PROOF_SCHEMA_VERSION,
96
+ run_id: runId,
74
97
  ts: new Date().toISOString(),
75
98
  event,
76
99
  stage: currentStage,
@@ -103,14 +126,35 @@ export async function runWorkflow(scriptPath, opts) {
103
126
  }
104
127
  const id = `a${++agentSeq}`;
105
128
  const label = agentOpts.label ?? `${prompt.slice(0, 60).replace(/\s+/g, " ")}…`;
129
+ const proofLabel = agentOpts.label ?? id;
106
130
  const maxAttempts = agentOpts.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
107
- // Call identity for resume: same stage + harness + model + turns + schema
131
+ const agentProof = {
132
+ id,
133
+ label: proofLabel,
134
+ stage: currentStage || undefined,
135
+ harness,
136
+ model: agentOpts.model,
137
+ status: "failed",
138
+ attempts: 0,
139
+ duration_ms: 0,
140
+ };
141
+ agentProofs.set(id, agentProof);
142
+ // Call identity for resume: same stage + harness + model + effort + turns + schema
108
143
  // + ORIGINAL prompt → same key. Retry-mutated prompts never enter the key.
109
144
  const key = agentCallKey(currentStage, harness, agentOpts, prompt);
110
145
  const cached = resumeCache.get(key);
111
146
  if (cached) {
112
147
  agentsCached++;
113
- journal("agent.cached", { id, label, key, kind: cached.kind });
148
+ agentProof.status = "cached";
149
+ agentProof.result = digestResult(cached.value, cached.kind);
150
+ journal("agent.cached", {
151
+ id,
152
+ label,
153
+ key,
154
+ harness,
155
+ model: agentOpts.model ?? null,
156
+ kind: cached.kind,
157
+ });
114
158
  log(`[${name}] ${currentStage || "(no stage)"} → ${id} ${label} (cached from ${opts.resumeFrom})`);
115
159
  return cached.value;
116
160
  }
@@ -154,31 +198,49 @@ export async function runWorkflow(scriptPath, opts) {
154
198
  }
155
199
  }
156
200
  if (agentsSpawned >= maxAgents) {
201
+ agentProof.error = `workflow agent cap reached (${maxAgents})`;
157
202
  throw new Error(`workflow agent cap reached (${maxAgents}); raise --max-agents deliberately if the fan-out is intended`);
158
203
  }
159
204
  agentsSpawned++;
160
205
  await acquire();
161
206
  try {
162
- journal("agent.start", { id, label, key, harness, model: agentOpts.model ?? null });
207
+ journal("agent.start", {
208
+ id,
209
+ label,
210
+ key,
211
+ harness,
212
+ model: agentOpts.model ?? null,
213
+ effort: agentOpts.effort ?? null,
214
+ });
163
215
  log(`[${name}] ${currentStage || "(no stage)"} → ${id} [${harness}] ${label}`);
164
216
  let attemptPrompt = prompt;
165
217
  let last = null;
218
+ let agentCostUsd = 0;
166
219
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
167
220
  last = await spawner({
168
221
  prompt: attemptPrompt,
169
222
  model: agentOpts.model,
223
+ effort: agentOpts.effort,
170
224
  timeoutMs: agentOpts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
171
225
  maxTurns: agentOpts.maxTurns ?? DEFAULT_MAX_TURNS,
172
226
  cwd,
173
227
  runId,
174
228
  subscriptionOnly: opts.subscriptionOnly,
175
229
  });
230
+ agentProof.attempts = attempt;
231
+ agentProof.duration_ms += last.durationMs;
232
+ agentCostUsd += last.costUsd ?? 0;
176
233
  costUsd += last.costUsd ?? 0;
177
234
  if (!last.ok) {
178
235
  journal("agent.attempt_failed", { id, attempt, error: last.error });
179
236
  continue; // spawn-level failure: retry with the original prompt
180
237
  }
181
238
  if (!agentOpts.schema) {
239
+ agentProof.status = "succeeded";
240
+ agentProof.cost_usd =
241
+ agentCostUsd > 0 || last.costUsd !== undefined ? agentCostUsd : undefined;
242
+ agentProof.session_id = last.sessionId;
243
+ agentProof.result = digestResult(last.text, "text");
182
244
  journal("agent.end", {
183
245
  id,
184
246
  key,
@@ -196,6 +258,11 @@ export async function runWorkflow(scriptPath, opts) {
196
258
  ? [parsed.error]
197
259
  : validateAgainstSchema(parsed.value, agentOpts.schema);
198
260
  if (problems.length === 0) {
261
+ agentProof.status = "succeeded";
262
+ agentProof.cost_usd =
263
+ agentCostUsd > 0 || last.costUsd !== undefined ? agentCostUsd : undefined;
264
+ agentProof.session_id = last.sessionId;
265
+ agentProof.result = digestResult(parsed.value, "json");
199
266
  journal("agent.end", {
200
267
  id,
201
268
  key,
@@ -219,8 +286,16 @@ export async function runWorkflow(scriptPath, opts) {
219
286
  const reason = last?.ok
220
287
  ? `schema validation failed after ${maxAttempts} attempt(s)`
221
288
  : (last?.error ?? "spawn failed");
289
+ agentProof.cost_usd =
290
+ agentCostUsd > 0 || last?.costUsd !== undefined ? agentCostUsd : undefined;
291
+ agentProof.session_id = last?.sessionId;
292
+ agentProof.error = reason;
222
293
  journal("agent.failed", { id, error: reason });
223
- throw new Error(`agent ${id} (${label}): ${reason}`);
294
+ throw new Error(`agent ${proofLabel}: ${reason}`);
295
+ }
296
+ catch (error) {
297
+ agentProof.error ??= error.message;
298
+ throw error;
224
299
  }
225
300
  finally {
226
301
  release();
@@ -240,11 +315,62 @@ export async function runWorkflow(scriptPath, opts) {
240
315
  journal("stage.start", { title });
241
316
  log(`[${name}] ── stage: ${title}`);
242
317
  };
243
- const ctx = { agent, parallel, stage, log };
318
+ const evidence = (input) => {
319
+ const record = createEvidenceRecord({
320
+ value: input,
321
+ sequence: evidenceSeq + 1,
322
+ acceptanceIds,
323
+ stage: currentStage || undefined,
324
+ });
325
+ evidenceSeq++;
326
+ evidenceRecords.push(record);
327
+ journal("evidence.recorded", { ...record });
328
+ return record.id;
329
+ };
330
+ const ctx = { agent, parallel, stage, log, evidence };
244
331
  const t0 = Date.now();
245
- journal("run.start", { name, script: absScript, max_agents: maxAgents, concurrency });
332
+ const startedAt = new Date(t0).toISOString();
333
+ journal("run.start", {
334
+ name,
335
+ script: absScript,
336
+ objective: meta.objective ?? null,
337
+ acceptance: meta.acceptance,
338
+ max_agents: maxAgents,
339
+ concurrency,
340
+ });
246
341
  try {
247
342
  const result = await mod.default(ctx);
343
+ const endedAt = new Date().toISOString();
344
+ const durationMs = Date.now() - t0;
345
+ journal("run.end", {
346
+ ok: true,
347
+ agents: agentsSpawned,
348
+ cached: agentsCached,
349
+ cost_usd: round4(costUsd),
350
+ duration_ms: durationMs,
351
+ });
352
+ let proof;
353
+ try {
354
+ proof = buildWorkflowProof({
355
+ runId,
356
+ meta,
357
+ status: "succeeded",
358
+ startedAt,
359
+ endedAt,
360
+ durationMs,
361
+ journalPath,
362
+ before: repoBefore,
363
+ after: snapshotRepo(cwd),
364
+ agents: Array.from(agentProofs.values()),
365
+ evidence: evidenceRecords,
366
+ harnessEvidence: opts.harnessEvidence,
367
+ result,
368
+ });
369
+ writeWorkflowProof(proofPath, proof);
370
+ }
371
+ catch (error) {
372
+ throw new WorkflowRunError(`workflow completed but its proof packet could not be written: ${error.message}`, runId, proofPath, error);
373
+ }
248
374
  const report = {
249
375
  runId,
250
376
  name,
@@ -252,32 +378,53 @@ export async function runWorkflow(scriptPath, opts) {
252
378
  agentsSpawned,
253
379
  agentsCached,
254
380
  costUsd: round4(costUsd),
255
- durationMs: Date.now() - t0,
381
+ durationMs,
256
382
  journalPath,
383
+ proofPath,
384
+ acceptance: proof.acceptance.summary,
257
385
  contextTokensPerChildEstimate,
258
386
  billing: Array.from(billingProbed.values()).map((p) => ({
259
387
  harness: p.harness,
260
388
  mode: opts.subscriptionOnly ? "subscription" : p.mode,
261
389
  })),
262
390
  };
263
- journal("run.end", {
264
- ok: true,
265
- agents: agentsSpawned,
266
- cached: agentsCached,
267
- cost_usd: report.costUsd,
268
- duration_ms: report.durationMs,
269
- });
270
391
  return report;
271
392
  }
272
393
  catch (err) {
394
+ if (err instanceof WorkflowRunError)
395
+ throw err;
396
+ const endedAt = new Date().toISOString();
397
+ const durationMs = Date.now() - t0;
273
398
  journal("run.end", {
274
399
  ok: false,
275
400
  error: err.message,
276
401
  agents: agentsSpawned,
277
402
  cached: agentsCached,
278
403
  cost_usd: round4(costUsd),
404
+ duration_ms: durationMs,
279
405
  });
280
- throw err;
406
+ try {
407
+ const proof = buildWorkflowProof({
408
+ runId,
409
+ meta,
410
+ status: "failed",
411
+ startedAt,
412
+ endedAt,
413
+ durationMs,
414
+ journalPath,
415
+ before: repoBefore,
416
+ after: snapshotRepo(cwd),
417
+ agents: Array.from(agentProofs.values()),
418
+ evidence: evidenceRecords,
419
+ harnessEvidence: opts.harnessEvidence,
420
+ error: err.message,
421
+ });
422
+ writeWorkflowProof(proofPath, proof);
423
+ }
424
+ catch (proofError) {
425
+ throw new WorkflowRunError(`${err.message}; proof packet write also failed: ${proofError.message}`, runId, proofPath, err);
426
+ }
427
+ throw new WorkflowRunError(err.message, runId, proofPath, err);
281
428
  }
282
429
  }
283
430
  /** Stable identity for one agent() call, for the resume cache. The ORIGINAL
@@ -287,6 +434,7 @@ function agentCallKey(stage, harness, agentOpts, prompt) {
287
434
  stage,
288
435
  harness,
289
436
  agentOpts.model ?? null,
437
+ agentOpts.effort ?? null,
290
438
  agentOpts.maxTurns ?? DEFAULT_MAX_TURNS,
291
439
  agentOpts.schema ?? null,
292
440
  prompt,
@@ -7,11 +7,11 @@
7
7
  * but they do drift — keep this module the single place they live.
8
8
  */
9
9
  import type { HarnessName } from "./types.js";
10
- export declare const HARNESS_BINARIES: Record<HarnessName, string>;
11
- export declare const HARNESS_INSTALL_HINTS: Record<HarnessName, string>;
10
+ export declare const HARNESS_BINARIES: Record<string, string>;
11
+ export declare const HARNESS_INSTALL_HINTS: Record<string, string>;
12
12
  /** How to authenticate each CLI with a subscription login (the billing
13
13
  * default — see billing.ts). */
14
- export declare const HARNESS_LOGIN_HINTS: Record<HarnessName, string>;
14
+ export declare const HARNESS_LOGIN_HINTS: Record<string, string>;
15
15
  /** One-line "it's missing, here's the fix" string for spawn adapters. */
16
16
  export declare function notFoundError(harness: HarnessName): string;
17
17
  //# sourceMappingURL=harnesses.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"harnesses.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/harnesses.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAIxD,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAI7D,CAAC;AAEF;gCACgC;AAChC,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAI3D,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAK1D"}
1
+ {"version":3,"file":"harnesses.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/harnesses.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEnD,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAExD,CAAC;AAEF;gCACgC;AAChC,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEtD,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAK1D"}
@@ -6,25 +6,16 @@
6
6
  * Install commands are the vendors' official one-liners; they drift rarely
7
7
  * but they do drift — keep this module the single place they live.
8
8
  */
9
- export const HARNESS_BINARIES = {
10
- "claude-code": "claude",
11
- codex: "codex",
12
- cursor: "cursor-agent",
13
- };
14
- export const HARNESS_INSTALL_HINTS = {
15
- "claude-code": "npm install -g @anthropic-ai/claude-code",
16
- codex: "npm install -g @openai/codex",
17
- cursor: "curl https://cursor.com/install -fsS | bash",
18
- };
9
+ import { BUILTIN_HARNESS_PROFILES } from "../harnesses/profiles.js";
10
+ export const HARNESS_BINARIES = Object.fromEntries(Object.values(BUILTIN_HARNESS_PROFILES).map((profile) => [profile.id, profile.binary]));
11
+ export const HARNESS_INSTALL_HINTS = Object.fromEntries(Object.values(BUILTIN_HARNESS_PROFILES).map((profile) => [profile.id, profile.installHint]));
19
12
  /** How to authenticate each CLI with a subscription login (the billing
20
13
  * default — see billing.ts). */
21
- export const HARNESS_LOGIN_HINTS = {
22
- "claude-code": "run `claude` and use /login",
23
- codex: "codex login",
24
- cursor: "cursor-agent login",
25
- };
14
+ export const HARNESS_LOGIN_HINTS = Object.fromEntries(Object.values(BUILTIN_HARNESS_PROFILES).map((profile) => [profile.id, profile.loginHint]));
26
15
  /** One-line "it's missing, here's the fix" string for spawn adapters. */
27
16
  export function notFoundError(harness) {
28
- return (`${HARNESS_BINARIES[harness]} CLI not found on PATH; ` +
29
- `install: ${HARNESS_INSTALL_HINTS[harness]} then authenticate: ${HARNESS_LOGIN_HINTS[harness]}`);
17
+ const binary = HARNESS_BINARIES[harness] ?? harness;
18
+ const install = HARNESS_INSTALL_HINTS[harness] ?? "install the harness CLI";
19
+ const login = HARNESS_LOGIN_HINTS[harness] ?? "authenticate the harness CLI";
20
+ return `${binary} CLI not found on PATH; ` + `install: ${install} then authenticate: ${login}`;
30
21
  }
@@ -0,0 +1,5 @@
1
+ export { runWorkflow, WorkflowRunError } from "./engine.js";
2
+ export { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
3
+ export type { AcceptanceCriterion, AcceptanceResult, AcceptanceStatus, AcceptanceSummary, AgentOpts, EngineOpts, EvidenceKind, EvidenceSource, EvidenceStatus, HarnessEvidenceCapability, HarnessEvidenceCoverage, HarnessName, ResultDigest, RunReport, Spawner, SpawnRequest, SpawnResult, StageSchema, WorkflowAgentProof, WorkflowContext, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowModule, WorkflowProof, WorkflowProofUnknown, WorkflowRepoEvidence, WorkflowRepoSnapshot, WorkflowRunStatus, } from "./types.js";
4
+ export { WORKFLOW_PROOF_SCHEMA_VERSION } from "./types.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,yBAAyB,EACzB,uBAAuB,EACvB,WAAW,EACX,YAAY,EACZ,SAAS,EACT,OAAO,EACP,YAAY,EACZ,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { runWorkflow, WorkflowRunError } from "./engine.js";
2
+ export { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
3
+ export { WORKFLOW_PROOF_SCHEMA_VERSION } from "./types.js";
@@ -0,0 +1,42 @@
1
+ import type { RepoSnapshot } from "../context/index.js";
2
+ import type { AcceptanceCriterion, AcceptanceResult, AcceptanceSummary, HarnessEvidenceCapability, ResultDigest, WorkflowAgentProof, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowProof } from "./types.js";
3
+ export interface NormalizedWorkflowMeta {
4
+ name: string;
5
+ description?: string;
6
+ objective?: string;
7
+ acceptance: AcceptanceCriterion[];
8
+ }
9
+ export interface BuildWorkflowProofInput {
10
+ runId: string;
11
+ meta: NormalizedWorkflowMeta;
12
+ status: "succeeded" | "failed";
13
+ startedAt: string;
14
+ endedAt: string;
15
+ durationMs: number;
16
+ journalPath: string;
17
+ before: RepoSnapshot;
18
+ after: RepoSnapshot;
19
+ agents: WorkflowAgentProof[];
20
+ evidence: WorkflowEvidenceRecord[];
21
+ harnessEvidence?: Readonly<Record<string, HarnessEvidenceCapability | undefined>>;
22
+ result?: unknown;
23
+ error?: string;
24
+ }
25
+ export declare function normalizeWorkflowMeta(meta: WorkflowMeta | undefined, fallbackName: string): NormalizedWorkflowMeta;
26
+ export declare function createEvidenceRecord(input: {
27
+ value: WorkflowEvidenceInput;
28
+ sequence: number;
29
+ acceptanceIds: ReadonlySet<string>;
30
+ stage?: string;
31
+ recordedAt?: string;
32
+ }): WorkflowEvidenceRecord;
33
+ export declare function rollupAcceptance(criteria: AcceptanceCriterion[], evidence: WorkflowEvidenceRecord[]): {
34
+ criteria: AcceptanceResult[];
35
+ summary: AcceptanceSummary;
36
+ };
37
+ export declare function digestResult(value: unknown, kind?: "text" | "json"): ResultDigest;
38
+ export declare function buildWorkflowProof(input: BuildWorkflowProofInput): WorkflowProof;
39
+ export declare function writeWorkflowProof(path: string, proof: WorkflowProof): void;
40
+ export declare function readWorkflowProof(coordRoot: string, runId: string): WorkflowProof;
41
+ export declare function renderWorkflowProof(proof: WorkflowProof): string;
42
+ //# sourceMappingURL=proof.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/proof.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EAEzB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,aAAa,EAId,MAAM,YAAY,CAAC;AAepB,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC,CAAC;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,YAAY,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,GACnB,sBAAsB,CAqCxB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,KAAK,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,sBAAsB,CA6BzB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,QAAQ,EAAE,sBAAsB,EAAE,GACjC;IAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,CAuB9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,YAAY,CAQjF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,aAAa,CAwChF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAe3E;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAkBjF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CA8BhE"}