harnery 0.26.0 → 0.28.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 (116) hide show
  1. package/dist/commands/browse.d.ts.map +1 -1
  2. package/dist/commands/browse.js +27 -1
  3. package/dist/commands/harness.d.ts.map +1 -1
  4. package/dist/commands/harness.js +2 -0
  5. package/dist/commands/work.d.ts +2 -0
  6. package/dist/commands/work.d.ts.map +1 -1
  7. package/dist/commands/work.js +47 -4
  8. package/dist/commands/workflow.d.ts.map +1 -1
  9. package/dist/commands/workflow.js +64 -0
  10. package/dist/core/harnesses/attest-projection.d.ts +44 -0
  11. package/dist/core/harnesses/attest-projection.d.ts.map +1 -0
  12. package/dist/core/harnesses/attest-projection.js +113 -0
  13. package/dist/core/harnesses/attest.d.ts +9 -0
  14. package/dist/core/harnesses/attest.d.ts.map +1 -1
  15. package/dist/core/harnesses/attest.js +14 -1
  16. package/dist/core/harnesses/attestation.d.ts +1 -1
  17. package/dist/core/harnesses/attestation.d.ts.map +1 -1
  18. package/dist/core/harnesses/attestation.js +7 -1
  19. package/dist/core/harnesses/bench.d.ts.map +1 -1
  20. package/dist/core/harnesses/bench.js +13 -0
  21. package/dist/core/harnesses/profiles.d.ts +7 -0
  22. package/dist/core/harnesses/profiles.d.ts.map +1 -1
  23. package/dist/core/harnesses/profiles.js +9 -0
  24. package/dist/core/harnesses/types.d.ts +19 -1
  25. package/dist/core/harnesses/types.d.ts.map +1 -1
  26. package/dist/core/harnesses/types.js +1 -0
  27. package/dist/core/work/index.d.ts +1 -1
  28. package/dist/core/work/index.d.ts.map +1 -1
  29. package/dist/core/work/index.js +1 -1
  30. package/dist/core/work/runner.d.ts.map +1 -1
  31. package/dist/core/work/runner.js +8 -1
  32. package/dist/core/work/state.d.ts +24 -1
  33. package/dist/core/work/state.d.ts.map +1 -1
  34. package/dist/core/work/state.js +108 -14
  35. package/dist/core/workflow/approvals.d.ts.map +1 -1
  36. package/dist/core/workflow/approvals.js +3 -6
  37. package/dist/core/workflow/attempt-context.d.ts.map +1 -1
  38. package/dist/core/workflow/attempt-context.js +38 -1
  39. package/dist/core/workflow/engine.d.ts.map +1 -1
  40. package/dist/core/workflow/engine.js +61 -19
  41. package/dist/core/workflow/index.d.ts +1 -1
  42. package/dist/core/workflow/index.d.ts.map +1 -1
  43. package/dist/core/workflow/index.js +1 -1
  44. package/dist/core/workflow/journal.d.ts +30 -0
  45. package/dist/core/workflow/journal.d.ts.map +1 -0
  46. package/dist/core/workflow/journal.js +70 -0
  47. package/dist/core/workflow/proof.d.ts +3 -1
  48. package/dist/core/workflow/proof.d.ts.map +1 -1
  49. package/dist/core/workflow/proof.js +1 -0
  50. package/dist/core/workflow/sandbox-projection.d.ts +57 -0
  51. package/dist/core/workflow/sandbox-projection.d.ts.map +1 -0
  52. package/dist/core/workflow/sandbox-projection.js +95 -0
  53. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  54. package/dist/core/workflow/spawn-claude.js +14 -1
  55. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  56. package/dist/core/workflow/spawn-codex.js +18 -2
  57. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  58. package/dist/core/workflow/spawn-cursor.js +14 -1
  59. package/dist/core/workflow/types.d.ts +62 -0
  60. package/dist/core/workflow/types.d.ts.map +1 -1
  61. package/dist/core/workflow/workspaces/index.d.ts +2 -0
  62. package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
  63. package/dist/core/workflow/workspaces/index.js +1 -0
  64. package/dist/core/workflow/workspaces/inspect.d.ts.map +1 -1
  65. package/dist/core/workflow/workspaces/inspect.js +14 -0
  66. package/dist/core/workflow/workspaces/reclaim.d.ts +50 -0
  67. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -0
  68. package/dist/core/workflow/workspaces/reclaim.js +88 -0
  69. package/dist/core/workflow/workspaces/state.d.ts +1 -1
  70. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  71. package/dist/core/workflow/workspaces/state.js +9 -30
  72. package/dist/lib/browser/client.d.ts +50 -0
  73. package/dist/lib/browser/client.d.ts.map +1 -1
  74. package/dist/lib/browser/client.js +103 -0
  75. package/dist/lib/exec.d.ts +4 -0
  76. package/dist/lib/exec.d.ts.map +1 -1
  77. package/dist/lib/exec.js +6 -1
  78. package/dist/lib/tunnel/error-page.d.ts +15 -0
  79. package/dist/lib/tunnel/error-page.d.ts.map +1 -0
  80. package/dist/lib/tunnel/error-page.js +98 -0
  81. package/dist/lib/tunnel/gate.d.ts +1 -12
  82. package/dist/lib/tunnel/gate.d.ts.map +1 -1
  83. package/dist/lib/tunnel/gate.js +59 -4
  84. package/package.json +1 -1
  85. package/src/commands/browse.ts +33 -1
  86. package/src/commands/harness.ts +6 -0
  87. package/src/commands/work.ts +80 -13
  88. package/src/commands/workflow.ts +74 -0
  89. package/src/core/harnesses/attest-projection.ts +151 -0
  90. package/src/core/harnesses/attest.ts +23 -1
  91. package/src/core/harnesses/attestation.ts +7 -1
  92. package/src/core/harnesses/bench.ts +16 -0
  93. package/src/core/harnesses/profiles.ts +11 -0
  94. package/src/core/harnesses/types.ts +20 -0
  95. package/src/core/work/index.ts +1 -0
  96. package/src/core/work/runner.ts +8 -0
  97. package/src/core/work/state.ts +177 -27
  98. package/src/core/workflow/approvals.ts +3 -6
  99. package/src/core/workflow/attempt-context.ts +47 -1
  100. package/src/core/workflow/engine.ts +106 -33
  101. package/src/core/workflow/index.ts +1 -0
  102. package/src/core/workflow/journal.ts +90 -0
  103. package/src/core/workflow/proof.ts +4 -0
  104. package/src/core/workflow/sandbox-projection.ts +150 -0
  105. package/src/core/workflow/spawn-claude.ts +18 -1
  106. package/src/core/workflow/spawn-codex.ts +25 -2
  107. package/src/core/workflow/spawn-cursor.ts +18 -1
  108. package/src/core/workflow/types.ts +66 -0
  109. package/src/core/workflow/workspaces/index.ts +2 -0
  110. package/src/core/workflow/workspaces/inspect.ts +15 -0
  111. package/src/core/workflow/workspaces/reclaim.ts +109 -0
  112. package/src/core/workflow/workspaces/state.ts +13 -33
  113. package/src/lib/browser/client.ts +129 -0
  114. package/src/lib/exec.ts +10 -1
  115. package/src/lib/tunnel/error-page.ts +114 -0
  116. package/src/lib/tunnel/gate.ts +66 -3
@@ -25,7 +25,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
25
25
  return path;
26
26
  };
27
27
  import { createHash, randomBytes } from "node:crypto";
28
- import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
28
+ import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
29
29
  import { isAbsolute, join, resolve } from "node:path";
30
30
  import { pathToFileURL } from "node:url";
31
31
  import { snapshotRepo } from "../context/index.js";
@@ -34,15 +34,27 @@ import { assertWorkflowRunId, createWorkflowApproval } from "./approvals.js";
34
34
  import { freezeWorkflowAttemptContext } from "./attempt-context.js";
35
35
  import { probeBilling } from "./billing.js";
36
36
  import { stableDigest } from "./durable-record.js";
37
+ import { appendWorkflowJournalEvent } from "./journal.js";
37
38
  import { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, writeWorkflowProof, } from "./proof.js";
38
39
  import { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
40
+ import { assertProjectionWithinWorkspace, resolveGitGrantRoots } from "./sandbox-projection.js";
39
41
  import { normalizeWorkflowSpecialists, resolveSpecialistAssignment } from "./specialists.js";
40
- import { WORKFLOW_PROOF_SCHEMA_VERSION } from "./types.js";
41
42
  import { parseStageOutput, validateAgainstSchema } from "./validate.js";
42
43
  import { freezeWorkflowWorkContext } from "./work-context.js";
43
44
  import { attestTerminal, attestWorkspaceFailure, resolveWorkspaceBinding, } from "./workspaces/execution.js";
44
- import { appendWorkflowJournalEvent } from "./workspaces/state.js";
45
45
  import { isWorkspaceAttestation } from "./workspaces/validate.js";
46
+ /** Proof record of the filesystem projection a run applied (ADR 0039/0040).
47
+ * Absent when no policy was in force, which reads as "no projection applied"
48
+ * rather than "an empty one". */
49
+ function sandboxProjectionEvidence(policy, gitWrite) {
50
+ if (!policy)
51
+ return undefined;
52
+ return {
53
+ mode: policy.mode,
54
+ writable_roots: [...(policy.writableRoots ?? [])],
55
+ git_grant: gitWrite,
56
+ };
57
+ }
46
58
  const DEFAULT_MAX_AGENTS = 50;
47
59
  const DEFAULT_CONCURRENCY = 4;
48
60
  const DEFAULT_MAX_ATTEMPTS = 2;
@@ -246,6 +258,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
246
258
  evidence: [],
247
259
  harnessEvidence: opts.harnessEvidence,
248
260
  harnessAttestations: opts.harnessAttestations,
261
+ sandboxProjection: sandboxProjectionEvidence(opts.filesystemPolicy, opts.gitWrite ?? "none"),
249
262
  policy: policy
250
263
  ? {
251
264
  config: policy,
@@ -267,6 +280,30 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
267
280
  }
268
281
  const effectiveIsolation = workspaceFallback?.effective_isolation ?? isolation;
269
282
  const executionCwd = workspaceBinding?.active_root ?? cwd;
283
+ // Validate the projection once, before any child launches, so a policy that
284
+ // would reach outside the provider's validated root fails the run rather than
285
+ // silently widening one child's write access (ADR 0039).
286
+ const requestedPolicy = opts.filesystemPolicy;
287
+ if (requestedPolicy?.writableRoots?.length) {
288
+ if (!workspaceBinding) {
289
+ throw new Error("a filesystem policy with writable roots requires an isolated workspace; none is bound to this run");
290
+ }
291
+ assertProjectionWithinWorkspace("workflow", workspaceBinding.writable_root.realpath, requestedPolicy.writableRoots);
292
+ }
293
+ // The Git grant is resolved after containment on purpose: caller-supplied
294
+ // roots must stay inside the workspace, while these come from the verified
295
+ // binding and are the one sanctioned way out of it (ADR 0040).
296
+ const gitWrite = opts.gitWrite ?? "none";
297
+ const gitGrantRoots = resolveGitGrantRoots(gitWrite, workspaceBinding);
298
+ if (gitGrantRoots.length > 0 && !requestedPolicy) {
299
+ throw new Error(`gitWrite "${gitWrite}" has no effect without a filesystem policy to carry it; set filesystemPolicy or drop the grant`);
300
+ }
301
+ const filesystemPolicy = requestedPolicy
302
+ ? {
303
+ ...requestedPolicy,
304
+ writableRoots: [...(requestedPolicy.writableRoots ?? []), ...gitGrantRoots],
305
+ }
306
+ : undefined;
270
307
  const executionRepoBefore = resumeState?.manifest.repository_before ?? snapshotRepo(executionCwd);
271
308
  if (!resumeState) {
272
309
  writeWorkflowRunManifest({
@@ -379,15 +416,18 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
379
416
  const policyDecisions = new Map(history?.policyDecisions.map((decision) => [decision.id, decision]));
380
417
  const acceptanceIds = new Set(meta.acceptance.map((criterion) => criterion.id));
381
418
  const journal = (event, data) => {
382
- const line = JSON.stringify({
383
- schema_version: WORKFLOW_PROOF_SCHEMA_VERSION,
384
- run_id: runId,
385
- ts: new Date().toISOString(),
386
- event,
387
- stage: currentStage,
419
+ appendWorkflowJournalEvent(opts.coordRoot, runId, event, { stage: currentStage, ...data });
420
+ };
421
+ const journalAgentEnd = (data, resultKind, result) => {
422
+ // The writer shrinks an oversized record itself and names what it dropped,
423
+ // so no per-call fallback is needed here. A digest rides along regardless so
424
+ // a shrunk record can still be tied back to the exact result.
425
+ journal("agent.end", {
388
426
  ...data,
427
+ result_kind: resultKind,
428
+ result_digest: digestResult(result, resultKind),
429
+ result,
389
430
  });
390
- appendFileSync(journalPath, `${line}\n`, "utf8");
391
431
  };
392
432
  // Bounded concurrency gate shared by every spawn in the run — direct
393
433
  // `agent()` calls and `parallel()` thunks draw from the same slot pool, so
@@ -726,6 +766,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
726
766
  cwd: executionCwd,
727
767
  runId,
728
768
  subscriptionOnly,
769
+ filesystemPolicy,
729
770
  });
730
771
  agentProof.attempts = attempt;
731
772
  agentProof.duration_ms += last.durationMs;
@@ -741,7 +782,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
741
782
  agentCostUsd > 0 || last.costUsd !== undefined ? agentCostUsd : undefined;
742
783
  agentProof.session_id = last.sessionId;
743
784
  agentProof.result = digestResult(last.text, "text");
744
- journal("agent.end", {
785
+ journalAgentEnd({
745
786
  id,
746
787
  key,
747
788
  attempts: attempt,
@@ -749,9 +790,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
749
790
  total_cost_usd: agentCostUsd,
750
791
  duration_ms: last.durationMs,
751
792
  session_id: last.sessionId,
752
- result_kind: "text",
753
- result: last.text,
754
- });
793
+ }, "text", last.text);
755
794
  return last.text;
756
795
  }
757
796
  const parsed = parseStageOutput(last.text);
@@ -764,7 +803,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
764
803
  agentCostUsd > 0 || last.costUsd !== undefined ? agentCostUsd : undefined;
765
804
  agentProof.session_id = last.sessionId;
766
805
  agentProof.result = digestResult(parsed.value, "json");
767
- journal("agent.end", {
806
+ journalAgentEnd({
768
807
  id,
769
808
  key,
770
809
  attempts: attempt,
@@ -772,9 +811,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
772
811
  total_cost_usd: agentCostUsd,
773
812
  duration_ms: last.durationMs,
774
813
  session_id: last.sessionId,
775
- result_kind: "json",
776
- result: parsed.value,
777
- });
814
+ }, "json", parsed.value);
778
815
  return parsed.value;
779
816
  }
780
817
  journal("agent.schema_retry", { id, attempt, problems });
@@ -927,6 +964,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
927
964
  evidence: evidenceRecords,
928
965
  harnessEvidence: opts.harnessEvidence,
929
966
  harnessAttestations: opts.harnessAttestations,
967
+ sandboxProjection: sandboxProjectionEvidence(filesystemPolicy, gitWrite),
930
968
  policy: policy
931
969
  ? {
932
970
  config: policy,
@@ -1005,6 +1043,7 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
1005
1043
  evidence: evidenceRecords,
1006
1044
  harnessEvidence: opts.harnessEvidence,
1007
1045
  harnessAttestations: opts.harnessAttestations,
1046
+ sandboxProjection: sandboxProjectionEvidence(filesystemPolicy, gitWrite),
1008
1047
  policy: policy
1009
1048
  ? {
1010
1049
  config: policy,
@@ -1086,7 +1125,10 @@ function loadResumeCache(coordRoot, resumeFrom) {
1086
1125
  continue;
1087
1126
  try {
1088
1127
  const e = JSON.parse(line);
1089
- if (e.event === "agent.end" && e.key && e.result_kind !== undefined) {
1128
+ if (e.event === "agent.end" &&
1129
+ e.key &&
1130
+ (e.result_kind === "json" || e.result_kind === "text") &&
1131
+ "result" in e) {
1090
1132
  cache.set(e.key, { kind: e.result_kind, value: e.result });
1091
1133
  }
1092
1134
  }
@@ -8,5 +8,5 @@ export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowS
8
8
  export type { AcceptanceCriterion, AcceptanceResult, AcceptanceStatus, AcceptanceSummary, AgentOpts, EngineOpts, EvidenceKind, EvidenceSource, EvidenceStatus, HarnessEvidenceCapability, HarnessEvidenceCoverage, HarnessName, ResultDigest, RunReport, Spawner, SpawnRequest, SpawnResult, StageSchema, WorkflowAgentProof, WorkflowApprovalMode, WorkflowAttemptContext, WorkflowAttemptFailureCause, WorkflowAttemptPriorContext, WorkflowAttemptUnresolvedCriterion, WorkflowContext, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowModule, WorkflowPolicyProof, WorkflowProof, WorkflowProofUnknown, WorkflowRepoEvidence, WorkflowRepoSnapshot, WorkflowRunStatus, WorkflowSpecialistProfile, WorkflowWorkContext, } from "./types.js";
9
9
  export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
10
10
  export type { ApplyIntegrationInput, AuthorizedIntegrationPlan, AuthorizedProviderIntegrationInput, CancelWorkspaceInput, CleanupWorkspaceInput, FilesystemIdentity, IntegrationApplyAttempt, IntegrationApplyReceipt, IntegrationAuthorization, IntegrationPlan, IntegrationReviewRecord, IntegrationTarget, LocalGitWorktreeProviderOptions, PrepareIntegrationInput, ProviderIntegrationInput, ProviderIntegrationPreview, ProviderIntegrationResult, ProviderUnknown, ProviderUnsupportedReason, RepositoryBinding, ValidatedFilesystemPath, WorkflowWorkspaceInspection, WorkflowWorkspaceSelection, WorkflowWorkspaceStatus, WorkflowWorkspaceVerification, WorkspaceAllocationRequest, WorkspaceAttestation, WorkspaceBinding, WorkspaceBoundExecutionEvidence, WorkspaceCancellationReceipt, WorkspaceCancellationResult, WorkspaceCleanupAttempt, WorkspaceCleanupIntent, WorkspaceCleanupMode, WorkspaceCleanupReceipt, WorkspaceCleanupResult, WorkspaceCompatibilityExecutionEvidence, WorkspaceExecutionEvidence, WorkspaceIntegrationState, WorkspaceIsolation, WorkspaceLifecycleProjection, WorkspaceLifecycleProjectionInput, WorkspaceLifecycleState, WorkspaceObservation, WorkspaceOwner, WorkspaceProbeInput, WorkspaceProbeResult, WorkspaceProofOutcome, WorkspaceProvider, WorkspaceProviderCapabilities, WorkspaceProviderRef, WorkspaceResourceState, WorkspaceUnsupportedExecutionEvidence, } from "./workspaces/index.js";
11
- export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
11
+ export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, prepareReclaim, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/index.ts"],"names":[],"mappings":"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"}
@@ -4,4 +4,4 @@ export { runWorkflow, WorkflowParkedError, WorkflowRunError } from "./engine.js"
4
4
  export { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
5
5
  export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
6
6
  export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
7
- export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
7
+ export { applyIntegration, assertWorkspaceLifecycleTransition, cancelWorkspace, cleanupWorkspace, createLocalGitWorktreeProvider, deriveWorkspaceLifecycle, IntegrationPrepareParkedError, inspectWorkflowWorkspace, isWorkspaceLifecycleState, listWorkflowWorkspaceInspections, prepareIntegration, prepareReclaim, probeLocalGitWorktreeProvider, readWorkflowWorkspaceStatus, renderWorkflowWorkspaceStatus, validateProviderEventChain, WORKSPACE_BINDING_SCHEMA_VERSION, WORKSPACE_RECEIPT_SCHEMA_VERSION, } from "./workspaces/index.js";
@@ -0,0 +1,30 @@
1
+ export declare const WORKFLOW_JOURNAL_EVENT_BYTES: number;
2
+ /** Key under which a shrunk record names the fields it had to drop. */
3
+ export declare const WORKFLOW_JOURNAL_OMITTED = "omitted_fields";
4
+ export interface WorkflowJournalOmission {
5
+ field: string;
6
+ bytes: number;
7
+ sha256: string;
8
+ }
9
+ export declare function workflowJournalPath(coordRoot: string, runId: string): string;
10
+ /**
11
+ * Append one journal record, always.
12
+ *
13
+ * A record that would exceed what a reader accepts has its largest fields
14
+ * replaced by a digest and a byte count until it fits, and it names what it
15
+ * dropped. Size never raises.
16
+ *
17
+ * Refusing an oversized record would let a valid run fail on its own opening
18
+ * line: `run.start` carries the workflow's declared metadata plus the frozen
19
+ * work and attempt context, and Harnery's own validators permit those to exceed
20
+ * this limit by construction. Losing detail from a record is recoverable.
21
+ * Losing the run that was writing it is not.
22
+ */
23
+ export declare function appendWorkflowJournalEvent(coordRoot: string, runId: string, event: string, data: Record<string, unknown>): void;
24
+ /**
25
+ * Shrink a record to the reader's limit by dropping its largest fields first.
26
+ * The envelope is never dropped, so a record keeps its run id, timestamp, and
27
+ * event name however much detail it loses. Exported for tests.
28
+ */
29
+ export declare function fitWorkflowJournalRecord(envelope: Record<string, unknown>, data: Record<string, unknown>): Record<string, unknown>;
30
+ //# sourceMappingURL=journal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/journal.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,4BAA4B,QAAY,CAAC;AAEtD,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AAIzD,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI,CAiBN;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB"}
@@ -0,0 +1,70 @@
1
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fsyncParentDirectory, stableDigest } from "./durable-record.js";
4
+ // Limit for the JSON record body. The trailing newline delimiter is outside the
5
+ // record and is not counted by readers after splitting journal lines.
6
+ export const WORKFLOW_JOURNAL_EVENT_BYTES = 16 * 1024;
7
+ /** Key under which a shrunk record names the fields it had to drop. */
8
+ export const WORKFLOW_JOURNAL_OMITTED = "omitted_fields";
9
+ const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
10
+ export function workflowJournalPath(coordRoot, runId) {
11
+ if (!RUN_ID.test(runId))
12
+ throw new Error(`invalid workflow run id ${JSON.stringify(runId)}`);
13
+ return join(resolve(coordRoot), ".harnery", "workflows", runId, "journal.jsonl");
14
+ }
15
+ /**
16
+ * Append one journal record, always.
17
+ *
18
+ * A record that would exceed what a reader accepts has its largest fields
19
+ * replaced by a digest and a byte count until it fits, and it names what it
20
+ * dropped. Size never raises.
21
+ *
22
+ * Refusing an oversized record would let a valid run fail on its own opening
23
+ * line: `run.start` carries the workflow's declared metadata plus the frozen
24
+ * work and attempt context, and Harnery's own validators permit those to exceed
25
+ * this limit by construction. Losing detail from a record is recoverable.
26
+ * Losing the run that was writing it is not.
27
+ */
28
+ export function appendWorkflowJournalEvent(coordRoot, runId, event, data) {
29
+ const path = workflowJournalPath(coordRoot, runId);
30
+ const record = fitWorkflowJournalRecord({ schema_version: 1, run_id: runId, ts: new Date().toISOString(), event }, data);
31
+ const line = `${JSON.stringify(record)}\n`;
32
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
33
+ const existed = existsSync(path);
34
+ const fd = openSync(path, "a", 0o600);
35
+ try {
36
+ writeFileSync(fd, line, "utf8");
37
+ fsyncSync(fd);
38
+ }
39
+ finally {
40
+ closeSync(fd);
41
+ }
42
+ if (!existed)
43
+ fsyncParentDirectory(path);
44
+ }
45
+ /**
46
+ * Shrink a record to the reader's limit by dropping its largest fields first.
47
+ * The envelope is never dropped, so a record keeps its run id, timestamp, and
48
+ * event name however much detail it loses. Exported for tests.
49
+ */
50
+ export function fitWorkflowJournalRecord(envelope, data) {
51
+ const candidate = { ...envelope, ...data };
52
+ if (Buffer.byteLength(JSON.stringify(candidate)) <= WORKFLOW_JOURNAL_EVENT_BYTES) {
53
+ return candidate;
54
+ }
55
+ const kept = { ...data };
56
+ const omitted = [];
57
+ const bySize = Object.keys(data)
58
+ .map((field) => ({ field, bytes: Buffer.byteLength(JSON.stringify(data[field]) ?? "") }))
59
+ .sort((a, b) => b.bytes - a.bytes);
60
+ for (const { field, bytes } of bySize) {
61
+ omitted.push({ field, bytes, sha256: stableDigest(data[field]) });
62
+ delete kept[field];
63
+ const next = { ...envelope, ...kept, [WORKFLOW_JOURNAL_OMITTED]: omitted };
64
+ if (Buffer.byteLength(JSON.stringify(next)) <= WORKFLOW_JOURNAL_EVENT_BYTES)
65
+ return next;
66
+ }
67
+ // Every field dropped and still over: the omission list is itself the excess.
68
+ // Keep the envelope and a count so the record stays parseable and honest.
69
+ return { ...envelope, [WORKFLOW_JOURNAL_OMITTED]: omitted.length };
70
+ }
@@ -1,6 +1,6 @@
1
1
  import type { RepoSnapshot } from "../context/index.js";
2
2
  import type { NormalizedPolicy, PolicyDecision, PolicyIsolation, PolicyNetworkAccess } from "../policy/index.js";
3
- import type { AcceptanceCriterion, AcceptanceResult, AcceptanceSummary, HarnessAttestationCitation, HarnessEvidenceCapability, ResultDigest, WorkflowAgentProof, WorkflowAttemptContext, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowProof, WorkflowWorkContext } from "./types.js";
3
+ import type { AcceptanceCriterion, AcceptanceResult, AcceptanceSummary, HarnessAttestationCitation, HarnessEvidenceCapability, ResultDigest, WorkflowAgentProof, WorkflowAttemptContext, WorkflowEvidenceInput, WorkflowEvidenceRecord, WorkflowMeta, WorkflowProof, WorkflowSandboxProjectionEvidence, WorkflowWorkContext } from "./types.js";
4
4
  import type { WorkspaceAttestation, WorkspaceBinding, WorkspaceUnsupportedExecutionEvidence } from "./workspaces/index.js";
5
5
  export interface NormalizedWorkflowMeta {
6
6
  name: string;
@@ -24,6 +24,8 @@ export interface BuildWorkflowProofInput {
24
24
  agents: WorkflowAgentProof[];
25
25
  evidence: WorkflowEvidenceRecord[];
26
26
  harnessEvidence?: Readonly<Record<string, HarnessEvidenceCapability | undefined>>;
27
+ /** Filesystem policy actually projected into children (ADR 0039). */
28
+ sandboxProjection?: WorkflowSandboxProjectionEvidence;
27
29
  /** Live attestations backing each harness's claims (ADR 0038). Injected by
28
30
  * the caller so proof stays free of filesystem lookups. */
29
31
  harnessAttestations?: Readonly<Record<string, HarnessAttestationCitation | undefined>>;
@@ -1 +1 @@
1
- {"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/proof.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EAEzB,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EAEZ,aAAa,EAIb,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAEhB,qCAAqC,EACtC,MAAM,uBAAuB,CAAC;AAoB/B,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,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAC5C,cAAc,CAAC,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC;IAClD,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;+DAC2D;IAC3D,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,0BAA0B,GAAG,SAAS,CAAC,CAAC,CAAC;IACvF,MAAM,CAAC,EAAE;QACP,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACnC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;QACrC,SAAS,EAAE,eAAe,CAAC;QAC3B,aAAa,EAAE,mBAAmB,CAAC;KACpC,CAAC;IACF,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iBAAiB,CAAC,EAAE,qCAAqC,CAAC;IAC1D,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,CAiDhF;AAgCD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAO3E;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAgDjF;AA2BD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAoChE"}
1
+ {"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/proof.ts"],"names":[],"mappings":"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"}
@@ -146,6 +146,7 @@ export function buildWorkflowProof(input) {
146
146
  ? buildExecutionEvidence(input.status, input.workspaceBinding, input.workspaceAttestation)
147
147
  : input.workspaceFallback,
148
148
  repository,
149
+ ...(input.sandboxProjection ? { sandbox_projection: input.sandboxProjection } : {}),
149
150
  harnesses,
150
151
  unknowns,
151
152
  integrity: {
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Project the host's filesystem policy into a harness's own vendor sandbox
3
+ * (ADR 0039).
4
+ *
5
+ * Harnery decides where a workflow child may write. Until this existed it never
6
+ * told the child, so a child working in a provider-owned Git worktree could
7
+ * edit files and could not commit: the vendor excludes a repository's
8
+ * administrative directory from its writable set by policy, and Harnery had no
9
+ * way to name it as an exception.
10
+ *
11
+ * The one rule that matters here is that a projection an adapter cannot
12
+ * represent is refused before launch. Passing a policy that gets silently
13
+ * dropped would leave an operator believing a child was constrained when it was
14
+ * not, which is worse than refusing and worse than never offering the feature.
15
+ */
16
+ import type { HarnessSandboxProjection } from "../harnesses/types.js";
17
+ import type { GitAdministrativeGrant, SpawnFilesystemPolicy } from "./types.js";
18
+ import type { WorkspaceBinding } from "./workspaces/types.js";
19
+ export declare class SandboxProjectionError extends Error {
20
+ readonly harness: string;
21
+ readonly reason: "mode_unrepresentable" | "writable_roots_unrepresentable" | "no_projection" | "writable_root_escapes_workspace" | "git_grant_unavailable";
22
+ constructor(harness: string, reason: SandboxProjectionError["reason"], message: string);
23
+ }
24
+ export interface ResolvedSandboxProjection {
25
+ /** Vendor-native name for the requested mode. */
26
+ nativeMode: string;
27
+ writableRoots: readonly string[];
28
+ }
29
+ /**
30
+ * Resolve a requested policy against what the adapter declares it can carry.
31
+ * Throws rather than degrading; see the module note.
32
+ */
33
+ export declare function resolveSandboxProjection(harness: string, declaration: HarnessSandboxProjection | undefined, policy: SpawnFilesystemPolicy): ResolvedSandboxProjection;
34
+ /**
35
+ * Refuse a projection that would grant write access outside the root the
36
+ * workspace provider already validated (ADR 0039).
37
+ *
38
+ * The renderer alone cannot do this: it never sees the binding. Granting a path
39
+ * the provider never sanctioned would let a projection quietly widen the blast
40
+ * radius of a run that the workspace lifecycle believes it has contained.
41
+ */
42
+ export declare function assertProjectionWithinWorkspace(harness: string, allowedRootRealpath: string, writableRoots: readonly string[]): void;
43
+ /**
44
+ * Resolve a named Git administrative grant into concrete writable roots
45
+ * (ADR 0040).
46
+ *
47
+ * These are the only paths a run may write outside its workspace, and the
48
+ * caller never names them: they come from the binding the provider verified.
49
+ * A caller-supplied path is checked by `assertProjectionWithinWorkspace` and can
50
+ * never reach here, so asking for the grant is the whole of the widening.
51
+ *
52
+ * In a linked worktree both halves of the administrative directory live under
53
+ * the source repository, and a commit needs the shared half regardless, so the
54
+ * grant returns both rather than pretending the private half is useful alone.
55
+ */
56
+ export declare function resolveGitGrantRoots(grant: GitAdministrativeGrant, binding: WorkspaceBinding | undefined): readonly string[];
57
+ //# sourceMappingURL=sandbox-projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-projection.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/sandbox-projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EACX,sBAAsB,GACtB,gCAAgC,GAChC,eAAe,GACf,iCAAiC,GACjC,uBAAuB,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM;CAMvF;AAED,MAAM,WAAW,yBAAyB;IACxC,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,wBAAwB,GAAG,SAAS,EACjD,MAAM,EAAE,qBAAqB,GAC5B,yBAAyB,CAoC3B;AASD;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,EACf,mBAAmB,EAAE,MAAM,EAC3B,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,IAAI,CAUN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,sBAAsB,EAC7B,OAAO,EAAE,gBAAgB,GAAG,SAAS,GACpC,SAAS,MAAM,EAAE,CAanB"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Project the host's filesystem policy into a harness's own vendor sandbox
3
+ * (ADR 0039).
4
+ *
5
+ * Harnery decides where a workflow child may write. Until this existed it never
6
+ * told the child, so a child working in a provider-owned Git worktree could
7
+ * edit files and could not commit: the vendor excludes a repository's
8
+ * administrative directory from its writable set by policy, and Harnery had no
9
+ * way to name it as an exception.
10
+ *
11
+ * The one rule that matters here is that a projection an adapter cannot
12
+ * represent is refused before launch. Passing a policy that gets silently
13
+ * dropped would leave an operator believing a child was constrained when it was
14
+ * not, which is worse than refusing and worse than never offering the feature.
15
+ */
16
+ export class SandboxProjectionError extends Error {
17
+ harness;
18
+ reason;
19
+ constructor(harness, reason, message) {
20
+ super(message);
21
+ this.name = "SandboxProjectionError";
22
+ this.harness = harness;
23
+ this.reason = reason;
24
+ }
25
+ }
26
+ /**
27
+ * Resolve a requested policy against what the adapter declares it can carry.
28
+ * Throws rather than degrading; see the module note.
29
+ */
30
+ export function resolveSandboxProjection(harness, declaration, policy) {
31
+ if (!declaration) {
32
+ throw new SandboxProjectionError(harness, "no_projection", `${harness} cannot project a filesystem policy into its sandbox; remove the policy or use a harness that can`);
33
+ }
34
+ const nativeMode = declaration.modes[policy.mode];
35
+ if (!nativeMode) {
36
+ throw new SandboxProjectionError(harness, "mode_unrepresentable", `${harness} does not distinguish the "${policy.mode}" filesystem mode, so it cannot be enforced`);
37
+ }
38
+ const writableRoots = policy.writableRoots ?? [];
39
+ if (writableRoots.length > 0 && !declaration.writableRoots) {
40
+ throw new SandboxProjectionError(harness, "writable_roots_unrepresentable", `${harness} does not accept an explicit writable-root set, so ${writableRoots.length} declared path(s) could not be enforced`);
41
+ }
42
+ for (const root of writableRoots) {
43
+ // Relative paths cannot be validated against the provider's roots and are
44
+ // resolved differently by every vendor, so they are refused outright.
45
+ if (typeof root !== "string" || !root.startsWith("/")) {
46
+ throw new SandboxProjectionError(harness, "writable_roots_unrepresentable", `writable root ${JSON.stringify(root)} must be an absolute path`);
47
+ }
48
+ }
49
+ return { nativeMode, writableRoots };
50
+ }
51
+ /** True when `candidate` is `root` or lies beneath it, comparing whole path
52
+ * segments so `/a/bc` is not treated as inside `/a/b`. */
53
+ function isWithin(root, candidate) {
54
+ const normalizedRoot = root.endsWith("/") ? root.slice(0, -1) : root;
55
+ return candidate === normalizedRoot || candidate.startsWith(`${normalizedRoot}/`);
56
+ }
57
+ /**
58
+ * Refuse a projection that would grant write access outside the root the
59
+ * workspace provider already validated (ADR 0039).
60
+ *
61
+ * The renderer alone cannot do this: it never sees the binding. Granting a path
62
+ * the provider never sanctioned would let a projection quietly widen the blast
63
+ * radius of a run that the workspace lifecycle believes it has contained.
64
+ */
65
+ export function assertProjectionWithinWorkspace(harness, allowedRootRealpath, writableRoots) {
66
+ for (const root of writableRoots) {
67
+ if (!isWithin(allowedRootRealpath, root)) {
68
+ throw new SandboxProjectionError(harness, "writable_root_escapes_workspace", `writable root ${JSON.stringify(root)} is outside the workspace root ${JSON.stringify(allowedRootRealpath)} the provider validated`);
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Resolve a named Git administrative grant into concrete writable roots
74
+ * (ADR 0040).
75
+ *
76
+ * These are the only paths a run may write outside its workspace, and the
77
+ * caller never names them: they come from the binding the provider verified.
78
+ * A caller-supplied path is checked by `assertProjectionWithinWorkspace` and can
79
+ * never reach here, so asking for the grant is the whole of the widening.
80
+ *
81
+ * In a linked worktree both halves of the administrative directory live under
82
+ * the source repository, and a commit needs the shared half regardless, so the
83
+ * grant returns both rather than pretending the private half is useful alone.
84
+ */
85
+ export function resolveGitGrantRoots(grant, binding) {
86
+ if (grant === "none")
87
+ return [];
88
+ const repository = binding?.repository;
89
+ if (!repository) {
90
+ throw new SandboxProjectionError("workflow", "git_grant_unavailable", `gitWrite "${grant}" needs a Git repository binding; this run has ${binding ? "a workspace with no repository" : "no isolated workspace"}`);
91
+ }
92
+ // Deduplicated: a full-clone topology reports the same path for both, and a
93
+ // repeated writable root is noise in the rendered argv and in proof.
94
+ return [...new Set([repository.gitdir.realpath, repository.common_dir.realpath])];
95
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-claude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIjF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYrE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAc1E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAgDxE;AAED,eAAO,MAAM,iBAAiB,EAAE,OAc/B,CAAC"}
1
+ {"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-claude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAKjF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYrE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAsB1E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAwDxE;AAED,eAAO,MAAM,iBAAiB,EAAE,OAc/B,CAAC"}
@@ -18,12 +18,17 @@
18
18
  * (stop-hook.ts), keeping heartbeats + events on.
19
19
  */
20
20
  import { exec } from "../../lib/exec.js";
21
- import { validateHarnessEffort } from "../harnesses/profiles.js";
21
+ import { builtinHarnessProfile, validateHarnessEffort } from "../harnesses/profiles.js";
22
22
  import { buildChildEnv } from "./child-env.js";
23
23
  import { notFoundError } from "./harnesses.js";
24
+ import { resolveSandboxProjection } from "./sandbox-projection.js";
24
25
  import { vendorFailureText } from "./spawn-failure.js";
25
26
  export function buildClaudeInvocation(req) {
26
27
  validateHarnessEffort("claude-code", req.effort);
28
+ if (req.filesystemPolicy) {
29
+ // Declared unrepresentable: refuse rather than drop it silently (ADR 0039).
30
+ resolveSandboxProjection("claude-code", builtinHarnessProfile("claude-code")?.sandboxProjection, req.filesystemPolicy);
31
+ }
27
32
  const argv = [
28
33
  "claude",
29
34
  "-p",
@@ -40,6 +45,14 @@ export function buildClaudeInvocation(req) {
40
45
  return { argv };
41
46
  }
42
47
  export function normalizeClaudeResult(raw) {
48
+ if (raw.timedOut) {
49
+ return {
50
+ ok: false,
51
+ text: "",
52
+ durationMs: raw.durationMs,
53
+ error: `claude timed out after ${raw.durationMs}ms and was killed`,
54
+ };
55
+ }
43
56
  if (raw.exitCode === 127) {
44
57
  return {
45
58
  ok: false,
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-codex.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIjF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAgB9F;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAiBvE;AAED,eAAO,MAAM,YAAY,EAAE,OA2B1B,CAAC"}
1
+ {"version":3,"file":"spawn-codex.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAKjF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB,CA8B9F;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAyBvE;AAED,eAAO,MAAM,YAAY,EAAE,OA2B1B,CAAC"}
@@ -19,14 +19,19 @@ import { existsSync, readFileSync, rmSync } from "node:fs";
19
19
  import { tmpdir } from "node:os";
20
20
  import { join } from "node:path";
21
21
  import { exec } from "../../lib/exec.js";
22
- import { validateHarnessEffort } from "../harnesses/profiles.js";
22
+ import { builtinHarnessProfile, validateHarnessEffort } from "../harnesses/profiles.js";
23
23
  import { buildChildEnv } from "./child-env.js";
24
24
  import { notFoundError } from "./harnesses.js";
25
+ import { resolveSandboxProjection } from "./sandbox-projection.js";
25
26
  import { vendorFailureText } from "./spawn-failure.js";
26
27
  export function buildCodexInvocation(req, resultFile) {
27
28
  validateHarnessEffort("codex", req.effort);
28
29
  if (!resultFile)
29
30
  throw new Error("codex adapter requires a final-message result file");
31
+ // Default stays workspace-write so an unprojected request is unchanged.
32
+ const projection = req.filesystemPolicy
33
+ ? resolveSandboxProjection("codex", builtinHarnessProfile("codex")?.sandboxProjection, req.filesystemPolicy)
34
+ : undefined;
30
35
  const argv = [
31
36
  "codex",
32
37
  "exec",
@@ -35,8 +40,11 @@ export function buildCodexInvocation(req, resultFile) {
35
40
  resultFile,
36
41
  "--skip-git-repo-check",
37
42
  "--sandbox",
38
- "workspace-write",
43
+ projection?.nativeMode ?? "workspace-write",
39
44
  ];
45
+ if (projection && projection.writableRoots.length > 0) {
46
+ argv.push("-c", `sandbox_workspace_write.writable_roots=${JSON.stringify(projection.writableRoots)}`);
47
+ }
40
48
  if (req.model)
41
49
  argv.push("--model", req.model);
42
50
  if (req.effort)
@@ -44,6 +52,14 @@ export function buildCodexInvocation(req, resultFile) {
44
52
  return { argv, resultFile };
45
53
  }
46
54
  export function normalizeCodexResult(raw) {
55
+ if (raw.timedOut) {
56
+ return {
57
+ ok: false,
58
+ text: "",
59
+ durationMs: raw.durationMs,
60
+ error: `codex timed out after ${raw.durationMs}ms and was killed`,
61
+ };
62
+ }
47
63
  if (raw.exitCode === 127) {
48
64
  return { ok: false, text: "", durationMs: raw.durationMs, error: notFoundError("codex") };
49
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-cursor.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIjF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AASrE,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,CAWA;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAU1E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CA6BxE;AAED,eAAO,MAAM,aAAa,EAAE,OAe3B,CAAC"}
1
+ {"version":3,"file":"spawn-cursor.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAKjF,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AASrE,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,CAWA;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAkB1E;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,WAAW,CAqCxE;AAED,eAAO,MAAM,aAAa,EAAE,OAe3B,CAAC"}