harnery 0.23.0 → 0.25.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/README.md +1 -1
- package/dist/commands/harness.d.ts +2 -1
- package/dist/commands/harness.d.ts.map +1 -1
- package/dist/commands/harness.js +105 -3
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +3 -13
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +2 -7
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +215 -17
- package/dist/core/harnesses/attest.d.ts +48 -0
- package/dist/core/harnesses/attest.d.ts.map +1 -0
- package/dist/core/harnesses/attest.js +110 -0
- package/dist/core/harnesses/attestation.d.ts +75 -0
- package/dist/core/harnesses/attestation.d.ts.map +1 -0
- package/dist/core/harnesses/attestation.js +168 -0
- package/dist/core/harnesses/bench.d.ts +23 -1
- package/dist/core/harnesses/bench.d.ts.map +1 -1
- package/dist/core/harnesses/bench.js +135 -32
- package/dist/core/harnesses/index.d.ts +6 -2
- package/dist/core/harnesses/index.d.ts.map +1 -1
- package/dist/core/harnesses/index.js +3 -1
- package/dist/core/workflow/engine.js +3 -0
- 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/proof.d.ts +4 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +3 -2
- package/dist/core/workflow/spawn-cursor.d.ts +3 -0
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +8 -4
- package/dist/core/workflow/types.d.ts +14 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +1 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +1 -1
- package/dist/core/workflow/workspaces/integration.d.ts +8 -0
- package/dist/core/workflow/workspaces/integration.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/integration.js +15 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +19 -2
- package/package.json +1 -1
- package/src/commands/harness.ts +123 -2
- package/src/commands/supervisor.ts +11 -15
- package/src/commands/work.ts +8 -8
- package/src/commands/workflow.ts +268 -20
- package/src/core/harnesses/attest.ts +162 -0
- package/src/core/harnesses/attestation.ts +231 -0
- package/src/core/harnesses/bench.ts +187 -32
- package/src/core/harnesses/index.ts +26 -1
- package/src/core/workflow/engine.ts +3 -0
- package/src/core/workflow/index.ts +1 -0
- package/src/core/workflow/proof.ts +7 -1
- package/src/core/workflow/spawn-cursor.ts +8 -4
- package/src/core/workflow/types.ts +15 -0
- package/src/core/workflow/workspaces/index.ts +5 -1
- package/src/core/workflow/workspaces/integration.ts +17 -1
- package/src/core/workflow/workspaces/leases.ts +24 -2
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* session_id, …}`).
|
|
11
11
|
* - `--trust` is required: headless runs refuse untrusted workspaces (exit 1,
|
|
12
12
|
* "Workspace Trust Required") — see the argv comment below.
|
|
13
|
+
* - `--force` is required: print mode has no channel for an operator to approve
|
|
14
|
+
* shell commands, so Smart Auto can leave a child able to patch files but
|
|
15
|
+
* unable to test or commit them.
|
|
13
16
|
* - Envelope drift guard: when stdout doesn't parse as JSON but the process
|
|
14
17
|
* exited 0, the raw stdout is returned as the reply text.
|
|
15
18
|
* - No per-run cost surface → undefined. No max-turns equivalent → `maxTurns`
|
|
@@ -51,10 +54,11 @@ export function parseCursorOutput(stdout: string): {
|
|
|
51
54
|
export function buildCursorInvocation(req: SpawnRequest): HarnessInvocation {
|
|
52
55
|
validateHarnessEffort("cursor", req.effort);
|
|
53
56
|
// --trust: headless cursor-agent refuses untrusted workspaces (exit 1,
|
|
54
|
-
// "Workspace Trust Required").
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
57
|
+
// "Workspace Trust Required"). --force: a print-mode child has no interactive
|
|
58
|
+
// approval channel, so host-authorized workflow dispatch must let it run
|
|
59
|
+
// commands. Neither flag maps host policy into child tools; that capability
|
|
60
|
+
// remains explicitly unsupported.
|
|
61
|
+
const argv = ["cursor-agent", "-p", req.prompt, "--output-format", "json", "--trust", "--force"];
|
|
58
62
|
if (req.model) argv.push("--model", req.model);
|
|
59
63
|
return { argv };
|
|
60
64
|
}
|
|
@@ -126,6 +126,14 @@ export interface WorkflowRepoEvidence {
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/** Bounded pointer to the live attestation backing a harness's claims
|
|
130
|
+
* (ADR 0038). Structural facts only: no prompt text, no host paths. */
|
|
131
|
+
export interface HarnessAttestationCitation {
|
|
132
|
+
binary_version: string;
|
|
133
|
+
observed_at: string;
|
|
134
|
+
record_digest: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
129
137
|
export interface HarnessEvidenceCoverage {
|
|
130
138
|
harness: HarnessName;
|
|
131
139
|
tool_evidence: {
|
|
@@ -137,6 +145,10 @@ export interface HarnessEvidenceCoverage {
|
|
|
137
145
|
session_ids: number;
|
|
138
146
|
costs: number;
|
|
139
147
|
};
|
|
148
|
+
/** What backs this harness's capability claims (ADR 0038). Absent when the
|
|
149
|
+
* host recorded no live attestation, which is the common case and is not by
|
|
150
|
+
* itself a proof unknown. */
|
|
151
|
+
attestation?: HarnessAttestationCitation;
|
|
140
152
|
}
|
|
141
153
|
|
|
142
154
|
export interface WorkflowProofUnknown {
|
|
@@ -429,6 +441,9 @@ export interface EngineOpts {
|
|
|
429
441
|
/** Capability claims used to state whether adapter-native tool evidence was
|
|
430
442
|
* available. Missing claims remain unknown. */
|
|
431
443
|
harnessEvidence?: Readonly<Record<HarnessName, HarnessEvidenceCapability | undefined>>;
|
|
444
|
+
/** Live attestations backing each harness's claims (ADR 0038). Read once by
|
|
445
|
+
* the host and injected, so the engine performs no capability lookups. */
|
|
446
|
+
harnessAttestations?: Readonly<Record<HarnessName, HarnessAttestationCitation | undefined>>;
|
|
432
447
|
/** Immutable host policy. Workflow scripts and model prompts cannot replace it. */
|
|
433
448
|
policy?: PolicySpec | NormalizedPolicy;
|
|
434
449
|
/** Host callback for ASK. Missing, invalid, throwing, or timed-out resolution denies. */
|
|
@@ -15,7 +15,11 @@ export {
|
|
|
15
15
|
renderWorkflowWorkspaceStatus,
|
|
16
16
|
} from "./inspect.ts";
|
|
17
17
|
export type { ApplyIntegrationInput, PrepareIntegrationInput } from "./integration.ts";
|
|
18
|
-
export {
|
|
18
|
+
export {
|
|
19
|
+
applyIntegration,
|
|
20
|
+
IntegrationPrepareParkedError,
|
|
21
|
+
prepareIntegration,
|
|
22
|
+
} from "./integration.ts";
|
|
19
23
|
export type {
|
|
20
24
|
WorkspaceLifecycleProjection,
|
|
21
25
|
WorkspaceLifecycleProjectionInput,
|
|
@@ -64,6 +64,22 @@ export interface ApplyIntegrationInput {
|
|
|
64
64
|
plan?: IntegrationPlan;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/** Host-policy ASK still pending during integration prepare. Carries the exact
|
|
68
|
+
* durable IDs an operator needs to approve and resume the same plan. */
|
|
69
|
+
export class IntegrationPrepareParkedError extends Error {
|
|
70
|
+
readonly runId: string;
|
|
71
|
+
readonly planId: string;
|
|
72
|
+
readonly approvalId: string;
|
|
73
|
+
|
|
74
|
+
constructor(runId: string, planId: string, approvalId: string) {
|
|
75
|
+
super(`integration preparation parked: run ${runId}, plan ${planId}, approval ${approvalId}`);
|
|
76
|
+
this.name = "IntegrationPrepareParkedError";
|
|
77
|
+
this.runId = runId;
|
|
78
|
+
this.planId = planId;
|
|
79
|
+
this.approvalId = approvalId;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
67
83
|
export async function prepareIntegration(input: PrepareIntegrationInput): Promise<IntegrationPlan> {
|
|
68
84
|
const evidence = await readAndValidateEvidence(input.coordRoot, input.runId, input.provider);
|
|
69
85
|
const targetRoot = resolve(input.targetRoot ?? requiredIntegrationRoot(evidence.binding));
|
|
@@ -662,7 +678,7 @@ function authorizePlan(input: PrepareIntegrationInput, plan: IntegrationPlan): v
|
|
|
662
678
|
});
|
|
663
679
|
approvalId = created.approval.request.id;
|
|
664
680
|
if (created.approval.status === "pending") {
|
|
665
|
-
throw new
|
|
681
|
+
throw new IntegrationPrepareParkedError(input.runId, plan.plan_id, approvalId);
|
|
666
682
|
}
|
|
667
683
|
if (created.approval.status !== "approved" || !created.approval.decision) {
|
|
668
684
|
throw new Error(`integration approval ${approvalId} was denied`);
|
|
@@ -134,8 +134,14 @@ export function acquireNoClobberLease(input: AcquireNoClobberLeaseInput): NoClob
|
|
|
134
134
|
input.onRecoveryStep?.("claim_created");
|
|
135
135
|
|
|
136
136
|
const pinPath = join(recoveryPath, "stale-pin");
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
try {
|
|
138
|
+
linkSync(currentPath, pinPath);
|
|
139
|
+
assertSameInodeAndBytes(currentPath, pinPath, observed.bytes);
|
|
140
|
+
} catch {
|
|
141
|
+
abortUnchangedRecoveryClaim(leaseDir, recoveryPath, pinPath, owner);
|
|
142
|
+
recoveryClaimed = false;
|
|
143
|
+
throw new Error(`lease ${input.scope} recovery raced with another contender`);
|
|
144
|
+
}
|
|
139
145
|
fsyncDirectory(recoveryPath);
|
|
140
146
|
input.onRecoveryStep?.("stale_pinned");
|
|
141
147
|
|
|
@@ -292,6 +298,22 @@ function reclaimAbandonedRecovery(
|
|
|
292
298
|
fsyncDirectory(leaseDir);
|
|
293
299
|
}
|
|
294
300
|
|
|
301
|
+
function abortUnchangedRecoveryClaim(
|
|
302
|
+
leaseDir: string,
|
|
303
|
+
recoveryPath: string,
|
|
304
|
+
pinPath: string,
|
|
305
|
+
claimant: NoClobberLeaseOwner,
|
|
306
|
+
): void {
|
|
307
|
+
const claim = readRecoveryClaim(recoveryPath);
|
|
308
|
+
if (claim.claimant.owner_id !== claimant.owner_id) {
|
|
309
|
+
throw new Error(`lease ${claimant.scope} recovery ownership changed`);
|
|
310
|
+
}
|
|
311
|
+
safeUnlink(pinPath);
|
|
312
|
+
unlinkSync(join(recoveryPath, "claim.json"));
|
|
313
|
+
rmdirSync(recoveryPath);
|
|
314
|
+
fsyncDirectory(leaseDir);
|
|
315
|
+
}
|
|
316
|
+
|
|
295
317
|
function readRecoveryClaim(recoveryPath: string): RecoveryClaim {
|
|
296
318
|
const path = join(recoveryPath, "claim.json");
|
|
297
319
|
const size = lstatSync(path).size;
|