harnery 0.12.1 → 0.13.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 (34) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/workflow.d.ts.map +1 -1
  3. package/dist/commands/workflow.js +193 -0
  4. package/dist/core/agents/coord-client.d.ts +8 -0
  5. package/dist/core/agents/coord-client.d.ts.map +1 -1
  6. package/dist/core/agents/coord-client.js +18 -2
  7. package/dist/core/policy/types.d.ts +1 -1
  8. package/dist/core/policy/types.d.ts.map +1 -1
  9. package/dist/core/scratch/index.d.ts.map +1 -1
  10. package/dist/core/scratch/index.js +3 -3
  11. package/dist/core/workflow/approvals.d.ts +71 -0
  12. package/dist/core/workflow/approvals.d.ts.map +1 -0
  13. package/dist/core/workflow/approvals.js +288 -0
  14. package/dist/core/workflow/engine.d.ts +6 -0
  15. package/dist/core/workflow/engine.d.ts.map +1 -1
  16. package/dist/core/workflow/engine.js +224 -43
  17. package/dist/core/workflow/index.d.ts +6 -2
  18. package/dist/core/workflow/index.d.ts.map +1 -1
  19. package/dist/core/workflow/index.js +3 -1
  20. package/dist/core/workflow/run-state.d.ts +43 -0
  21. package/dist/core/workflow/run-state.d.ts.map +1 -0
  22. package/dist/core/workflow/run-state.js +231 -0
  23. package/dist/core/workflow/types.d.ts +9 -0
  24. package/dist/core/workflow/types.d.ts.map +1 -1
  25. package/package.json +1 -1
  26. package/src/commands/workflow.ts +226 -0
  27. package/src/core/agents/coord-client.ts +19 -2
  28. package/src/core/policy/types.ts +1 -1
  29. package/src/core/scratch/index.ts +3 -2
  30. package/src/core/workflow/approvals.ts +393 -0
  31. package/src/core/workflow/engine.ts +268 -44
  32. package/src/core/workflow/index.ts +31 -1
  33. package/src/core/workflow/run-state.ts +287 -0
  34. package/src/core/workflow/types.ts +9 -0
package/README.md CHANGED
@@ -21,7 +21,7 @@
21
21
 
22
22
  - **Multi-agent coordination:** per-agent heartbeats in `.harnery/active/`, claim-time and commit-time guards, the canonical event stream, councils, a decision docket, per-agent scratchpads, and harness adapters for Claude Code / Cursor / Codex.
23
23
  - **Cross-machine presence:** sessions on every machine that shares a repo see each other, labeled by machine. Zero-config over the repo's own git remote (presence refs), with an optional live-socket upgrade via an end-to-end-encrypted relay — the public `relay.harnery.com`, or self-host with `harn relay serve` / the bundled Cloudflare Worker (`relay/worker/`). The relay sees only ciphertext in opaquely-named rooms.
24
- - **Coordination-aware workflows:** `harn workflow run <script>` executes bounded, schema-gated multi-subagent workflow scripts — plain JS stages fanning out to headless harness-CLI subagents that are born coordination-registered (heartbeats, events, claim guards), with deterministic code deciding the routing between stages. An optional host-owned policy gates dispatch and declared external mutations with fail-closed `ALLOW`, `ASK`, and `DENY` decisions recorded in the run proof.
24
+ - **Coordination-aware workflows:** `harn workflow run <script>` executes bounded, schema-gated multi-subagent workflow scripts — plain JS stages fanning out to headless harness-CLI subagents that are born coordination-registered (heartbeats, events, claim guards), with deterministic code deciding the routing between stages. An optional host-owned policy gates dispatch and declared external mutations with `ALLOW`, `ASK`, and `DENY`; CLI `ASK` requests park durably for an explicit approve/deny and same-run resume, while library callers remain fail-closed by default.
25
25
  - **Standalone web UI:** `harn web up` boots a local Next.js dashboard for the coord layer, councils, per-project state, and a local coding-agent status view (Claude Code / Codex / Cursor usage, backed by `harn devtools`). Ships with the git clone, not the npm package (see [Install](#install)).
26
26
  - **Backup + sync:** `harn backup` snapshots `.harnery/` via [restic](https://restic.net/); `harn sync` keeps a curated subset live across machines via [rclone](https://rclone.org/) (Google Drive or any rclone remote).
27
27
 
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAkCnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAuKjF"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/commands/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAgDnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA2XjF"}
@@ -2,6 +2,7 @@ import { workflowSubscriptionOnly } from "../core/config.js";
2
2
  import { createBuiltinHarnessRegistry } from "../core/harnesses/index.js";
3
3
  import { findCoordRoot } from "../core/hooks/resolve/coord-root.js";
4
4
  import { loadPolicyFile } from "../core/policy/index.js";
5
+ import { WorkflowParkedError } from "../core/workflow/engine.js";
5
6
  export function registerWorkflowCommand(program, emit) {
6
7
  const registry = createBuiltinHarnessRegistry();
7
8
  const harnesses = registry.ids();
@@ -23,6 +24,7 @@ export function registerWorkflowCommand(program, emit) {
23
24
  "without this the engine refuses that silent-override state")
24
25
  .option("--policy <file>", "Host policy JSON/JSONC applied before dispatch and external mutation")
25
26
  .option("--isolation <mode>", "Host-created execution boundary: shared | worktree | sandbox | remote (default shared)")
27
+ .option("--approval-to <address>", "Address durable ASK requests (default: operator)")
26
28
  .option("--json", "Emit the full RunReport as JSON")
27
29
  .action(async (script, opts) => {
28
30
  const coordRoot = findCoordRoot();
@@ -77,6 +79,8 @@ export function registerWorkflowCommand(program, emit) {
77
79
  { toolEvidence: adapter.profile.capabilities.toolEvidence },
78
80
  ])),
79
81
  policy: opts.policy ? loadPolicyFile(opts.policy) : undefined,
82
+ approvalMode: "park",
83
+ approvalAddressee: opts.approvalTo,
80
84
  isolation: opts.isolation,
81
85
  // CLI harness subprocesses inherit the host network. A policy that
82
86
  // forbids network must therefore deny dispatch unless a future host
@@ -101,6 +105,23 @@ export function registerWorkflowCommand(program, emit) {
101
105
  `result: ${typeof report.result === "string" ? report.result : JSON.stringify(report.result, null, 2)}\n`);
102
106
  }
103
107
  catch (err) {
108
+ if (err instanceof WorkflowParkedError) {
109
+ if (opts.json) {
110
+ emit.config({ format: "json" });
111
+ emit.data({
112
+ status: "parked",
113
+ runId: err.runId,
114
+ approvalId: err.approvalId,
115
+ journalPath: err.journalPath,
116
+ });
117
+ }
118
+ else {
119
+ emit.text(`run ${err.runId} parked\napproval: ${err.approvalId}\n` +
120
+ `journal: ${err.journalPath}\n` +
121
+ `resume after resolution: harn workflow resume ${err.runId}\n`);
122
+ }
123
+ return;
124
+ }
104
125
  const proofPath = typeof err === "object" && err !== null && "proofPath" in err
105
126
  ? String(err.proofPath)
106
127
  : undefined;
@@ -111,6 +132,178 @@ export function registerWorkflowCommand(program, emit) {
111
132
  process.exit(1);
112
133
  }
113
134
  });
135
+ workflow
136
+ .command("resume <run-id>")
137
+ .description("Resume a parked workflow after its durable approval has been resolved.")
138
+ .option("--json", "Emit the full RunReport or parked result as JSON")
139
+ .action(async (runId, opts) => {
140
+ const coordRoot = findCoordRoot();
141
+ if (!coordRoot) {
142
+ emit.error({
143
+ code: "no_coord_root",
144
+ message: "no .harnery/ coordination root found; run `init` first",
145
+ });
146
+ process.exit(1);
147
+ }
148
+ try {
149
+ const { assertWorkflowRunResumable, runWorkflow } = await import("../core/workflow/index.js");
150
+ const { manifest } = assertWorkflowRunResumable(coordRoot, runId);
151
+ const report = await runWorkflow(manifest.script.path, {
152
+ coordRoot,
153
+ spawners: registry.spawners(),
154
+ resumeRunId: runId,
155
+ harnessEvidence: Object.fromEntries(registry
156
+ .list()
157
+ .map((adapter) => [
158
+ adapter.profile.id,
159
+ { toolEvidence: adapter.profile.capabilities.toolEvidence },
160
+ ])),
161
+ });
162
+ if (opts.json) {
163
+ emit.config({ format: "json" });
164
+ emit.data(report);
165
+ return;
166
+ }
167
+ const cachedPart = report.agentsCached > 0 ? ` (+${report.agentsCached} cached)` : "";
168
+ emit.text(`run ${report.runId} (${report.name}) finished: ${report.agentsSpawned} agent(s)${cachedPart}, ` +
169
+ `$${report.costUsd.toFixed(4)}, ${Math.round(report.durationMs / 1000)}s\n` +
170
+ `acceptance: ${report.acceptance.satisfied} satisfied, ${report.acceptance.unsatisfied} unsatisfied, ` +
171
+ `${report.acceptance.unknown} unknown\n` +
172
+ `journal: ${report.journalPath}\nproof: ${report.proofPath}\n` +
173
+ `result: ${typeof report.result === "string" ? report.result : JSON.stringify(report.result, null, 2)}\n`);
174
+ }
175
+ catch (err) {
176
+ if (err instanceof WorkflowParkedError) {
177
+ if (opts.json) {
178
+ emit.config({ format: "json" });
179
+ emit.data({
180
+ status: "parked",
181
+ runId: err.runId,
182
+ approvalId: err.approvalId,
183
+ journalPath: err.journalPath,
184
+ });
185
+ }
186
+ else {
187
+ emit.text(`run ${err.runId} parked again\napproval: ${err.approvalId}\n` +
188
+ `journal: ${err.journalPath}\n`);
189
+ }
190
+ return;
191
+ }
192
+ const proofPath = typeof err === "object" && err !== null && "proofPath" in err
193
+ ? String(err.proofPath)
194
+ : undefined;
195
+ emit.error({
196
+ code: "workflow_resume_failed",
197
+ message: `${err.message}${proofPath ? `\nproof: ${proofPath}` : ""}`,
198
+ });
199
+ process.exit(1);
200
+ }
201
+ });
202
+ const approvals = workflow
203
+ .command("approvals")
204
+ .description("Inspect and resolve durable workflow policy approvals.");
205
+ approvals
206
+ .command("list")
207
+ .description("List durable workflow approvals.")
208
+ .option("--status <status>", "Filter: pending | approved | denied")
209
+ .option("--json", "Emit approval records as JSON")
210
+ .action(async (opts) => {
211
+ const coordRoot = findCoordRoot();
212
+ if (!coordRoot) {
213
+ emit.error({ code: "no_coord_root", message: "no .harnery/ coordination root found" });
214
+ process.exit(1);
215
+ }
216
+ if (opts.status && !["pending", "approved", "denied"].includes(opts.status)) {
217
+ emit.error({
218
+ code: "bad_approval_status",
219
+ message: `unknown approval status ${JSON.stringify(opts.status)}`,
220
+ });
221
+ process.exit(1);
222
+ }
223
+ const { listWorkflowApprovals } = await import("../core/workflow/approvals.js");
224
+ const records = listWorkflowApprovals(coordRoot, { status: opts.status });
225
+ if (opts.json) {
226
+ emit.config({ format: "json" });
227
+ emit.data(records);
228
+ return;
229
+ }
230
+ if (records.length === 0) {
231
+ emit.text("no workflow approvals\n");
232
+ return;
233
+ }
234
+ emit.text(`${records
235
+ .map(({ request, status }) => `${request.id} ${status.padEnd(8)} ${request.run_id} ${request.request.phase} ${request.request.action} -> ${request.addressed_to}`)
236
+ .join("\n")}\n`);
237
+ });
238
+ approvals
239
+ .command("show <approval-id>")
240
+ .description("Show one durable workflow approval.")
241
+ .option("--json", "Emit the approval record as JSON")
242
+ .action(async (approvalId, opts) => {
243
+ const coordRoot = findCoordRoot();
244
+ if (!coordRoot) {
245
+ emit.error({ code: "no_coord_root", message: "no .harnery/ coordination root found" });
246
+ process.exit(1);
247
+ }
248
+ try {
249
+ const { readWorkflowApproval, renderWorkflowApproval } = await import("../core/workflow/approvals.js");
250
+ const approval = readWorkflowApproval(coordRoot, approvalId);
251
+ if (opts.json) {
252
+ emit.config({ format: "json" });
253
+ emit.data(approval);
254
+ }
255
+ else {
256
+ emit.text(renderWorkflowApproval(approval));
257
+ }
258
+ }
259
+ catch (err) {
260
+ emit.error({ code: "workflow_approval_read_failed", message: err.message });
261
+ process.exit(1);
262
+ }
263
+ });
264
+ const registerApprovalDecision = (command, verdict) => {
265
+ approvals
266
+ .command(`${command} <approval-id>`)
267
+ .description(`${command === "approve" ? "Approve" : "Deny"} a pending workflow approval.`)
268
+ .option("--actor <name>", "Decision actor recorded in the receipt")
269
+ .option("--reason <text>", "Bounded decision reason")
270
+ .option("--json", "Emit the resolved approval as JSON")
271
+ .action(async (approvalId, opts) => {
272
+ const coordRoot = findCoordRoot();
273
+ if (!coordRoot) {
274
+ emit.error({ code: "no_coord_root", message: "no .harnery/ coordination root found" });
275
+ process.exit(1);
276
+ }
277
+ try {
278
+ const { resolveWorkflowApproval, renderWorkflowApproval } = await import("../core/workflow/approvals.js");
279
+ const resolved = resolveWorkflowApproval({
280
+ coordRoot,
281
+ approvalId,
282
+ verdict,
283
+ actor: opts.actor ?? process.env.USER ?? process.env.USERNAME ?? "operator",
284
+ reason: opts.reason,
285
+ });
286
+ if (opts.json) {
287
+ emit.config({ format: "json" });
288
+ emit.data(resolved);
289
+ }
290
+ else {
291
+ emit.text(`${renderWorkflowApproval(resolved.approval)}` +
292
+ `${resolved.applied ? "decision recorded" : "decision already recorded"}\n` +
293
+ `resume: harn workflow resume ${resolved.approval.request.run_id}\n`);
294
+ }
295
+ }
296
+ catch (err) {
297
+ emit.error({
298
+ code: "workflow_approval_resolution_failed",
299
+ message: err.message,
300
+ });
301
+ process.exit(1);
302
+ }
303
+ });
304
+ };
305
+ registerApprovalDecision("approve", "allow");
306
+ registerApprovalDecision("deny", "deny");
114
307
  workflow
115
308
  .command("proof <run-id>")
116
309
  .description("Show the bounded proof packet for a completed workflow run.")
@@ -13,6 +13,14 @@
13
13
  */
14
14
  export declare function isSafeInstanceId(value: unknown): value is string;
15
15
  export declare function assertSafeInstanceId(value: unknown): asserts value is string;
16
+ /**
17
+ * Resolve one direct-child filename beneath a trusted coordination directory.
18
+ *
19
+ * This is a second boundary behind the instance-ID allowlist. Normalizing the
20
+ * candidate and proving its directory prefix prevents traversal even if a new
21
+ * caller constructs a filename from a different untrusted source.
22
+ */
23
+ export declare function resolveContainedFile(directory: string, fileName: string): string;
16
24
  export interface Heartbeat {
17
25
  instance_id: string;
18
26
  name?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0IAA0I;IAC1I,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CA0C5C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAkEA;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2BpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAKlE;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAIxC"}
1
+ {"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOhF;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0IAA0I;IAC1I,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CA0C5C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAkEA;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2BpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAMlE;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAIxC"}
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { spawnSync } from "node:child_process";
10
10
  import { existsSync, readdirSync, readFileSync } from "node:fs";
11
- import { resolve } from "node:path";
11
+ import { dirname, resolve, sep } from "node:path";
12
12
  // NOTE: kept dependency-free (node builtins only); this file is vendored verbatim into
13
13
  // a downstream consumer, so it cannot import the coordEnv helper.
14
14
  const MAX_INSTANCE_ID_LENGTH = 128;
@@ -35,6 +35,21 @@ export function assertSafeInstanceId(value) {
35
35
  throw new Error("instance_id must be 1-128 ASCII letters, digits, hyphens, or underscores");
36
36
  }
37
37
  }
38
+ /**
39
+ * Resolve one direct-child filename beneath a trusted coordination directory.
40
+ *
41
+ * This is a second boundary behind the instance-ID allowlist. Normalizing the
42
+ * candidate and proving its directory prefix prevents traversal even if a new
43
+ * caller constructs a filename from a different untrusted source.
44
+ */
45
+ export function resolveContainedFile(directory, fileName) {
46
+ const root = resolve(directory);
47
+ const candidate = resolve(root, fileName);
48
+ if (!candidate.startsWith(`${root}${sep}`) || dirname(candidate) !== root) {
49
+ throw new Error("coordination filename must resolve directly beneath its root");
50
+ }
51
+ return candidate;
52
+ }
38
53
  /**
39
54
  * Resolve the monorepo root for coord-state purposes.
40
55
  *
@@ -334,7 +349,8 @@ export function readHeartbeat(instanceId) {
334
349
  const root = monorepoRoot();
335
350
  if (!root)
336
351
  return null;
337
- return readJsonHeartbeatFile(resolve(root, ".harnery", "active", `${instanceId}.json`));
352
+ const path = resolveContainedFile(resolve(root, ".harnery", "active"), `${instanceId}.json`);
353
+ return readJsonHeartbeatFile(path);
338
354
  }
339
355
  function readJsonHeartbeatFile(path) {
340
356
  if (!existsSync(path))
@@ -73,7 +73,7 @@ export interface PolicyDecision {
73
73
  phase: PolicyPhase;
74
74
  initial_verdict: PolicyVerdict;
75
75
  verdict: "allow" | "deny";
76
- resolved_by: "policy" | "host" | "fail_closed";
76
+ resolved_by: "policy" | "host" | "approval" | "fail_closed";
77
77
  reason: string;
78
78
  rule_codes: string[];
79
79
  request: PolicyRequestSummary;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/policy/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/policy/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/scratch/index.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,kBAAkB,iFAQrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,QACyF,CAAC;AAEtH,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,UAAU,IAAI,MAAM,CAMnC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtD;AAeD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAW1D;AAID,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAwEtE;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAkBxD;AAmBD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,SAAoB,GAAG,SAAS,EAAE,CA2DrF;AAID,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,GACX,UAAU,CAgEZ;AAID,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,IAAI,SAAyB,GAAG,MAAM,CAkBnE;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAajD;AAED,qDAAqD;AACrD,wBAAgB,YAAY,IAAI;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,CAWF;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAIjE;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoB9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/scratch/index.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,kBAAkB,iFAQrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,QACyF,CAAC;AAEtH,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,UAAU,IAAI,MAAM,CAMnC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtD;AAeD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAW1D;AAID,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAwEtE;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAkBxD;AAmBD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,SAAoB,GAAG,SAAS,EAAE,CA2DrF;AAID,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,GACX,UAAU,CAgEZ;AAID,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,IAAI,SAAyB,GAAG,MAAM,CAkBnE;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAajD;AAED,qDAAqD;AACrD,wBAAgB,YAAY,IAAI;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,CAWF;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAIjE;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoB9D"}
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import { resolveMachineLabel } from "../../lib/machine.js";
4
- import { assertSafeInstanceId, monorepoRoot, readHeartbeat, resolveOwner, } from "../agents/index.js";
4
+ import { assertSafeInstanceId, monorepoRoot, readHeartbeat, resolveContainedFile, resolveOwner, } from "../agents/index.js";
5
5
  /**
6
6
  * Agent scratchpad: per-agent markdown journal at `.harnery/scratch/<instance_id>.md`.
7
7
  *
@@ -47,7 +47,7 @@ export function archiveDir() {
47
47
  }
48
48
  export function scratchPath(instanceId) {
49
49
  assertSafeInstanceId(instanceId);
50
- return resolve(scratchDir(), `${instanceId}.md`);
50
+ return resolveContainedFile(scratchDir(), `${instanceId}.md`);
51
51
  }
52
52
  // ─── Chicago time formatting ──────────────────────────────────────────────
53
53
  const CHICAGO_FORMATTER = new Intl.DateTimeFormat("en-US", {
@@ -307,7 +307,7 @@ export function archiveScratch(instanceId) {
307
307
  if (!existsSync(src))
308
308
  return null;
309
309
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
310
- const dest = resolve(archiveDir(), `${instanceId}-${ts}.md`);
310
+ const dest = resolveContainedFile(archiveDir(), `${instanceId}-${ts}.md`);
311
311
  renameSync(src, dest);
312
312
  return dest;
313
313
  }
@@ -0,0 +1,71 @@
1
+ import type { PolicyEvaluation, PolicyRequestSummary, PolicyVerdict } from "../policy/index.js";
2
+ export declare const WORKFLOW_APPROVAL_SCHEMA_VERSION: 1;
3
+ export type WorkflowApprovalStatus = "pending" | "approved" | "denied";
4
+ export interface WorkflowApprovalRequest {
5
+ schema_version: typeof WORKFLOW_APPROVAL_SCHEMA_VERSION;
6
+ id: string;
7
+ run_id: string;
8
+ decision_id: string;
9
+ requested_at: string;
10
+ addressed_to: string;
11
+ policy: {
12
+ name: string;
13
+ sha256: string;
14
+ };
15
+ initial_verdict: Extract<PolicyVerdict, "ask">;
16
+ request: PolicyRequestSummary;
17
+ evaluation: PolicyEvaluation;
18
+ request_sha256: string;
19
+ }
20
+ export interface WorkflowApprovalDecision {
21
+ schema_version: typeof WORKFLOW_APPROVAL_SCHEMA_VERSION;
22
+ approval_id: string;
23
+ run_id: string;
24
+ verdict: "allow" | "deny";
25
+ actor: string;
26
+ reason?: string;
27
+ decided_at: string;
28
+ }
29
+ export interface WorkflowApproval {
30
+ request: WorkflowApprovalRequest;
31
+ decision?: WorkflowApprovalDecision;
32
+ status: WorkflowApprovalStatus;
33
+ }
34
+ export interface CreateWorkflowApprovalInput {
35
+ coordRoot: string;
36
+ runId: string;
37
+ decisionId: string;
38
+ addressedTo?: string;
39
+ policy: {
40
+ name: string;
41
+ sha256: string;
42
+ };
43
+ request: PolicyRequestSummary;
44
+ evaluation: PolicyEvaluation;
45
+ now?: () => string;
46
+ }
47
+ export interface ResolveWorkflowApprovalInput {
48
+ coordRoot: string;
49
+ approvalId: string;
50
+ verdict: "allow" | "deny";
51
+ actor: string;
52
+ reason?: string;
53
+ now?: () => string;
54
+ }
55
+ export declare function workflowApprovalId(runId: string, decisionId: string): string;
56
+ export declare function createWorkflowApproval(input: CreateWorkflowApprovalInput): {
57
+ approval: WorkflowApproval;
58
+ created: boolean;
59
+ };
60
+ export declare function resolveWorkflowApproval(input: ResolveWorkflowApprovalInput): {
61
+ approval: WorkflowApproval;
62
+ applied: boolean;
63
+ };
64
+ export declare function readWorkflowApproval(coordRoot: string, approvalId: string): WorkflowApproval;
65
+ export declare function listWorkflowApprovals(coordRoot: string, options?: {
66
+ status?: WorkflowApprovalStatus;
67
+ runId?: string;
68
+ }): WorkflowApproval[];
69
+ export declare function renderWorkflowApproval(approval: WorkflowApproval): string;
70
+ export declare function assertWorkflowRunId(runId: string): void;
71
+ //# sourceMappingURL=approvals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/approvals.ts"],"names":[],"mappings":"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"}