harnery 0.28.0 → 0.30.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/work.d.ts +2 -1
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +44 -4
- package/dist/core/harnesses/types.d.ts +5 -0
- package/dist/core/harnesses/types.d.ts.map +1 -1
- package/dist/core/supervisor/plan-read.js +7 -1
- package/dist/core/supervisor/plan-types.d.ts +10 -0
- package/dist/core/supervisor/plan-types.d.ts.map +1 -1
- package/dist/core/supervisor/planning.d.ts.map +1 -1
- package/dist/core/supervisor/planning.js +18 -0
- package/dist/core/supervisor/state.d.ts.map +1 -1
- package/dist/core/supervisor/state.js +65 -11
- package/dist/core/work/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +11 -2
- package/dist/core/work/state.d.ts +17 -0
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +102 -3
- package/dist/core/workflow/engine.js +13 -0
- package/dist/core/workflow/index.d.ts +3 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/proof.d.ts +18 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +34 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +19 -2
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +17 -2
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +18 -2
- package/dist/core/workflow/spawn-failure.d.ts +14 -0
- package/dist/core/workflow/spawn-failure.d.ts.map +1 -1
- package/dist/core/workflow/spawn-failure.js +34 -0
- package/dist/core/workflow/types.d.ts +26 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +101 -20
- package/dist/lib/exec.d.ts +7 -0
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +6 -3
- package/package.json +1 -1
- package/src/commands/work.ts +51 -4
- package/src/core/harnesses/types.ts +5 -0
- package/src/core/supervisor/plan-read.ts +11 -2
- package/src/core/supervisor/plan-types.ts +11 -0
- package/src/core/supervisor/planning.ts +21 -0
- package/src/core/supervisor/state.ts +68 -11
- package/src/core/work/runner.ts +11 -2
- package/src/core/work/state.ts +125 -3
- package/src/core/workflow/engine.ts +11 -0
- package/src/core/workflow/index.ts +3 -0
- package/src/core/workflow/proof.ts +36 -0
- package/src/core/workflow/spawn-claude.ts +21 -2
- package/src/core/workflow/spawn-codex.ts +17 -2
- package/src/core/workflow/spawn-cursor.ts +18 -2
- package/src/core/workflow/spawn-failure.ts +40 -0
- package/src/core/workflow/types.ts +27 -0
- package/src/core/workflow/workspaces/local-git.ts +112 -22
- package/src/lib/exec.ts +13 -3
package/dist/core/work/state.js
CHANGED
|
@@ -25,6 +25,12 @@ const MAX_EVENTS_BYTES = 4 * 1024 * 1024;
|
|
|
25
25
|
const MAX_EVENT_BYTES = 16 * 1024;
|
|
26
26
|
const MAX_EVENTS = 1_000;
|
|
27
27
|
const FOREIGN_LEASE_STALE_MS = 24 * 60 * 60 * 1_000;
|
|
28
|
+
/** Default ceiling on consecutive uncharged attempts (ADR 0046). Kept low: each
|
|
29
|
+
* one is a real vendor round-trip, so a handful gives a transient outage room
|
|
30
|
+
* to recover before the item stops and names the outside service. There is no
|
|
31
|
+
* backoff between them beyond the cadence of the supervisor and the vendor's own
|
|
32
|
+
* responses, which is why the bound stays small. */
|
|
33
|
+
const DEFAULT_MAX_UNCHARGED_ATTEMPTS = 3;
|
|
28
34
|
export function createWorkItem(input) {
|
|
29
35
|
const coordRoot = resolve(input.coordRoot);
|
|
30
36
|
const id = input.id ?? newWorkId();
|
|
@@ -45,6 +51,12 @@ export function createWorkItem(input) {
|
|
|
45
51
|
if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 100) {
|
|
46
52
|
throw new Error("work maxAttempts must be an integer from 1 to 100");
|
|
47
53
|
}
|
|
54
|
+
const maxUnchargedAttempts = input.maxUnchargedAttempts ?? DEFAULT_MAX_UNCHARGED_ATTEMPTS;
|
|
55
|
+
if (!Number.isSafeInteger(maxUnchargedAttempts) ||
|
|
56
|
+
maxUnchargedAttempts < 1 ||
|
|
57
|
+
maxUnchargedAttempts > 100) {
|
|
58
|
+
throw new Error("work maxUnchargedAttempts must be an integer from 1 to 100");
|
|
59
|
+
}
|
|
48
60
|
const acceptance = (input.acceptance ?? []).map((value, index) => boundedString(value, `work acceptance[${index}]`, MAX_ACCEPTANCE_ITEM));
|
|
49
61
|
if (acceptance.length > MAX_ACCEPTANCE) {
|
|
50
62
|
throw new Error(`work acceptance exceeds ${MAX_ACCEPTANCE} criteria`);
|
|
@@ -59,6 +71,7 @@ export function createWorkItem(input) {
|
|
|
59
71
|
dependencies,
|
|
60
72
|
workflow: { path: workflowPath, sha256: workflowScriptDigest(workflowPath) },
|
|
61
73
|
max_attempts: maxAttempts,
|
|
74
|
+
max_uncharged_attempts: maxUnchargedAttempts,
|
|
62
75
|
source,
|
|
63
76
|
created_at: new Date().toISOString(),
|
|
64
77
|
};
|
|
@@ -178,13 +191,18 @@ function deriveWorkProjection(coordRoot, intent, events, ignoreLease = false) {
|
|
|
178
191
|
const terminal = latestGovernance?.event === "work.accepted" || latestGovernance?.event === "work.cancelled";
|
|
179
192
|
const attemptEvents = events.filter((event) => event.event === "attempt.started");
|
|
180
193
|
const attempts = attemptEvents.map((event, index) => inspectAttempt(coordRoot, event, intent, attemptEvents[index - 1]?.run_id));
|
|
194
|
+
// Charged attempts — not the raw count — are what max_attempts budgets
|
|
195
|
+
// (ADR 0046). An uncharged environment/upstream attempt still increments
|
|
196
|
+
// attempts_used (ordering + next number) but not the budget.
|
|
197
|
+
const chargedAttempts = attempts.filter((attempt) => attempt.uncharged === undefined).length;
|
|
181
198
|
const base = {
|
|
182
199
|
id: intent.id,
|
|
183
200
|
title: intent.title,
|
|
184
201
|
unresolved_dependencies: [],
|
|
185
202
|
attempts,
|
|
186
203
|
attempts_used: attempts.length,
|
|
187
|
-
|
|
204
|
+
charged_attempts: chargedAttempts,
|
|
205
|
+
attempts_remaining: Math.max(0, intent.max_attempts - chargedAttempts),
|
|
188
206
|
latest_run_id: attempts.at(-1)?.run_id,
|
|
189
207
|
approval_id: attempts.at(-1)?.approval_id,
|
|
190
208
|
proof_path: attempts.at(-1)?.proof_path,
|
|
@@ -266,7 +284,7 @@ function deriveWorkProjection(coordRoot, intent, events, ignoreLease = false) {
|
|
|
266
284
|
next_action: "review",
|
|
267
285
|
};
|
|
268
286
|
}
|
|
269
|
-
const attemptsRemaining = intent.max_attempts -
|
|
287
|
+
const attemptsRemaining = intent.max_attempts - chargedAttempts;
|
|
270
288
|
if (latest.status === "journal_unreadable") {
|
|
271
289
|
return {
|
|
272
290
|
...base,
|
|
@@ -275,6 +293,49 @@ function deriveWorkProjection(coordRoot, intent, events, ignoreLease = false) {
|
|
|
275
293
|
next_action: attemptsRemaining > 0 ? "retry" : "none",
|
|
276
294
|
};
|
|
277
295
|
}
|
|
296
|
+
// Uncharged failures (ADR 0046) are handled before the ordinary work-failure
|
|
297
|
+
// path: they did not touch the work, so they neither spent the budget nor
|
|
298
|
+
// signal that retrying the work would help.
|
|
299
|
+
if (latest.status === "failed" && latest.uncharged === "environment") {
|
|
300
|
+
// A missing precondition. The operator chose to STOP the item immediately
|
|
301
|
+
// rather than retry an unchanged environment (ADR 0046); next_action "none"
|
|
302
|
+
// stops the supervisor. A human who fixes the environment can still force a
|
|
303
|
+
// retry — the attempt was uncharged, so the budget is intact.
|
|
304
|
+
return {
|
|
305
|
+
...base,
|
|
306
|
+
state: "blocked",
|
|
307
|
+
reason: environmentBlockedReason(latest),
|
|
308
|
+
next_action: "none",
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (latest.status === "failed" && latest.uncharged === "upstream") {
|
|
312
|
+
// Count trailing consecutive uncharged attempts in the current window. The
|
|
313
|
+
// bound is the only brake on an outage that never ends, so at the limit the
|
|
314
|
+
// item stops and names the outside service — distinct from work-blocked.
|
|
315
|
+
const maxUncharged = intent.max_uncharged_attempts ?? DEFAULT_MAX_UNCHARGED_ATTEMPTS;
|
|
316
|
+
let trailingUncharged = 0;
|
|
317
|
+
for (let index = currentAttempts.length - 1; index >= 0; index--) {
|
|
318
|
+
if (currentAttempts[index].uncharged === undefined)
|
|
319
|
+
break;
|
|
320
|
+
trailingUncharged++;
|
|
321
|
+
}
|
|
322
|
+
if (trailingUncharged >= maxUncharged || attemptsRemaining <= 0) {
|
|
323
|
+
return {
|
|
324
|
+
...base,
|
|
325
|
+
state: "blocked",
|
|
326
|
+
reason: trailingUncharged >= maxUncharged
|
|
327
|
+
? `blocked waiting on an outside service after ${trailingUncharged} consecutive uncharged attempt(s): ${upstreamReason(latest)}`
|
|
328
|
+
: `workflow attempt ${latest.number} was uncharged (${upstreamReason(latest)}) but the work attempt budget is exhausted`,
|
|
329
|
+
next_action: "none",
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
...base,
|
|
334
|
+
state: "blocked",
|
|
335
|
+
reason: `workflow attempt ${latest.number} did not touch the work; an outside service refused: ${upstreamReason(latest)}`,
|
|
336
|
+
next_action: "retry",
|
|
337
|
+
};
|
|
338
|
+
}
|
|
278
339
|
return {
|
|
279
340
|
...base,
|
|
280
341
|
state: "blocked",
|
|
@@ -284,6 +345,31 @@ function deriveWorkProjection(coordRoot, intent, events, ignoreLease = false) {
|
|
|
284
345
|
next_action: attemptsRemaining > 0 ? "retry" : "none",
|
|
285
346
|
};
|
|
286
347
|
}
|
|
348
|
+
/** The failure reason for an uncharged attempt, read from the proof when
|
|
349
|
+
* present. Bounded so a verbose vendor transcript cannot bloat the projection
|
|
350
|
+
* reason (which is persisted verbatim into the reconciliation event). */
|
|
351
|
+
function unchargedProofError(attempt) {
|
|
352
|
+
if (!attempt.proof_path)
|
|
353
|
+
return undefined;
|
|
354
|
+
try {
|
|
355
|
+
const proof = parseObject(readFileSync(attempt.proof_path, "utf8"), "workflow proof");
|
|
356
|
+
const run = proof.run;
|
|
357
|
+
const error = run && typeof run === "object" ? run.error : undefined;
|
|
358
|
+
return typeof error === "string" && error.trim() ? boundedJournalError(error) : undefined;
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function environmentBlockedReason(attempt) {
|
|
365
|
+
const detail = unchargedProofError(attempt);
|
|
366
|
+
return detail
|
|
367
|
+
? `workflow attempt ${attempt.number} could not start; a required precondition is missing: ${detail}`
|
|
368
|
+
: `workflow attempt ${attempt.number} could not start; a required precondition is missing`;
|
|
369
|
+
}
|
|
370
|
+
function upstreamReason(attempt) {
|
|
371
|
+
return unchargedProofError(attempt) ?? "the vendor was reached and refused";
|
|
372
|
+
}
|
|
287
373
|
function inspectAttempt(coordRoot, event, intent, priorRunId) {
|
|
288
374
|
const runId = event.run_id;
|
|
289
375
|
const attempt = {
|
|
@@ -334,6 +420,13 @@ function inspectAttempt(coordRoot, event, intent, priorRunId) {
|
|
|
334
420
|
proof.acceptance.summary.unknown === 0
|
|
335
421
|
? "succeeded"
|
|
336
422
|
: "failed";
|
|
423
|
+
// A failed attempt the run classified as uninformative about the work is
|
|
424
|
+
// uncharged (ADR 0046). Absent class ⇒ charged, as before. Read only for a
|
|
425
|
+
// failed attempt: a succeeded run never carries a class.
|
|
426
|
+
if (attempt.status === "failed" &&
|
|
427
|
+
(proof.run.class === "environment" || proof.run.class === "upstream")) {
|
|
428
|
+
attempt.uncharged = proof.run.class;
|
|
429
|
+
}
|
|
337
430
|
return attempt;
|
|
338
431
|
}
|
|
339
432
|
const journalPath = workflowJournalPath(coordRoot, runId);
|
|
@@ -596,7 +689,13 @@ function validateWorkIntent(intent, workId) {
|
|
|
596
689
|
!/^[a-f0-9]{64}$/.test(intent.workflow.sha256) ||
|
|
597
690
|
!Number.isSafeInteger(intent.max_attempts) ||
|
|
598
691
|
intent.max_attempts < 1 ||
|
|
599
|
-
intent.max_attempts > 100
|
|
692
|
+
intent.max_attempts > 100 ||
|
|
693
|
+
// Optional for back-compat: absent on pre-ADR-0046 intents. When present it
|
|
694
|
+
// must be a valid bound.
|
|
695
|
+
(intent.max_uncharged_attempts !== undefined &&
|
|
696
|
+
(!Number.isSafeInteger(intent.max_uncharged_attempts) ||
|
|
697
|
+
intent.max_uncharged_attempts < 1 ||
|
|
698
|
+
intent.max_uncharged_attempts > 100))) {
|
|
600
699
|
throw new Error(`work intent ${workId} has an unsupported or mismatched schema`);
|
|
601
700
|
}
|
|
602
701
|
boundedString(intent.title, "work title", MAX_TITLE);
|
|
@@ -774,6 +774,12 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
|
|
|
774
774
|
costUsd += last.costUsd ?? 0;
|
|
775
775
|
if (!last.ok) {
|
|
776
776
|
journal("agent.attempt_failed", { id, attempt, error: last.error });
|
|
777
|
+
// ADR 0046: an environment failure (the binary was absent) cannot be
|
|
778
|
+
// helped by retrying an unchanged environment, so stop the in-agent
|
|
779
|
+
// retry too — not just the outer attempt/replan budget. An upstream
|
|
780
|
+
// refusal keeps retrying here: the vendor may recover mid-loop.
|
|
781
|
+
if (last.class === "environment")
|
|
782
|
+
break;
|
|
777
783
|
continue; // spawn-level failure: retry with the original prompt
|
|
778
784
|
}
|
|
779
785
|
if (!agentOpts.schema) {
|
|
@@ -833,6 +839,13 @@ async function executeWorkflow(scriptPath, absScript, opts, resumeState, workCon
|
|
|
833
839
|
agentCostUsd > 0 || last?.costUsd !== undefined ? agentCostUsd : undefined;
|
|
834
840
|
agentProof.session_id = last?.sessionId;
|
|
835
841
|
agentProof.error = reason;
|
|
842
|
+
// Carry the spawn class (environment/upstream) onto the proof only when the
|
|
843
|
+
// final outcome was a spawn failure. A schema failure after a spawn that
|
|
844
|
+
// reached the model is a work failure — left unclassed (charged). An
|
|
845
|
+
// earlier attempt that transiently failed and then succeeded returned
|
|
846
|
+
// above, so this only fires when the agent genuinely failed.
|
|
847
|
+
if (last && !last.ok && last.class)
|
|
848
|
+
agentProof.class = last.class;
|
|
836
849
|
journal("agent.failed", { id, error: reason });
|
|
837
850
|
throw new Error(`agent ${proofLabel}: ${reason}`);
|
|
838
851
|
}
|
|
@@ -2,10 +2,11 @@ export type { CreateWorkflowApprovalInput, ResolveWorkflowApprovalInput, Workflo
|
|
|
2
2
|
export { assertWorkflowRunId, createWorkflowApproval, listWorkflowApprovals, readWorkflowApproval, renderWorkflowApproval, resolveWorkflowApproval, WORKFLOW_APPROVAL_SCHEMA_VERSION, workflowApprovalId, } from "./approvals.js";
|
|
3
3
|
export { freezeWorkflowAttemptContext, isCanonicalWorkflowAttemptContext, normalizeWorkflowAttemptContext, } from "./attempt-context.js";
|
|
4
4
|
export { runWorkflow, WorkflowParkedError, WorkflowRunError } from "./engine.js";
|
|
5
|
-
export { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
|
|
5
|
+
export { buildWorkflowProof, createEvidenceRecord, deriveRunFailureClass, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
|
|
6
6
|
export type { WorkflowRunManifest } from "./run-state.js";
|
|
7
7
|
export { acquireWorkflowResumeLease, assertWorkflowRunResumable, assertWorkflowScriptUnchanged, readWorkflowRunManifest, WORKFLOW_RUN_MANIFEST_SCHEMA_VERSION, workflowRunManifestPath, workflowScriptDigest, writeWorkflowRunManifest, } from "./run-state.js";
|
|
8
|
-
export
|
|
8
|
+
export { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
9
|
+
export type { AcceptanceCriterion, AcceptanceResult, AcceptanceStatus, AcceptanceSummary, AgentOpts, EngineOpts, EvidenceKind, EvidenceSource, EvidenceStatus, HarnessEvidenceCapability, HarnessEvidenceCoverage, HarnessName, ResultDigest, RunReport, Spawner, SpawnFailureClass, 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
10
|
export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
|
|
10
11
|
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
12
|
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";
|
|
@@ -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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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { assertWorkflowRunId, createWorkflowApproval, listWorkflowApprovals, readWorkflowApproval, renderWorkflowApproval, resolveWorkflowApproval, WORKFLOW_APPROVAL_SCHEMA_VERSION, workflowApprovalId, } from "./approvals.js";
|
|
2
2
|
export { freezeWorkflowAttemptContext, isCanonicalWorkflowAttemptContext, normalizeWorkflowAttemptContext, } from "./attempt-context.js";
|
|
3
3
|
export { runWorkflow, WorkflowParkedError, WorkflowRunError } from "./engine.js";
|
|
4
|
-
export { buildWorkflowProof, createEvidenceRecord, digestResult, normalizeWorkflowMeta, readWorkflowProof, renderWorkflowProof, rollupAcceptance, writeWorkflowProof, } from "./proof.js";
|
|
4
|
+
export { buildWorkflowProof, createEvidenceRecord, deriveRunFailureClass, 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
|
+
export { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
6
7
|
export { WORKFLOW_ATTEMPT_CONTEXT_SCHEMA_VERSION, WORKFLOW_PROOF_SCHEMA_VERSION, WORKFLOW_WORK_CONTEXT_SCHEMA_VERSION, } from "./types.js";
|
|
7
8
|
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";
|
|
@@ -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, WorkflowSandboxProjectionEvidence, WorkflowWorkContext } from "./types.js";
|
|
3
|
+
import type { AcceptanceCriterion, AcceptanceResult, AcceptanceSummary, HarnessAttestationCitation, HarnessEvidenceCapability, ResultDigest, SpawnFailureClass, 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;
|
|
@@ -54,6 +54,23 @@ export declare function rollupAcceptance(criteria: AcceptanceCriterion[], eviden
|
|
|
54
54
|
summary: AcceptanceSummary;
|
|
55
55
|
};
|
|
56
56
|
export declare function digestResult(value: unknown, kind?: "text" | "json"): ResultDigest;
|
|
57
|
+
/**
|
|
58
|
+
* The run-level failure class (ADR 0046), derived from the agents rather than a
|
|
59
|
+
* single terminal throw so it survives a script's `parallel()` swallowing the
|
|
60
|
+
* rejection — a swallowed agent's proof is still recorded with its class.
|
|
61
|
+
*
|
|
62
|
+
* Rules, in order, all in service of "default to charging":
|
|
63
|
+
* 1. A succeeded run is never classed (there is nothing uncharged about it).
|
|
64
|
+
* 2. If ANY agent produced a result (succeeded or replayed from cache) the
|
|
65
|
+
* attempt was informative about the work — charge it. This also keeps a
|
|
66
|
+
* resumed run whose earlier segment did real work from being written off by
|
|
67
|
+
* a later environment failure.
|
|
68
|
+
* 3. Otherwise, among the failed agents, environment wins over upstream: a
|
|
69
|
+
* missing binary means nothing ran at all, and it is the operator-chosen
|
|
70
|
+
* hard stop.
|
|
71
|
+
* 4. Anything else is undefined ⇒ a charged work failure, exactly as today.
|
|
72
|
+
*/
|
|
73
|
+
export declare function deriveRunFailureClass(status: "succeeded" | "failed", agents: readonly Pick<WorkflowAgentProof, "status" | "class">[]): SpawnFailureClass | undefined;
|
|
57
74
|
export declare function buildWorkflowProof(input: BuildWorkflowProofInput): WorkflowProof;
|
|
58
75
|
export declare function writeWorkflowProof(path: string, proof: WorkflowProof): void;
|
|
59
76
|
export declare function readWorkflowProof(coordRoot: string, runId: string): WorkflowProof;
|
|
@@ -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":"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"}
|
|
@@ -108,6 +108,35 @@ export function digestResult(value, kind) {
|
|
|
108
108
|
bytes: Buffer.byteLength(serialized),
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The run-level failure class (ADR 0046), derived from the agents rather than a
|
|
113
|
+
* single terminal throw so it survives a script's `parallel()` swallowing the
|
|
114
|
+
* rejection — a swallowed agent's proof is still recorded with its class.
|
|
115
|
+
*
|
|
116
|
+
* Rules, in order, all in service of "default to charging":
|
|
117
|
+
* 1. A succeeded run is never classed (there is nothing uncharged about it).
|
|
118
|
+
* 2. If ANY agent produced a result (succeeded or replayed from cache) the
|
|
119
|
+
* attempt was informative about the work — charge it. This also keeps a
|
|
120
|
+
* resumed run whose earlier segment did real work from being written off by
|
|
121
|
+
* a later environment failure.
|
|
122
|
+
* 3. Otherwise, among the failed agents, environment wins over upstream: a
|
|
123
|
+
* missing binary means nothing ran at all, and it is the operator-chosen
|
|
124
|
+
* hard stop.
|
|
125
|
+
* 4. Anything else is undefined ⇒ a charged work failure, exactly as today.
|
|
126
|
+
*/
|
|
127
|
+
export function deriveRunFailureClass(status, agents) {
|
|
128
|
+
if (status !== "failed")
|
|
129
|
+
return undefined;
|
|
130
|
+
if (agents.some((agent) => agent.status === "succeeded" || agent.status === "cached")) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const failed = agents.filter((agent) => agent.status === "failed");
|
|
134
|
+
if (failed.some((agent) => agent.class === "environment"))
|
|
135
|
+
return "environment";
|
|
136
|
+
if (failed.some((agent) => agent.class === "upstream"))
|
|
137
|
+
return "upstream";
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
111
140
|
export function buildWorkflowProof(input) {
|
|
112
141
|
const acceptance = rollupAcceptance(input.meta.acceptance, input.evidence);
|
|
113
142
|
const repository = buildRepoEvidence(input.before, input.after);
|
|
@@ -121,6 +150,7 @@ export function buildWorkflowProof(input) {
|
|
|
121
150
|
}));
|
|
122
151
|
const harnesses = buildHarnessCoverage(agents, input.harnessEvidence, input.harnessAttestations);
|
|
123
152
|
const unknowns = buildUnknowns(agents, harnesses, repository);
|
|
153
|
+
const runClass = deriveRunFailureClass(input.status, agents);
|
|
124
154
|
const journal = readFileSync(input.journalPath);
|
|
125
155
|
return {
|
|
126
156
|
schema_version: WORKFLOW_PROOF_SCHEMA_VERSION,
|
|
@@ -137,6 +167,7 @@ export function buildWorkflowProof(input) {
|
|
|
137
167
|
objective: input.meta.objective,
|
|
138
168
|
error: clippedOptional(input.error, MAX_SUMMARY_CHARS),
|
|
139
169
|
result: input.result === undefined ? undefined : digestResult(input.result),
|
|
170
|
+
...(runClass ? { class: runClass } : {}),
|
|
140
171
|
},
|
|
141
172
|
acceptance,
|
|
142
173
|
agents,
|
|
@@ -212,6 +243,9 @@ export function readWorkflowProof(coordRoot, runId) {
|
|
|
212
243
|
}
|
|
213
244
|
if (proof.schema_version !== WORKFLOW_PROOF_SCHEMA_VERSION ||
|
|
214
245
|
proof.run?.id !== runId ||
|
|
246
|
+
(proof.run.class !== undefined &&
|
|
247
|
+
proof.run.class !== "environment" &&
|
|
248
|
+
proof.run.class !== "upstream") ||
|
|
215
249
|
(proof.run.work_context !== undefined &&
|
|
216
250
|
(!proof.run.work_item_id ||
|
|
217
251
|
proof.run.work_context.id !== proof.run.work_item_id ||
|
|
@@ -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;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,
|
|
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,CA2ExE;AAED,eAAO,MAAM,iBAAiB,EAAE,OAc/B,CAAC"}
|
|
@@ -22,7 +22,7 @@ import { builtinHarnessProfile, validateHarnessEffort } from "../harnesses/profi
|
|
|
22
22
|
import { buildChildEnv } from "./child-env.js";
|
|
23
23
|
import { notFoundError } from "./harnesses.js";
|
|
24
24
|
import { resolveSandboxProjection } from "./sandbox-projection.js";
|
|
25
|
-
import { vendorFailureText } from "./spawn-failure.js";
|
|
25
|
+
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
26
26
|
export function buildClaudeInvocation(req) {
|
|
27
27
|
validateHarnessEffort("claude-code", req.effort);
|
|
28
28
|
if (req.filesystemPolicy) {
|
|
@@ -53,7 +53,20 @@ export function normalizeClaudeResult(raw) {
|
|
|
53
53
|
error: `claude timed out after ${raw.durationMs}ms and was killed`,
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
+
// Structural environment signal: the binary was never there (spawned directly,
|
|
57
|
+
// so a missing binary surfaces as ENOENT). Uncharged and not retried.
|
|
58
|
+
if (raw.spawnErrno === "ENOENT") {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
text: "",
|
|
62
|
+
durationMs: raw.durationMs,
|
|
63
|
+
error: notFoundError("claude-code"),
|
|
64
|
+
class: "environment",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
56
67
|
if (raw.exitCode === 127) {
|
|
68
|
+
// A bare 127 with no errno is a shell/vendor 127, indistinguishable from a
|
|
69
|
+
// legitimate one — charged as work rather than classed environment.
|
|
57
70
|
return {
|
|
58
71
|
ok: false,
|
|
59
72
|
text: "",
|
|
@@ -62,11 +75,13 @@ export function normalizeClaudeResult(raw) {
|
|
|
62
75
|
};
|
|
63
76
|
}
|
|
64
77
|
if (raw.exitCode !== 0) {
|
|
78
|
+
const failureText = vendorFailureText(raw);
|
|
65
79
|
return {
|
|
66
80
|
ok: false,
|
|
67
81
|
text: "",
|
|
68
82
|
durationMs: raw.durationMs,
|
|
69
|
-
error: `claude exited ${raw.exitCode}: ${
|
|
83
|
+
error: `claude exited ${raw.exitCode}: ${failureText}`,
|
|
84
|
+
...(isUpstreamFailureText(failureText) ? { class: "upstream" } : {}),
|
|
70
85
|
};
|
|
71
86
|
}
|
|
72
87
|
let envelope;
|
|
@@ -82,6 +97,7 @@ export function normalizeClaudeResult(raw) {
|
|
|
82
97
|
};
|
|
83
98
|
}
|
|
84
99
|
if (envelope.is_error) {
|
|
100
|
+
const envelopeError = `${envelope.subtype ?? ""} ${(envelope.errors ?? []).join("; ")} ${String(envelope.result ?? "")}`;
|
|
85
101
|
return {
|
|
86
102
|
ok: false,
|
|
87
103
|
text: String(envelope.result ?? ""),
|
|
@@ -89,6 +105,7 @@ export function normalizeClaudeResult(raw) {
|
|
|
89
105
|
costUsd: envelope.total_cost_usd,
|
|
90
106
|
durationMs: raw.durationMs,
|
|
91
107
|
error: `harness error (${envelope.subtype ?? "unknown"}): ${(envelope.errors ?? []).join("; ") || "see envelope"}`,
|
|
108
|
+
...(isUpstreamFailureText(envelopeError) ? { class: "upstream" } : {}),
|
|
92
109
|
};
|
|
93
110
|
}
|
|
94
111
|
return {
|
|
@@ -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;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,
|
|
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,CAwCvE;AAED,eAAO,MAAM,YAAY,EAAE,OA2B1B,CAAC"}
|
|
@@ -23,7 +23,7 @@ import { builtinHarnessProfile, validateHarnessEffort } from "../harnesses/profi
|
|
|
23
23
|
import { buildChildEnv } from "./child-env.js";
|
|
24
24
|
import { notFoundError } from "./harnesses.js";
|
|
25
25
|
import { resolveSandboxProjection } from "./sandbox-projection.js";
|
|
26
|
-
import { vendorFailureText } from "./spawn-failure.js";
|
|
26
|
+
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
27
27
|
export function buildCodexInvocation(req, resultFile) {
|
|
28
28
|
validateHarnessEffort("codex", req.effort);
|
|
29
29
|
if (!resultFile)
|
|
@@ -60,15 +60,30 @@ export function normalizeCodexResult(raw) {
|
|
|
60
60
|
error: `codex timed out after ${raw.durationMs}ms and was killed`,
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
+
// Structural environment signal: the binary was never there (spawned directly,
|
|
64
|
+
// so a missing binary surfaces as ENOENT). Uncharged and not retried.
|
|
65
|
+
if (raw.spawnErrno === "ENOENT") {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
text: "",
|
|
69
|
+
durationMs: raw.durationMs,
|
|
70
|
+
error: notFoundError("codex"),
|
|
71
|
+
class: "environment",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
63
74
|
if (raw.exitCode === 127) {
|
|
75
|
+
// A bare 127 with no errno is a shell/vendor 127, indistinguishable from a
|
|
76
|
+
// legitimate one — charged as work rather than classed environment.
|
|
64
77
|
return { ok: false, text: "", durationMs: raw.durationMs, error: notFoundError("codex") };
|
|
65
78
|
}
|
|
66
79
|
if (raw.exitCode !== 0) {
|
|
80
|
+
const failureText = vendorFailureText(raw);
|
|
67
81
|
return {
|
|
68
82
|
ok: false,
|
|
69
83
|
text: "",
|
|
70
84
|
durationMs: raw.durationMs,
|
|
71
|
-
error: `codex exited ${raw.exitCode}: ${
|
|
85
|
+
error: `codex exited ${raw.exitCode}: ${failureText}`,
|
|
86
|
+
...(isUpstreamFailureText(failureText) ? { class: "upstream" } : {}),
|
|
72
87
|
};
|
|
73
88
|
}
|
|
74
89
|
return {
|
|
@@ -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;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,
|
|
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,CAqDxE;AAED,eAAO,MAAM,aAAa,EAAE,OAe3B,CAAC"}
|
|
@@ -23,7 +23,7 @@ import { builtinHarnessProfile, validateHarnessEffort } from "../harnesses/profi
|
|
|
23
23
|
import { buildChildEnv } from "./child-env.js";
|
|
24
24
|
import { notFoundError } from "./harnesses.js";
|
|
25
25
|
import { resolveSandboxProjection } from "./sandbox-projection.js";
|
|
26
|
-
import { vendorFailureText } from "./spawn-failure.js";
|
|
26
|
+
import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.js";
|
|
27
27
|
/** Exported for unit tests (no live binary to test against). */
|
|
28
28
|
export function parseCursorOutput(stdout) {
|
|
29
29
|
try {
|
|
@@ -63,15 +63,30 @@ export function normalizeCursorResult(raw) {
|
|
|
63
63
|
error: `cursor timed out after ${raw.durationMs}ms and was killed`,
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
+
// Structural environment signal: the binary was never there (spawned directly,
|
|
67
|
+
// so a missing binary surfaces as ENOENT). Uncharged and not retried.
|
|
68
|
+
if (raw.spawnErrno === "ENOENT") {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
text: "",
|
|
72
|
+
durationMs: raw.durationMs,
|
|
73
|
+
error: notFoundError("cursor"),
|
|
74
|
+
class: "environment",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
66
77
|
if (raw.exitCode === 127) {
|
|
78
|
+
// A bare 127 with no errno is a shell/vendor 127, indistinguishable from a
|
|
79
|
+
// legitimate one — charged as work rather than classed environment.
|
|
67
80
|
return { ok: false, text: "", durationMs: raw.durationMs, error: notFoundError("cursor") };
|
|
68
81
|
}
|
|
69
82
|
if (raw.exitCode !== 0) {
|
|
83
|
+
const failureText = vendorFailureText(raw);
|
|
70
84
|
return {
|
|
71
85
|
ok: false,
|
|
72
86
|
text: "",
|
|
73
87
|
durationMs: raw.durationMs,
|
|
74
|
-
error: `cursor-agent exited ${raw.exitCode}: ${
|
|
88
|
+
error: `cursor-agent exited ${raw.exitCode}: ${failureText}`,
|
|
89
|
+
...(isUpstreamFailureText(failureText) ? { class: "upstream" } : {}),
|
|
75
90
|
};
|
|
76
91
|
}
|
|
77
92
|
const parsed = parseCursorOutput(raw.stdout);
|
|
@@ -82,6 +97,7 @@ export function normalizeCursorResult(raw) {
|
|
|
82
97
|
sessionId: parsed.sessionId,
|
|
83
98
|
durationMs: raw.durationMs,
|
|
84
99
|
error: `cursor-agent reported is_error: ${parsed.text.slice(0, 300)}`,
|
|
100
|
+
...(isUpstreamFailureText(parsed.text) ? { class: "upstream" } : {}),
|
|
85
101
|
};
|
|
86
102
|
}
|
|
87
103
|
return {
|
|
@@ -15,4 +15,18 @@ export declare function vendorFailureText(raw: {
|
|
|
15
15
|
stdout?: string;
|
|
16
16
|
stderr?: string;
|
|
17
17
|
}, maxChars?: number): string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether failure text names an UPSTREAM refusal — the vendor was reached and
|
|
20
|
+
* refused (a 5xx/429 status or circuit-open wording), as opposed to a work
|
|
21
|
+
* failure (the model ran and produced a wrong or incomplete result).
|
|
22
|
+
*
|
|
23
|
+
* There is no structural upstream signal the way ENOENT structurally marks a
|
|
24
|
+
* missing binary, so this is the one text match in the classifier. It is kept
|
|
25
|
+
* SHORT and documented on purpose: a per-vendor regex zoo would rot, and a
|
|
26
|
+
* false positive here wrongly withholds a charge, so both the phrase list and
|
|
27
|
+
* the numeric match stay tight — the number must sit in HTTP-status context, not
|
|
28
|
+
* merely fall in the 429/5xx range. Anything it does not match falls through to
|
|
29
|
+
* a charged work failure.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isUpstreamFailureText(text: string): boolean;
|
|
18
32
|
//# sourceMappingURL=spawn-failure.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-failure.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,mEAAmE;AACnE,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EACzC,QAAQ,SAAoB,GAC3B,MAAM,CAQR"}
|
|
1
|
+
{"version":3,"file":"spawn-failure.d.ts","sourceRoot":"","sources":["../../../src/core/workflow/spawn-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,mEAAmE;AACnE,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EACzC,QAAQ,SAAoB,GAC3B,MAAM,CAQR;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D"}
|
|
@@ -22,3 +22,37 @@ export function vendorFailureText(raw, maxChars = DEFAULT_MAX_CHARS) {
|
|
|
22
22
|
const combined = parts.join("\n");
|
|
23
23
|
return combined.length > maxChars ? `…${combined.slice(-maxChars)}` : combined;
|
|
24
24
|
}
|
|
25
|
+
// A 429 or 5xx status, but only where it sits in HTTP-status CONTEXT — not any
|
|
26
|
+
// digits that merely happen to fall in that range. A bare-number match treats a
|
|
27
|
+
// line number ("at line 500"), an item count ("got 429 items"), or a duration
|
|
28
|
+
// ("after 502 seconds") as an upstream refusal, which wrongly withholds a charge
|
|
29
|
+
// for a failure that was entirely our code. Three recognized shapes:
|
|
30
|
+
// 1. after an "HTTP" label: "HTTP 429", "HTTP/1.1 503 Service Unavailable"
|
|
31
|
+
// 2. after a "status" label: "status 500", "status: 429", "status code 502"
|
|
32
|
+
// 3. a bare status line: "503 Service Unavailable" (code at line start,
|
|
33
|
+
// then a reason phrase) — the classic "<code> <Reason-Phrase>" shape.
|
|
34
|
+
// The trailing `(?![0-9])` and the label/line-start anchors also keep a status
|
|
35
|
+
// embedded in a longer number ("4295"/"5031") out. All 5xx are server-side; 429
|
|
36
|
+
// is the only 4xx that means "reached and refused, retry later".
|
|
37
|
+
const STATUS = "(?:429|5[0-9]{2})";
|
|
38
|
+
const UPSTREAM_STATUS = new RegExp(`(?:\\bHTTP\\b[/0-9. ]*|\\bstatus(?:\\s*code)?\\b[\\s:=]*)${STATUS}(?![0-9])` +
|
|
39
|
+
`|(?:^|\\n)\\s*${STATUS}\\s+[A-Za-z]`, "i");
|
|
40
|
+
// Explicit vendor wording for the same conditions when a numeric status is
|
|
41
|
+
// absent. Deliberately short — see isUpstreamFailureText.
|
|
42
|
+
const UPSTREAM_PHRASES = /circuit[ _-]?open|service unavailable|too many requests|rate[ _-]?limit|overloaded|bad gateway|gateway time-?out|internal server error/i;
|
|
43
|
+
/**
|
|
44
|
+
* Whether failure text names an UPSTREAM refusal — the vendor was reached and
|
|
45
|
+
* refused (a 5xx/429 status or circuit-open wording), as opposed to a work
|
|
46
|
+
* failure (the model ran and produced a wrong or incomplete result).
|
|
47
|
+
*
|
|
48
|
+
* There is no structural upstream signal the way ENOENT structurally marks a
|
|
49
|
+
* missing binary, so this is the one text match in the classifier. It is kept
|
|
50
|
+
* SHORT and documented on purpose: a per-vendor regex zoo would rot, and a
|
|
51
|
+
* false positive here wrongly withholds a charge, so both the phrase list and
|
|
52
|
+
* the numeric match stay tight — the number must sit in HTTP-status context, not
|
|
53
|
+
* merely fall in the 429/5xx range. Anything it does not match falls through to
|
|
54
|
+
* a charged work failure.
|
|
55
|
+
*/
|
|
56
|
+
export function isUpstreamFailureText(text) {
|
|
57
|
+
return UPSTREAM_STATUS.test(text) || UPSTREAM_PHRASES.test(text);
|
|
58
|
+
}
|