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.
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +27 -1
- package/dist/commands/harness.d.ts.map +1 -1
- package/dist/commands/harness.js +2 -0
- package/dist/commands/work.d.ts +2 -0
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +47 -4
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +64 -0
- package/dist/core/harnesses/attest-projection.d.ts +44 -0
- package/dist/core/harnesses/attest-projection.d.ts.map +1 -0
- package/dist/core/harnesses/attest-projection.js +113 -0
- package/dist/core/harnesses/attest.d.ts +9 -0
- package/dist/core/harnesses/attest.d.ts.map +1 -1
- package/dist/core/harnesses/attest.js +14 -1
- package/dist/core/harnesses/attestation.d.ts +1 -1
- package/dist/core/harnesses/attestation.d.ts.map +1 -1
- package/dist/core/harnesses/attestation.js +7 -1
- package/dist/core/harnesses/bench.d.ts.map +1 -1
- package/dist/core/harnesses/bench.js +13 -0
- package/dist/core/harnesses/profiles.d.ts +7 -0
- package/dist/core/harnesses/profiles.d.ts.map +1 -1
- package/dist/core/harnesses/profiles.js +9 -0
- package/dist/core/harnesses/types.d.ts +19 -1
- package/dist/core/harnesses/types.d.ts.map +1 -1
- package/dist/core/harnesses/types.js +1 -0
- package/dist/core/work/index.d.ts +1 -1
- package/dist/core/work/index.d.ts.map +1 -1
- package/dist/core/work/index.js +1 -1
- package/dist/core/work/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +8 -1
- package/dist/core/work/state.d.ts +24 -1
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +108 -14
- package/dist/core/workflow/approvals.d.ts.map +1 -1
- package/dist/core/workflow/approvals.js +3 -6
- package/dist/core/workflow/attempt-context.d.ts.map +1 -1
- package/dist/core/workflow/attempt-context.js +38 -1
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +61 -19
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +1 -1
- package/dist/core/workflow/journal.d.ts +30 -0
- package/dist/core/workflow/journal.d.ts.map +1 -0
- package/dist/core/workflow/journal.js +70 -0
- package/dist/core/workflow/proof.d.ts +3 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +1 -0
- package/dist/core/workflow/sandbox-projection.d.ts +57 -0
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -0
- package/dist/core/workflow/sandbox-projection.js +95 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +14 -1
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +18 -2
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +14 -1
- package/dist/core/workflow/types.d.ts +62 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +2 -0
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +1 -0
- package/dist/core/workflow/workspaces/inspect.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/inspect.js +14 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts +50 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -0
- package/dist/core/workflow/workspaces/reclaim.js +88 -0
- package/dist/core/workflow/workspaces/state.d.ts +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +9 -30
- package/dist/lib/browser/client.d.ts +50 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +103 -0
- package/dist/lib/exec.d.ts +4 -0
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +6 -1
- package/dist/lib/tunnel/error-page.d.ts +15 -0
- package/dist/lib/tunnel/error-page.d.ts.map +1 -0
- package/dist/lib/tunnel/error-page.js +98 -0
- package/dist/lib/tunnel/gate.d.ts +1 -12
- package/dist/lib/tunnel/gate.d.ts.map +1 -1
- package/dist/lib/tunnel/gate.js +59 -4
- package/package.json +1 -1
- package/src/commands/browse.ts +33 -1
- package/src/commands/harness.ts +6 -0
- package/src/commands/work.ts +80 -13
- package/src/commands/workflow.ts +74 -0
- package/src/core/harnesses/attest-projection.ts +151 -0
- package/src/core/harnesses/attest.ts +23 -1
- package/src/core/harnesses/attestation.ts +7 -1
- package/src/core/harnesses/bench.ts +16 -0
- package/src/core/harnesses/profiles.ts +11 -0
- package/src/core/harnesses/types.ts +20 -0
- package/src/core/work/index.ts +1 -0
- package/src/core/work/runner.ts +8 -0
- package/src/core/work/state.ts +177 -27
- package/src/core/workflow/approvals.ts +3 -6
- package/src/core/workflow/attempt-context.ts +47 -1
- package/src/core/workflow/engine.ts +106 -33
- package/src/core/workflow/index.ts +1 -0
- package/src/core/workflow/journal.ts +90 -0
- package/src/core/workflow/proof.ts +4 -0
- package/src/core/workflow/sandbox-projection.ts +150 -0
- package/src/core/workflow/spawn-claude.ts +18 -1
- package/src/core/workflow/spawn-codex.ts +25 -2
- package/src/core/workflow/spawn-cursor.ts +18 -1
- package/src/core/workflow/types.ts +66 -0
- package/src/core/workflow/workspaces/index.ts +2 -0
- package/src/core/workflow/workspaces/inspect.ts +15 -0
- package/src/core/workflow/workspaces/reclaim.ts +109 -0
- package/src/core/workflow/workspaces/state.ts +13 -33
- package/src/lib/browser/client.ts +129 -0
- package/src/lib/exec.ts +10 -1
- package/src/lib/tunnel/error-page.ts +114 -0
- 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 {
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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" &&
|
|
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":"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
|
|
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,iCAAiC,EACjC,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,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,iCAAiC,CAAC;IACtD;+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,CAkDhF;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"}
|
|
@@ -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;
|
|
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;
|
|
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;
|
|
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"}
|