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
@@ -17,6 +17,8 @@
17
17
  */
18
18
 
19
19
  import { exec } from "../../lib/exec.ts";
20
+ import { validateHarnessEffort } from "../harnesses/profiles.ts";
21
+ import type { HarnessInvocation, HarnessRawResult } from "../harnesses/types.ts";
20
22
  import { buildChildEnv } from "./child-env.ts";
21
23
  import { notFoundError } from "./harnesses.ts";
22
24
  import type { Spawner, SpawnRequest, SpawnResult } from "./types.ts";
@@ -46,44 +48,61 @@ export function parseCursorOutput(stdout: string): {
46
48
  }
47
49
  }
48
50
 
49
- export const cursorSpawner: Spawner = async (req: SpawnRequest): Promise<SpawnResult> => {
50
- const t0 = Date.now();
51
-
51
+ export function buildCursorInvocation(req: SpawnRequest): HarnessInvocation {
52
+ validateHarnessEffort("cursor", req.effort);
52
53
  // --trust: headless cursor-agent refuses untrusted workspaces (exit 1,
53
54
  // "Workspace Trust Required"). Workflow children run in the engine's cwd
54
55
  // deliberately and may edit files — the same posture as the codex adapter's
55
56
  // `--sandbox workspace-write` — so trusting that directory is implied.
56
57
  const argv = ["cursor-agent", "-p", req.prompt, "--output-format", "json", "--trust"];
57
58
  if (req.model) argv.push("--model", req.model);
59
+ return { argv };
60
+ }
58
61
 
59
- const r = await exec(argv, {
60
- cwd: req.cwd,
61
- env: buildChildEnv(req.runId, { subscriptionOnly: req.subscriptionOnly }),
62
- timeout: req.timeoutMs,
63
- });
64
- const durationMs = Date.now() - t0;
65
-
66
- if (r.exitCode === 127) {
67
- return { ok: false, text: "", durationMs, error: notFoundError("cursor") };
62
+ export function normalizeCursorResult(raw: HarnessRawResult): SpawnResult {
63
+ if (raw.exitCode === 127) {
64
+ return { ok: false, text: "", durationMs: raw.durationMs, error: notFoundError("cursor") };
68
65
  }
69
- if (r.exitCode !== 0) {
66
+ if (raw.exitCode !== 0) {
70
67
  return {
71
68
  ok: false,
72
69
  text: "",
73
- durationMs,
74
- error: `cursor-agent exited ${r.exitCode}: ${(r.stderr || r.stdout).slice(0, 500)}`,
70
+ durationMs: raw.durationMs,
71
+ error: `cursor-agent exited ${raw.exitCode}: ${(raw.stderr || raw.stdout).slice(0, 500)}`,
75
72
  };
76
73
  }
77
74
 
78
- const parsed = parseCursorOutput(r.stdout);
75
+ const parsed = parseCursorOutput(raw.stdout);
79
76
  if (parsed.isError) {
80
77
  return {
81
78
  ok: false,
82
79
  text: parsed.text,
83
80
  sessionId: parsed.sessionId,
84
- durationMs,
81
+ durationMs: raw.durationMs,
85
82
  error: `cursor-agent reported is_error: ${parsed.text.slice(0, 300)}`,
86
83
  };
87
84
  }
88
- return { ok: true, text: parsed.text, sessionId: parsed.sessionId, durationMs };
85
+ return {
86
+ ok: true,
87
+ text: parsed.text,
88
+ sessionId: parsed.sessionId,
89
+ durationMs: raw.durationMs,
90
+ };
91
+ }
92
+
93
+ export const cursorSpawner: Spawner = async (req: SpawnRequest): Promise<SpawnResult> => {
94
+ const t0 = Date.now();
95
+ let invocation: HarnessInvocation;
96
+ try {
97
+ invocation = buildCursorInvocation(req);
98
+ } catch (error) {
99
+ return { ok: false, text: "", durationMs: 0, error: (error as Error).message };
100
+ }
101
+
102
+ const r = await exec(invocation.argv, {
103
+ cwd: req.cwd,
104
+ env: buildChildEnv(req.runId, { subscriptionOnly: req.subscriptionOnly }),
105
+ timeout: req.timeoutMs,
106
+ });
107
+ return normalizeCursorResult({ ...r, durationMs: Date.now() - t0 });
89
108
  };
@@ -9,6 +9,164 @@
9
9
 
10
10
  import type { BillingMode, BillingProber } from "./billing.ts";
11
11
 
12
+ export const WORKFLOW_PROOF_SCHEMA_VERSION = 1 as const;
13
+
14
+ export type EvidenceKind = "test" | "command" | "artifact" | "change" | "review" | "observation";
15
+ export type EvidenceStatus = "passed" | "failed" | "observed" | "unknown";
16
+ export type EvidenceSource = "workflow" | "engine";
17
+ export type AcceptanceStatus = "satisfied" | "unsatisfied" | "unknown";
18
+ export type WorkflowRunStatus = "succeeded" | "failed";
19
+
20
+ export interface AcceptanceCriterion {
21
+ /** Stable identifier referenced by evidence, for example `tests-pass`. */
22
+ id: string;
23
+ statement: string;
24
+ }
25
+
26
+ export interface WorkflowEvidenceInput {
27
+ kind: EvidenceKind;
28
+ status: EvidenceStatus;
29
+ label: string;
30
+ summary?: string;
31
+ /** Inspectable local path, URL, command name, or other bounded reference. */
32
+ ref?: string;
33
+ /** Declared acceptance criteria this evidence bears on. */
34
+ acceptanceIds?: string[];
35
+ }
36
+
37
+ export interface WorkflowEvidenceRecord {
38
+ id: string;
39
+ source: EvidenceSource;
40
+ recorded_at: string;
41
+ kind: EvidenceKind;
42
+ status: EvidenceStatus;
43
+ label: string;
44
+ summary?: string;
45
+ ref?: string;
46
+ stage?: string;
47
+ acceptance_ids: string[];
48
+ }
49
+
50
+ export interface AcceptanceResult extends AcceptanceCriterion {
51
+ status: AcceptanceStatus;
52
+ evidence_ids: string[];
53
+ /** Sources behind the decisive evidence. Empty when status is unknown. */
54
+ sources: EvidenceSource[];
55
+ }
56
+
57
+ export interface AcceptanceSummary {
58
+ satisfied: number;
59
+ unsatisfied: number;
60
+ unknown: number;
61
+ total: number;
62
+ }
63
+
64
+ export interface ResultDigest {
65
+ kind: "text" | "json";
66
+ sha256: string;
67
+ bytes: number;
68
+ }
69
+
70
+ export interface WorkflowAgentProof {
71
+ id: string;
72
+ label: string;
73
+ stage?: string;
74
+ harness: HarnessName;
75
+ model?: string;
76
+ status: "succeeded" | "failed" | "cached";
77
+ attempts: number;
78
+ duration_ms: number;
79
+ cost_usd?: number;
80
+ session_id?: string;
81
+ result?: ResultDigest;
82
+ error?: string;
83
+ }
84
+
85
+ export interface WorkflowRepoSnapshot {
86
+ cwd: string;
87
+ root?: string;
88
+ branch?: string;
89
+ head?: string;
90
+ dirty_paths: string[];
91
+ dirty_paths_truncated?: boolean;
92
+ }
93
+
94
+ export interface WorkflowRepoEvidence {
95
+ source: "engine";
96
+ before: WorkflowRepoSnapshot;
97
+ after: WorkflowRepoSnapshot;
98
+ drift: {
99
+ branch_changed: boolean;
100
+ head_changed: boolean;
101
+ dirty_paths_added: string[];
102
+ dirty_paths_cleared: string[];
103
+ dirty_paths_retained: string[];
104
+ /** True when snapshots cannot prove whether every retained dirty path changed. */
105
+ incomplete: boolean;
106
+ note?: string;
107
+ };
108
+ }
109
+
110
+ export interface HarnessEvidenceCoverage {
111
+ harness: HarnessName;
112
+ tool_evidence: {
113
+ support: "supported" | "partial" | "unsupported" | "unknown";
114
+ note?: string;
115
+ };
116
+ observed: {
117
+ final_results: number;
118
+ session_ids: number;
119
+ costs: number;
120
+ };
121
+ }
122
+
123
+ export interface WorkflowProofUnknown {
124
+ code:
125
+ | "tool_evidence_unavailable"
126
+ | "harness_capability_unregistered"
127
+ | "agent_cost_unreported"
128
+ | "agent_session_unreported"
129
+ | "repository_drift_incomplete";
130
+ message: string;
131
+ harness?: HarnessName;
132
+ agent_id?: string;
133
+ }
134
+
135
+ export interface WorkflowProof {
136
+ schema_version: typeof WORKFLOW_PROOF_SCHEMA_VERSION;
137
+ run: {
138
+ id: string;
139
+ name: string;
140
+ status: WorkflowRunStatus;
141
+ started_at: string;
142
+ ended_at: string;
143
+ duration_ms: number;
144
+ objective?: string;
145
+ error?: string;
146
+ result?: ResultDigest;
147
+ };
148
+ acceptance: {
149
+ criteria: AcceptanceResult[];
150
+ summary: AcceptanceSummary;
151
+ };
152
+ agents: WorkflowAgentProof[];
153
+ evidence: WorkflowEvidenceRecord[];
154
+ repository: WorkflowRepoEvidence;
155
+ harnesses: HarnessEvidenceCoverage[];
156
+ unknowns: WorkflowProofUnknown[];
157
+ integrity: {
158
+ journal: {
159
+ path: "journal.jsonl";
160
+ sha256: string;
161
+ bytes: number;
162
+ };
163
+ };
164
+ }
165
+
166
+ export interface HarnessEvidenceCapability {
167
+ toolEvidence: HarnessEvidenceCoverage["tool_evidence"];
168
+ }
169
+
12
170
  /** JSON-schema *subset* accepted by stage gates (see validate.ts). */
13
171
  export interface StageSchema {
14
172
  type: "object" | "array" | "string" | "number" | "boolean";
@@ -28,6 +186,9 @@ export interface AgentOpts {
28
186
  schema?: StageSchema;
29
187
  /** Model slug passed through to the harness CLI (default: the CLI's default). */
30
188
  model?: string;
189
+ /** Reasoning effort mapped through the selected harness profile. Unsupported
190
+ * values fail before the vendor process starts. */
191
+ effort?: string;
31
192
  /** Attempt ceiling for the schema-retry loop (default 2). */
32
193
  maxAttempts?: number;
33
194
  /** Subprocess timeout ms (default 300_000). */
@@ -43,7 +204,10 @@ export interface AgentOpts {
43
204
  harness?: HarnessName;
44
205
  }
45
206
 
46
- export type HarnessName = "claude-code" | "codex" | "cursor";
207
+ /** Open registry key. The built-in catalog currently contains Claude Code,
208
+ * Codex, and Cursor; consumers may register another adapter without widening
209
+ * a package-owned union first. */
210
+ export type HarnessName = string;
47
211
 
48
212
  /** What a spawn adapter returns for one subagent run. */
49
213
  export interface SpawnResult {
@@ -61,6 +225,7 @@ export interface SpawnResult {
61
225
  export interface SpawnRequest {
62
226
  prompt: string;
63
227
  model?: string;
228
+ effort?: string;
64
229
  timeoutMs: number;
65
230
  maxTurns: number;
66
231
  cwd: string;
@@ -87,11 +252,15 @@ export interface WorkflowContext {
87
252
  stage: (title: string) => void;
88
253
  /** Narrate progress (stderr + journal). */
89
254
  log: (message: string) => void;
255
+ /** Attach a bounded, sourced receipt to the run and optional acceptance criteria. */
256
+ evidence: (input: WorkflowEvidenceInput) => string;
90
257
  }
91
258
 
92
259
  export interface WorkflowMeta {
93
260
  name: string;
94
261
  description?: string;
262
+ objective?: string;
263
+ acceptance?: AcceptanceCriterion[];
95
264
  }
96
265
 
97
266
  /** Loaded script shape: `export const meta` + `export default async (ctx) => …`. */
@@ -105,7 +274,7 @@ export interface EngineOpts {
105
274
  coordRoot: string;
106
275
  /** Spawner registry keyed by harness. A single-harness caller registers one
107
276
  * entry and names it in `defaultHarness`. */
108
- spawners: Partial<Record<HarnessName, Spawner>>;
277
+ spawners: Readonly<Record<HarnessName, Spawner | undefined>>;
109
278
  /** Harness used when an agent() call doesn't name one (default "claude-code"). */
110
279
  defaultHarness?: HarnessName;
111
280
  /** Resume: run id of a prior run whose journal supplies cached results.
@@ -131,6 +300,9 @@ export interface EngineOpts {
131
300
  allowApiBilling?: boolean;
132
301
  /** Billing-probe override for tests (default: the real probeBilling). */
133
302
  probeBilling?: BillingProber;
303
+ /** Capability claims used to state whether adapter-native tool evidence was
304
+ * available. Missing claims remain unknown. */
305
+ harnessEvidence?: Readonly<Record<HarnessName, HarnessEvidenceCapability | undefined>>;
134
306
  }
135
307
 
136
308
  export interface RunReport {
@@ -144,6 +316,8 @@ export interface RunReport {
144
316
  costUsd: number;
145
317
  durationMs: number;
146
318
  journalPath: string;
319
+ proofPath: string;
320
+ acceptance: AcceptanceSummary;
147
321
  /** Estimated tokens of repo instructions (CLAUDE.md/AGENTS.md at the child
148
322
  * cwd) that EVERY child cache-writes on spawn — the fixed per-child context
149
323
  * overhead a fan-out multiplies. bytes/4 heuristic; 0 when no such file. */
@@ -74,6 +74,14 @@ export interface BrowserOptions {
74
74
  * via this callback (e.g., a Cloudflare-bypass header for specific zones).
75
75
  */
76
76
  extraHeaders?: (url: string) => Record<string, string>;
77
+ /**
78
+ * Extra Chromium command-line flags, passed through to Playwright's
79
+ * `launchPersistentContext` `args`. Used for environment-specific
80
+ * workarounds — most notably `--disable-gpu` for headed windows under
81
+ * WSLg (see `./launch-args.ts`). Empty/undefined means Playwright's
82
+ * defaults only.
83
+ */
84
+ launchArgs?: string[];
77
85
  }
78
86
 
79
87
  export interface NavigateResult {
@@ -98,6 +106,14 @@ export interface FailedRequest {
98
106
  method: string;
99
107
  failure: string;
100
108
  resourceType: string;
109
+ /** HTTP status for kind "http" entries; null for network-level failures. */
110
+ status: number | null;
111
+ /** "http" = request completed with a >=400 response; "network" = never completed (DNS, TLS, aborts, tunnel). */
112
+ kind: "http" | "network";
113
+ /** True when the entry is the main frame's document response — lets consumers
114
+ * distinguish an expected error-page status (a 404 route under test) from a
115
+ * broken subresource. */
116
+ document?: boolean;
101
117
  }
102
118
 
103
119
  export interface Diagnostics {
@@ -137,6 +153,9 @@ export class Browser {
137
153
  this.context = await chromium.launchPersistentContext(this.profileDir, {
138
154
  headless: !this.opts.headed,
139
155
  viewport: this.opts.viewport ?? { width: 1280, height: 800 },
156
+ ...(this.opts.launchArgs && this.opts.launchArgs.length > 0
157
+ ? { args: this.opts.launchArgs }
158
+ : {}),
140
159
  ...(this.opts.recordHarPath
141
160
  ? { recordHar: { path: this.opts.recordHarPath, mode: "full" as const } }
142
161
  : {}),
@@ -192,6 +211,24 @@ export class Browser {
192
211
  method: req.method(),
193
212
  failure: req.failure()?.errorText ?? "unknown",
194
213
  resourceType: req.resourceType(),
214
+ status: null,
215
+ kind: "network",
216
+ });
217
+ });
218
+ // HTTP-level failures: `requestfailed` only fires for requests that never
219
+ // complete (DNS, TLS, aborts), so a script/stylesheet answered with a
220
+ // 4xx/5xx would otherwise be invisible to failedRequests-based gates.
221
+ page.on("response", (res) => {
222
+ if (res.status() < 400) return;
223
+ const req = res.request();
224
+ this.failedRequests.push({
225
+ url: res.url(),
226
+ method: req.method(),
227
+ failure: `HTTP ${res.status()}`,
228
+ resourceType: req.resourceType(),
229
+ status: res.status(),
230
+ kind: "http",
231
+ document: req.resourceType() === "document" && res.frame() === page.mainFrame(),
195
232
  });
196
233
  });
197
234
  }
@@ -12,6 +12,7 @@ export {
12
12
  type DevOverlayError,
13
13
  type DevOverlayResult,
14
14
  } from "./dev-overlay.js";
15
+ export { isWSL, wslHeadedLaunchArgs } from "./launch-args.js";
15
16
  export type {
16
17
  OverflowElement,
17
18
  OverflowResult,
@@ -0,0 +1,33 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ /**
4
+ * Chromium launch-arg helpers for environment-specific workarounds.
5
+ *
6
+ * The one that matters today is WSLg: a headed Chromium window renders its
7
+ * page to a GPU-backed surface that WSLg composites over an RDP stream to the
8
+ * Windows host. On a range of Windows / GPU-driver / WSLg combinations that
9
+ * GPU-composited surface never presents, so the window shows in the taskbar
10
+ * but paints blank — even though the page itself runs fine (JS, navigation,
11
+ * and DOM all work). Forcing Chromium onto its software compositor with
12
+ * `--disable-gpu` restores on-screen paint. It only matters for headed mode
13
+ * (headless never composites to a display) and only under WSL.
14
+ */
15
+
16
+ /** True when running under WSL (WSL1 or WSL2). */
17
+ export function isWSL(): boolean {
18
+ if (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) return true;
19
+ try {
20
+ return /microsoft|wsl/i.test(readFileSync("/proc/version", "utf8"));
21
+ } catch {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Chromium launch flags to make a headed window paint under WSLg. Returns
28
+ * `["--disable-gpu"]` on WSL, `[]` elsewhere. Callers apply these only for
29
+ * headed launches. See the module doc for the failure mode.
30
+ */
31
+ export function wslHeadedLaunchArgs(): string[] {
32
+ return isWSL() ? ["--disable-gpu"] : [];
33
+ }