@fitlab-ai/agent-infra 0.11.2 → 0.11.3
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/bin/internal-cli.js +7 -2
- package/dist/lib/agent-clients/adapter.js +3 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
- package/dist/lib/agent-clients/adapters/codex.js +10 -0
- package/dist/lib/internal/manual-validation.js +22 -27
- package/dist/lib/internal/sandbox-control.js +1 -43
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +6 -2
- package/dist/lib/platform/issue-comments.js +2 -31
- package/dist/lib/platform/pr-summary.js +7 -2
- package/dist/lib/sandbox/control/client.js +3 -44
- package/dist/lib/sandbox/control/executor.js +14 -79
- package/dist/lib/sandbox/control/finalization-response.js +3 -5
- package/dist/lib/sandbox/control/protocol.js +5 -64
- package/dist/lib/sandbox/control/server.js +29 -206
- package/dist/lib/sandbox/control/state.js +1 -7
- package/dist/lib/sandbox/control/task-view.js +1 -2
- package/dist/lib/task/control-authority.js +34 -36
- package/dist/lib/task/control-recovery.js +3 -42
- package/dist/lib/task/delegation-receipts.js +2 -2
- package/dist/lib/task/finalization-artifacts.js +1 -3
- package/dist/lib/task/finalization-state.js +1 -1
- package/dist/lib/task/finalization.js +53 -126
- package/dist/lib/task/lifecycle.js +1 -1
- package/dist/lib/task/manual-validation-receipt.js +8 -3
- package/dist/lib/task/orchestration.js +12 -6
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +46 -0
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
- package/lib/agent-clients/adapters/codex.ts +10 -0
- package/lib/internal/manual-validation.ts +22 -26
- package/lib/internal/sandbox-control.ts +0 -42
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +7 -4
- package/lib/platform/issue-comments.ts +1 -35
- package/lib/platform/pr-summary.ts +10 -2
- package/lib/sandbox/control/client.ts +5 -68
- package/lib/sandbox/control/executor.ts +14 -81
- package/lib/sandbox/control/finalization-response.ts +3 -5
- package/lib/sandbox/control/protocol.ts +7 -83
- package/lib/sandbox/control/server.ts +30 -220
- package/lib/sandbox/control/state.ts +1 -9
- package/lib/sandbox/control/task-view.ts +1 -2
- package/lib/task/control-authority.ts +38 -47
- package/lib/task/control-recovery.ts +3 -43
- package/lib/task/delegation-receipts.ts +2 -2
- package/lib/task/finalization-artifacts.ts +1 -3
- package/lib/task/finalization-state.ts +1 -1
- package/lib/task/finalization.ts +57 -121
- package/lib/task/lifecycle.ts +2 -2
- package/lib/task/manual-validation-receipt.ts +8 -2
- package/lib/task/orchestration.ts +30 -19
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.en.md +6 -7
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +6 -7
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
- package/dist/lib/sandbox/control/task-workflow.js +0 -74
- package/dist/lib/sandbox/control/workflow-executor.js +0 -142
- package/dist/lib/task/lifecycle-recovery.js +0 -549
- package/dist/lib/task/recovery-warning.js +0 -19
- package/dist/lib/task/workflow-command.js +0 -21
- package/dist/lib/task/workflow-dispatch.js +0 -213
- package/lib/sandbox/control/task-workflow.ts +0 -95
- package/lib/sandbox/control/workflow-executor.ts +0 -156
- package/lib/task/lifecycle-recovery.ts +0 -691
- package/lib/task/recovery-warning.ts +0 -21
- package/lib/task/workflow-command.ts +0 -22
- package/lib/task/workflow-dispatch.ts +0 -214
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { isCompletionEvidence } from './orchestration.js';
|
|
3
|
-
import { TASK_WORKFLOW_OPERATIONS } from './workflow-command.js';
|
|
4
|
-
import { sameRecoveryWarning } from './recovery-warning.js';
|
|
5
2
|
/** Normalize the command result once at the control transport boundary. */
|
|
6
3
|
export function parseControlOutput(output) {
|
|
7
4
|
if (output === null)
|
|
@@ -20,20 +17,10 @@ export const SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES = Object.freeze([
|
|
|
20
17
|
'completed', 'evidence-written', 'publish-authorized'
|
|
21
18
|
]);
|
|
22
19
|
const LIFECYCLE_INTENTS = ['block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'];
|
|
23
|
-
const ORCHESTRATION_INTENTS = [
|
|
24
|
-
'begin-or-resume', 'route.read', 'route.clean-completion', 'status', 'prepare', 'dispatch',
|
|
25
|
-
'await-activation', 'recover-prepared', 'hook-start', 'hook-stop', 'advance', 'pause'
|
|
26
|
-
];
|
|
27
20
|
export const SANDBOX_CONTROL_RECOVERY_OPERATIONS = Object.freeze([
|
|
28
21
|
...LIFECYCLE_INTENTS.map((intent) => ({ family: 'task-lifecycle', intent, class: 'lifecycle-mutation' })),
|
|
29
22
|
{ family: 'task-finalization', intent: 'complete', class: 'finalization' },
|
|
30
|
-
...ORCHESTRATION_INTENTS.map((intent) => ({
|
|
31
|
-
family: 'task-orchestration',
|
|
32
|
-
intent,
|
|
33
|
-
class: intent === 'route.clean-completion' ? 'route.clean-completion' : intent === 'route.read' || intent === 'status' ? 'read-only' : 'orchestration'
|
|
34
|
-
})),
|
|
35
23
|
{ family: 'task-create', intent: 'create', class: 'task-create' },
|
|
36
|
-
...TASK_WORKFLOW_OPERATIONS.map((intent) => ({ family: 'task-workflow', intent, class: intent === 'artifact-inspect' || intent === 'decision-next-id' ? 'read-only' : 'workflow' })),
|
|
37
24
|
...['open', 'close', 'verify'].map((intent) => ({ family: 'codex-controller', intent, class: 'codex-controller' }))
|
|
38
25
|
]);
|
|
39
26
|
export function digestControlRecoveryIntent(family, intent) {
|
|
@@ -53,30 +40,8 @@ function domainEvidenceMatches(operation, result, domain) {
|
|
|
53
40
|
return false;
|
|
54
41
|
if (domain.consistent !== true)
|
|
55
42
|
return false;
|
|
56
|
-
if (operation.
|
|
57
|
-
|
|
58
|
-
const snapshot = domain.snapshot;
|
|
59
|
-
const lastReviewedCommit = domain.lastReviewedCommit;
|
|
60
|
-
return result.status === 'completed'
|
|
61
|
-
&& domain.status === 'completed'
|
|
62
|
-
&& domain.pendingDelegation === null
|
|
63
|
-
&& isCompletionEvidence(completion)
|
|
64
|
-
&& snapshot?.head === completion.head
|
|
65
|
-
&& snapshot.headTree === completion.headTree
|
|
66
|
-
&& snapshot.worktreeTree === completion.worktreeTree
|
|
67
|
-
&& lastReviewedCommit === completion.lastReviewedCommit;
|
|
68
|
-
}
|
|
69
|
-
if (operation.family === 'task-lifecycle' && operation.intent === 'recover-started') {
|
|
70
|
-
const retryRequired = domain.recoveryState === 'retry-required'
|
|
71
|
-
&& sameRecoveryWarning(domain.warning, result.warning);
|
|
72
|
-
return result.targetState === 'active'
|
|
73
|
-
&& ['applied', 'no-op'].includes(String(result.status))
|
|
74
|
-
&& domain.recovery === true
|
|
75
|
-
&& domain.targetState === 'active'
|
|
76
|
-
&& (domain.recoveryState === 'released' || retryRequired);
|
|
77
|
-
}
|
|
78
|
-
if (operation.class === 'read-only')
|
|
79
|
-
return result.changed === false && domain.snapshotValid === true;
|
|
43
|
+
if (operation.family === 'task-lifecycle' && operation.intent === 'recover-started')
|
|
44
|
+
return false;
|
|
80
45
|
return true;
|
|
81
46
|
}
|
|
82
47
|
export function classifySandboxControlRecovery(input) {
|
|
@@ -127,11 +92,7 @@ export function classifySandboxControlRecovery(input) {
|
|
|
127
92
|
return {
|
|
128
93
|
outcome: 'success',
|
|
129
94
|
responseReconstructable: true,
|
|
130
|
-
reasonCode:
|
|
131
|
-
? 'RECOVERY_ROUTE_COMPLETION_NOOP'
|
|
132
|
-
: input.operation.intent === 'recover-started' && input.domain?.recoveryState === 'retry-required'
|
|
133
|
-
? 'RECOVERY_RELEASE_RETRY_REQUIRED'
|
|
134
|
-
: 'RECOVERY_TERMINAL_AND_DOMAIN_MATCH'
|
|
95
|
+
reasonCode: 'RECOVERY_TERMINAL_AND_DOMAIN_MATCH'
|
|
135
96
|
};
|
|
136
97
|
}
|
|
137
98
|
export function operationRecoveryBinding(requestId, generation, taskId, family, intent) {
|
|
@@ -149,7 +149,7 @@ function hasStatusBoundEvidence(receipt) {
|
|
|
149
149
|
&& host !== undefined
|
|
150
150
|
&& Number.isSafeInteger(host.stopRevision)
|
|
151
151
|
&& host.stopRevision > host.startRevision
|
|
152
|
-
&& host.consumer ===
|
|
152
|
+
&& host.consumer === receipt.id
|
|
153
153
|
&& exactText(host.consumedAt);
|
|
154
154
|
}
|
|
155
155
|
if (!exactText(receipt.agent)
|
|
@@ -405,7 +405,7 @@ function abortActivatedDelegation(receipt, event) {
|
|
|
405
405
|
if (event.childId !== receipt.childId
|
|
406
406
|
|| !Number.isSafeInteger(event.stopRevision)
|
|
407
407
|
|| event.stopRevision <= receipt.hostEvidence.startRevision
|
|
408
|
-
|| event.consumer !==
|
|
408
|
+
|| event.consumer !== receipt.id
|
|
409
409
|
|| !exactText(event.consumedAt)) {
|
|
410
410
|
return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'recovery stop evidence does not match the activated delegation');
|
|
411
411
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function taskFinalizationReceiptState(receipt) {
|
|
2
2
|
if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
|
|
3
3
|
|| receipt.verification === 'pending' || receipt.summary === 'pending'
|
|
4
|
-
|| receipt.
|
|
4
|
+
|| receipt.warningProjection !== 'done')
|
|
5
5
|
return 'pending';
|
|
6
6
|
if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open'))
|
|
7
7
|
return 'unresolved';
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
import { syncPlatformComment } from '../platform/issue-comments.js';
|
|
5
5
|
import { canonicalizeSummaryBody } from '../platform/comment-safety.js';
|
|
6
|
+
import { backfillCompletionComments } from '../platform/completion-backfill.js';
|
|
6
7
|
import { taskIssueIdentity } from '../platform/task-identities.js';
|
|
7
8
|
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
8
9
|
import { applyTaskLifecycle } from './lifecycle.js';
|
|
@@ -12,8 +13,8 @@ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-
|
|
|
12
13
|
import { verifyTaskEvent } from './verification.js';
|
|
13
14
|
import { projectFinalizationWarning } from './workflow-warning-intents.js';
|
|
14
15
|
import { mergeOperationWarnings } from './operation-outcome.js';
|
|
15
|
-
const RECEIPT_VERSION =
|
|
16
|
-
const FINALIZATION_STEPS = ['lifecycle', 'task-comment', 'verification', 'summary'
|
|
16
|
+
const RECEIPT_VERSION = 4;
|
|
17
|
+
const FINALIZATION_STEPS = ['lifecycle', 'task-comment', 'verification', 'summary'];
|
|
17
18
|
function finalizationRoot(repoRoot) {
|
|
18
19
|
return path.join(repoRoot, '.agents', 'workspace', '.task-finalization');
|
|
19
20
|
}
|
|
@@ -34,11 +35,11 @@ function failed(taskId, error, overrides = {}) {
|
|
|
34
35
|
status: error.retryable ? 'blocked' : 'failed',
|
|
35
36
|
changed: false,
|
|
36
37
|
taskId,
|
|
38
|
+
backfill: null,
|
|
37
39
|
lifecycle: null,
|
|
38
40
|
taskComment: null,
|
|
39
41
|
verification: null,
|
|
40
42
|
summary: null,
|
|
41
|
-
postSummaryVerification: null,
|
|
42
43
|
completedSteps: [],
|
|
43
44
|
pendingSteps: [...FINALIZATION_STEPS],
|
|
44
45
|
result: error.retryable ? 'blocked' : 'failed',
|
|
@@ -58,10 +59,8 @@ function emptyReceipt(taskId, controlBinding) {
|
|
|
58
59
|
taskComment: 'pending',
|
|
59
60
|
verification: 'pending',
|
|
60
61
|
summary: 'pending',
|
|
61
|
-
postSummaryVerification: 'pending',
|
|
62
62
|
warningProjection: 'done',
|
|
63
63
|
warnings: [],
|
|
64
|
-
summarySha256: null,
|
|
65
64
|
...(controlBinding ? { controlBinding } : {}),
|
|
66
65
|
updatedAt: now(),
|
|
67
66
|
lastError: null
|
|
@@ -97,10 +96,8 @@ function validateReceipt(value, taskId) {
|
|
|
97
96
|
|| !states.includes(String(receipt.taskComment))
|
|
98
97
|
|| !states.includes(String(receipt.verification))
|
|
99
98
|
|| !states.includes(String(receipt.summary))
|
|
100
|
-
|| !states.includes(String(receipt.postSummaryVerification))
|
|
101
99
|
|| !['pending', 'done'].includes(String(receipt.warningProjection))
|
|
102
100
|
|| !Array.isArray(receipt.warnings) || receipt.warnings.some((warning) => !validWarning(warning))
|
|
103
|
-
|| (receipt.summarySha256 !== null && (typeof receipt.summarySha256 !== 'string' || !/^[a-f0-9]{64}$/u.test(receipt.summarySha256)))
|
|
104
101
|
|| typeof receipt.updatedAt !== 'string'
|
|
105
102
|
|| (receipt.lastError !== null && (typeof receipt.lastError !== 'object' || Array.isArray(receipt.lastError)))
|
|
106
103
|
|| (receipt.controlBinding !== undefined && (!controlBinding || typeof controlBinding !== 'object'
|
|
@@ -154,8 +151,6 @@ function pendingSteps(receipt) {
|
|
|
154
151
|
function stepState(receipt, step) {
|
|
155
152
|
if (step === 'task-comment')
|
|
156
153
|
return receipt.taskComment;
|
|
157
|
-
if (step === 'post-summary-verification')
|
|
158
|
-
return receipt.postSummaryVerification;
|
|
159
154
|
return receipt[step];
|
|
160
155
|
}
|
|
161
156
|
function lifecycleStep(result) {
|
|
@@ -436,16 +431,13 @@ function readDeliverySummary(taskDir, taskId) {
|
|
|
436
431
|
}
|
|
437
432
|
async function syncPendingSummary(input) {
|
|
438
433
|
let { receipt } = input;
|
|
439
|
-
if (receipt.summary !== 'pending') {
|
|
440
|
-
return { receipt, step: { status: receipt.summary === 'skipped' ? 'skipped' : 'no-op', changed: false, error: null }, changed: false, error: null };
|
|
441
|
-
}
|
|
442
434
|
try {
|
|
443
435
|
const resolved = resolveTaskRef(input.taskId, { repoRoot: input.repoRoot });
|
|
444
436
|
if (!resolved.ok || resolved.state !== 'completed')
|
|
445
437
|
throw Object.assign(new Error('completed task is unavailable for summary sync'), { code: 'SUMMARY_STAGING_INVALID' });
|
|
446
438
|
if (!taskIssueIdentity(parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8')))) {
|
|
447
439
|
receipt = updateReceipt(input.repoRoot, receipt, {
|
|
448
|
-
summary: 'skipped',
|
|
440
|
+
summary: 'skipped', lastError: null
|
|
449
441
|
});
|
|
450
442
|
return { receipt, step: { status: 'skipped', changed: false, error: null }, changed: false, error: null };
|
|
451
443
|
}
|
|
@@ -460,8 +452,7 @@ async function syncPendingSummary(input) {
|
|
|
460
452
|
const resolvedWarning = receipt.warnings.some((warning) => warning.step === 'summary' && warning.status === 'open');
|
|
461
453
|
const warnings = resolveStepWarnings(receipt, 'summary');
|
|
462
454
|
receipt = updateReceipt(input.repoRoot, receipt, {
|
|
463
|
-
summary: skipped ? 'skipped' : 'done',
|
|
464
|
-
summarySha256: skipped ? null : summary.sha256,
|
|
455
|
+
summary: skipped ? 'skipped' : 'done',
|
|
465
456
|
taskComment: resolvedWarning ? 'pending' : receipt.taskComment,
|
|
466
457
|
warningProjection: warnings.some((warning) => warning.status === 'open') ? 'pending' : 'done',
|
|
467
458
|
warnings,
|
|
@@ -488,14 +479,9 @@ async function syncPendingSummary(input) {
|
|
|
488
479
|
return { receipt, step: { status: 'blocked', changed: false, error }, changed: false, error };
|
|
489
480
|
}
|
|
490
481
|
}
|
|
491
|
-
function clearDeliverySummary(taskDir) {
|
|
492
|
-
const file = path.join(taskDir, '.delivery-summary.json');
|
|
493
|
-
if (fs.existsSync(file))
|
|
494
|
-
fs.unlinkSync(file);
|
|
495
|
-
}
|
|
496
482
|
function terminalResult(taskId, receipt, steps, changed, error = null) {
|
|
497
483
|
const pending = pendingSteps(receipt);
|
|
498
|
-
const blocked = [steps.lifecycle, steps.taskComment, steps.verification, steps.summary
|
|
484
|
+
const blocked = [steps.backfill, steps.lifecycle, steps.taskComment, steps.verification, steps.summary].some((step) => step?.status === 'blocked');
|
|
499
485
|
const warnings = openWarnings(receipt);
|
|
500
486
|
const postLifecyclePending = receipt.lifecycle === 'done' && (pending.some((step) => step !== 'lifecycle') || receipt.warningProjection === 'pending');
|
|
501
487
|
const hardError = error?.code.startsWith('FINALIZATION_') || error?.code === 'TASK_FINALIZATION_RECEIPT_INVALID';
|
|
@@ -503,18 +489,18 @@ function terminalResult(taskId, receipt, steps, changed, error = null) {
|
|
|
503
489
|
status: hardError ? (error?.retryable ? 'blocked' : 'failed') : pending.length === 0 ? 'completed' : postLifecyclePending ? 'completed' : blocked ? 'blocked' : 'failed',
|
|
504
490
|
changed,
|
|
505
491
|
taskId,
|
|
492
|
+
backfill: steps.backfill ?? null,
|
|
506
493
|
lifecycle: steps.lifecycle ?? null,
|
|
507
494
|
taskComment: steps.taskComment ?? null,
|
|
508
495
|
verification: steps.verification ?? null,
|
|
509
496
|
summary: steps.summary ?? null,
|
|
510
|
-
postSummaryVerification: steps.postSummaryVerification ?? null,
|
|
511
497
|
completedSteps: completedSteps(receipt),
|
|
512
498
|
pendingSteps: pending,
|
|
513
499
|
result: hardError
|
|
514
500
|
? (error?.retryable ? 'blocked' : 'failed')
|
|
515
|
-
: postLifecyclePending
|
|
501
|
+
: postLifecyclePending
|
|
516
502
|
? 'completed_with_warnings'
|
|
517
|
-
: pending.length === 0
|
|
503
|
+
: pending.length === 0
|
|
518
504
|
? 'completed'
|
|
519
505
|
: blocked
|
|
520
506
|
? 'blocked'
|
|
@@ -526,6 +512,7 @@ function terminalResult(taskId, receipt, steps, changed, error = null) {
|
|
|
526
512
|
async function applyUnderLock(request, taskId, options) {
|
|
527
513
|
const repoRoot = path.resolve(options.repoRoot);
|
|
528
514
|
const lifecycle = options.lifecycle ?? applyTaskLifecycle;
|
|
515
|
+
const backfill = options.backfill ?? backfillCompletionComments;
|
|
529
516
|
const commentSync = options.commentSync ?? syncPlatformComment;
|
|
530
517
|
const verify = options.verify ?? verifyTaskEvent;
|
|
531
518
|
const consumedCapabilities = new Set();
|
|
@@ -562,8 +549,36 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
562
549
|
return failed(taskId, errorOf(error, 'TASK_FINALIZATION_PREFLIGHT_FAILED', true));
|
|
563
550
|
}
|
|
564
551
|
}
|
|
565
|
-
let lifecycleResult = null;
|
|
566
552
|
let changed = false;
|
|
553
|
+
let backfillResult = null;
|
|
554
|
+
try {
|
|
555
|
+
const result = await backfill(taskId, { agent: request.agent, cwd: repoRoot });
|
|
556
|
+
backfillResult = commentStep(result);
|
|
557
|
+
const issueNotLinked = result.status === 'no-op' && result.error?.code === 'ISSUE_NOT_LINKED';
|
|
558
|
+
if (!issueNotLinked && (result.status !== 'applied' && result.status !== 'no-op' || result.error)) {
|
|
559
|
+
const detail = backfillResult.error ?? {
|
|
560
|
+
code: 'COMPLETION_BACKFILL_FAILED', message: 'completion artifact backfill did not reach a successful terminal result', retryable: true
|
|
561
|
+
};
|
|
562
|
+
return failed(taskId, detail, {
|
|
563
|
+
backfill: backfillResult,
|
|
564
|
+
completedSteps: completedSteps(receipt),
|
|
565
|
+
pendingSteps: pendingSteps(receipt)
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
changed = result.changed;
|
|
569
|
+
if (result.changed && receipt.taskComment !== 'pending') {
|
|
570
|
+
receipt = updateReceipt(repoRoot, receipt, { taskComment: 'pending' });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
const detail = errorOf(error, 'COMPLETION_BACKFILL_FAILED', true);
|
|
575
|
+
return failed(taskId, detail, {
|
|
576
|
+
backfill: { status: detail.retryable ? 'blocked' : 'failed', changed: false, error: detail },
|
|
577
|
+
completedSteps: completedSteps(receipt),
|
|
578
|
+
pendingSteps: pendingSteps(receipt)
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
let lifecycleResult = null;
|
|
567
582
|
try {
|
|
568
583
|
const resolved = resolveTaskRef(taskId, { repoRoot });
|
|
569
584
|
const registry = resolved.ok && resolved.state === 'completed' ? inspectShortIdRegistry(repoRoot) : null;
|
|
@@ -575,6 +590,7 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
575
590
|
};
|
|
576
591
|
receipt = updateReceipt(repoRoot, receipt, { lifecycle: 'pending', lastError: detail });
|
|
577
592
|
return terminalResult(taskId, receipt, {
|
|
593
|
+
backfill: backfillResult,
|
|
578
594
|
lifecycle: { status: 'failed', changed: false, error: detail },
|
|
579
595
|
taskComment: null,
|
|
580
596
|
verification: null
|
|
@@ -589,6 +605,7 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
589
605
|
};
|
|
590
606
|
receipt = updateReceipt(repoRoot, receipt, { lifecycle: 'pending', lastError: detail });
|
|
591
607
|
return terminalResult(taskId, receipt, {
|
|
608
|
+
backfill: backfillResult,
|
|
592
609
|
lifecycle: { status: 'failed', changed: false, error: detail },
|
|
593
610
|
taskComment: null,
|
|
594
611
|
verification: null
|
|
@@ -604,9 +621,9 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
604
621
|
lifecycleResult = lifecycleStep(result);
|
|
605
622
|
if (result.status !== 'applied' && result.status !== 'no-op') {
|
|
606
623
|
receipt = updateReceipt(repoRoot, receipt, { lifecycle: 'pending', lastError: lifecycleResult.error });
|
|
607
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment: null, verification: null }, result.changed, lifecycleResult.error);
|
|
624
|
+
return terminalResult(taskId, receipt, { backfill: backfillResult, lifecycle: lifecycleResult, taskComment: null, verification: null }, result.changed, lifecycleResult.error);
|
|
608
625
|
}
|
|
609
|
-
changed = result.changed;
|
|
626
|
+
changed = changed || result.changed;
|
|
610
627
|
receipt = updateReceipt(repoRoot, receipt, { lifecycle: 'done', lastError: null });
|
|
611
628
|
}
|
|
612
629
|
}
|
|
@@ -616,7 +633,7 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
616
633
|
receipt = updateReceipt(repoRoot, receipt, { lifecycle: 'pending', lastError: detail });
|
|
617
634
|
}
|
|
618
635
|
catch { /* preserve the primary error */ }
|
|
619
|
-
return failed(taskId, detail, { lifecycle: lifecycleResult });
|
|
636
|
+
return failed(taskId, detail, { backfill: backfillResult, lifecycle: lifecycleResult });
|
|
620
637
|
}
|
|
621
638
|
let taskComment = null;
|
|
622
639
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
@@ -627,7 +644,7 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
627
644
|
taskComment = initialComment.step;
|
|
628
645
|
changed = changed || initialComment.changed;
|
|
629
646
|
if (initialComment.error) {
|
|
630
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification: null }, changed, initialComment.error);
|
|
647
|
+
return terminalResult(taskId, receipt, { backfill: backfillResult, lifecycle: lifecycleResult, taskComment, verification: null }, changed, initialComment.error);
|
|
631
648
|
}
|
|
632
649
|
let verification = null;
|
|
633
650
|
try {
|
|
@@ -650,7 +667,7 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
650
667
|
taskComment = finalComment.step;
|
|
651
668
|
changed = changed || finalComment.changed;
|
|
652
669
|
if (finalComment.error) {
|
|
653
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification }, changed, finalComment.error);
|
|
670
|
+
return terminalResult(taskId, receipt, { backfill: backfillResult, lifecycle: lifecycleResult, taskComment, verification }, changed, finalComment.error);
|
|
654
671
|
}
|
|
655
672
|
}
|
|
656
673
|
else {
|
|
@@ -678,7 +695,7 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
678
695
|
taskComment = warningComment.step;
|
|
679
696
|
changed = changed || warningComment.changed;
|
|
680
697
|
}
|
|
681
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification }, changed, detail);
|
|
698
|
+
return terminalResult(taskId, receipt, { backfill: backfillResult, lifecycle: lifecycleResult, taskComment, verification }, changed, detail);
|
|
682
699
|
}
|
|
683
700
|
}
|
|
684
701
|
catch (error) {
|
|
@@ -706,7 +723,7 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
706
723
|
retryable: true
|
|
707
724
|
};
|
|
708
725
|
return terminalResult(taskId, receipt, {
|
|
709
|
-
lifecycle: lifecycleResult, taskComment, verification: { status: 'blocked', changed: false, error: detail }
|
|
726
|
+
backfill: backfillResult, lifecycle: lifecycleResult, taskComment, verification: { status: 'blocked', changed: false, error: detail }
|
|
710
727
|
}, changed, hardError);
|
|
711
728
|
}
|
|
712
729
|
try {
|
|
@@ -717,107 +734,17 @@ async function applyUnderLock(request, taskId, options) {
|
|
|
717
734
|
changed = changed || warningComment.changed;
|
|
718
735
|
}
|
|
719
736
|
catch { /* preserve the primary error */ }
|
|
720
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification: { status: 'blocked', changed: false, error: detail } }, changed, detail);
|
|
737
|
+
return terminalResult(taskId, receipt, { backfill: backfillResult, lifecycle: lifecycleResult, taskComment, verification: { status: 'blocked', changed: false, error: detail } }, changed, detail);
|
|
721
738
|
}
|
|
722
|
-
|
|
739
|
+
let summary = await syncPendingSummary({ repoRoot, taskId, agent: request.agent, receipt, commentSync });
|
|
723
740
|
receipt = summary.receipt;
|
|
724
741
|
changed = changed || summary.changed;
|
|
725
742
|
if (summary.error) {
|
|
726
743
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
727
|
-
|
|
728
|
-
repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities
|
|
729
|
-
});
|
|
730
|
-
receipt = warningComment.receipt;
|
|
731
|
-
if (warningComment.step.status !== 'no-op')
|
|
732
|
-
taskComment = warningComment.step;
|
|
733
|
-
changed = changed || warningComment.changed;
|
|
734
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification, summary: summary.step }, changed, summary.error);
|
|
735
|
-
}
|
|
736
|
-
let postSummaryVerification = null;
|
|
737
|
-
if (receipt.postSummaryVerification === 'skipped') {
|
|
738
|
-
postSummaryVerification = { status: 'skipped', changed: false, error: null };
|
|
739
|
-
}
|
|
740
|
-
else if (receipt.postSummaryVerification !== 'pending') {
|
|
741
|
-
const replay = await commentSync(taskId, {
|
|
742
|
-
kind: 'summary', agent: request.agent, body: '', cwd: repoRoot, verifyOnly: true,
|
|
743
|
-
summaryAuthorization: { sha256: receipt.summarySha256 ?? '' }
|
|
744
|
-
});
|
|
745
|
-
postSummaryVerification = commentStep(replay);
|
|
746
|
-
if (replay.status !== 'applied' && replay.status !== 'no-op') {
|
|
747
|
-
const error = postSummaryVerification.error ?? { code: 'SUMMARY_VERIFICATION_FAILED', message: 'summary replay verification failed', retryable: true };
|
|
748
|
-
const warnings = replaceWarning(receipt, warningFromError('post-summary-verification', error), 'open');
|
|
749
|
-
receipt = updateReceipt(repoRoot, receipt, { postSummaryVerification: 'pending', taskComment: 'pending', warningProjection: 'pending', warnings, lastError: error });
|
|
750
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
751
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification, summary: summary.step, postSummaryVerification }, changed, error);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
else {
|
|
755
|
-
try {
|
|
756
|
-
const summaryVerification = await commentSync(taskId, {
|
|
757
|
-
kind: 'summary', agent: request.agent, body: '', cwd: repoRoot, verifyOnly: true,
|
|
758
|
-
summaryAuthorization: { sha256: receipt.summarySha256 ?? '' }
|
|
759
|
-
});
|
|
760
|
-
if (summaryVerification.status !== 'applied' && summaryVerification.status !== 'no-op') {
|
|
761
|
-
const error = commentStep(summaryVerification).error ?? { code: 'SUMMARY_VERIFICATION_FAILED', message: 'summary verification failed', retryable: true };
|
|
762
|
-
const warnings = replaceWarning(receipt, warningFromError('post-summary-verification', error), 'open');
|
|
763
|
-
receipt = updateReceipt(repoRoot, receipt, { postSummaryVerification: 'pending', taskComment: 'pending', warningProjection: 'pending', warnings, lastError: error });
|
|
764
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
765
|
-
const warningComment = await syncPendingTaskComment({ repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities });
|
|
766
|
-
receipt = warningComment.receipt;
|
|
767
|
-
if (warningComment.step.status !== 'no-op')
|
|
768
|
-
taskComment = warningComment.step;
|
|
769
|
-
changed = changed || warningComment.changed;
|
|
770
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification, summary: summary.step, postSummaryVerification: commentStep(summaryVerification) }, changed, error);
|
|
771
|
-
}
|
|
772
|
-
const result = await verify({ taskRef: taskId, event: 'complete-task.completed' }, { repoRoot });
|
|
773
|
-
postSummaryVerification = verificationStep(result);
|
|
774
|
-
if (result.status === 'pass') {
|
|
775
|
-
const resolvedWarning = receipt.warnings.some((warning) => warning.step === 'post-summary-verification' && warning.status === 'open');
|
|
776
|
-
const warnings = resolveStepWarnings(receipt, 'post-summary-verification');
|
|
777
|
-
receipt = updateReceipt(repoRoot, receipt, {
|
|
778
|
-
postSummaryVerification: 'done', taskComment: resolvedWarning ? 'pending' : receipt.taskComment,
|
|
779
|
-
warningProjection: warnings.some((warning) => warning.status === 'open') ? 'pending' : 'done', warnings, lastError: null
|
|
780
|
-
});
|
|
781
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
782
|
-
const warningComment = await syncPendingTaskComment({ repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities });
|
|
783
|
-
receipt = warningComment.receipt;
|
|
784
|
-
if (warningComment.step.status !== 'no-op')
|
|
785
|
-
taskComment = warningComment.step;
|
|
786
|
-
changed = changed || warningComment.changed;
|
|
787
|
-
}
|
|
788
|
-
else {
|
|
789
|
-
const error = postSummaryVerification.error ?? { code: 'VERIFY_FAILED', message: 'post-summary verification failed', retryable: true };
|
|
790
|
-
const warnings = replaceWarning(receipt, warningFromError('post-summary-verification', error), 'open');
|
|
791
|
-
receipt = updateReceipt(repoRoot, receipt, { postSummaryVerification: 'pending', taskComment: 'pending', warningProjection: 'pending', warnings, lastError: error });
|
|
792
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
793
|
-
const warningComment = await syncPendingTaskComment({ repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities });
|
|
794
|
-
receipt = warningComment.receipt;
|
|
795
|
-
if (warningComment.step.status !== 'no-op')
|
|
796
|
-
taskComment = warningComment.step;
|
|
797
|
-
changed = changed || warningComment.changed;
|
|
798
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification, summary: summary.step, postSummaryVerification }, changed, error);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
catch (error) {
|
|
802
|
-
const detail = errorOf(error, 'POST_SUMMARY_VERIFY_FAILED', true);
|
|
803
|
-
const warnings = replaceWarning(receipt, warningFromError('post-summary-verification', detail), 'open');
|
|
804
|
-
receipt = updateReceipt(repoRoot, receipt, { postSummaryVerification: 'pending', taskComment: 'pending', warningProjection: 'pending', warnings, lastError: detail });
|
|
805
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
806
|
-
const warningComment = await syncPendingTaskComment({ repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities });
|
|
807
|
-
receipt = warningComment.receipt;
|
|
808
|
-
if (warningComment.step.status !== 'no-op')
|
|
809
|
-
taskComment = warningComment.step;
|
|
810
|
-
changed = changed || warningComment.changed;
|
|
811
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification, summary: summary.step, postSummaryVerification: { status: 'blocked', changed: false, error: detail } }, changed, detail);
|
|
812
|
-
}
|
|
744
|
+
return terminalResult(taskId, receipt, { backfill: backfillResult, lifecycle: lifecycleResult, taskComment, verification, summary: summary.step }, changed, summary.error);
|
|
813
745
|
}
|
|
814
746
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
815
|
-
|
|
816
|
-
const resolved = resolveTaskRef(taskId, { repoRoot });
|
|
817
|
-
if (resolved.ok && resolved.state === 'completed')
|
|
818
|
-
clearDeliverySummary(resolved.taskDir);
|
|
819
|
-
}
|
|
820
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification, summary: summary.step, postSummaryVerification }, changed);
|
|
747
|
+
return terminalResult(taskId, receipt, { backfill: backfillResult, lifecycle: lifecycleResult, taskComment, verification, summary: summary.step }, changed);
|
|
821
748
|
}
|
|
822
749
|
async function applyTaskFinalization(request, options) {
|
|
823
750
|
if (request.intent !== 'complete' || !request.taskRef || !request.agent) {
|
|
@@ -151,7 +151,7 @@ function actionAndNote(request, restoredFiles = 0) {
|
|
|
151
151
|
if (request.intent === 'close-dependabot')
|
|
152
152
|
return { action: 'Close Dependabot', note: `Dependabot alert #${request.alertNumber} dismissed: ${request.reason}` };
|
|
153
153
|
if (request.intent === 'recover-started')
|
|
154
|
-
return { action: 'Recover Started', note:
|
|
154
|
+
return { action: 'Recover Started', note: 'automatic recovery' };
|
|
155
155
|
return { action: 'Restore Task', note: `Restored ${restoredFiles} files from Issue #${request.issueNumber}` };
|
|
156
156
|
}
|
|
157
157
|
function matchingCompletion(content, request, restoredFiles = 0) {
|
|
@@ -4,6 +4,7 @@ import { createHash } from 'node:crypto';
|
|
|
4
4
|
import { MANUAL_VALIDATION_ARTIFACT as ARTIFACT, MANUAL_VALIDATION_SHA40 as SHA40, MANUAL_VALIDATION_SHA64 as SHA64, MANUAL_VALIDATION_TASK_ID as TASK_ID, exactKeys, isRecord, validTimestamp, writeJsonAtomic } from './manual-validation-shared.js';
|
|
5
5
|
const MANUAL_VALIDATION_RECEIPT_SCHEMA = 'agent-infra/manual-validation-receipt';
|
|
6
6
|
const MANUAL_VALIDATION_RECEIPT_VERSION = 1;
|
|
7
|
+
const MANUAL_VALIDATION_RECEIPT_PLACEHOLDER = '[[manual-validation-receipt]]';
|
|
7
8
|
function invalid(message) {
|
|
8
9
|
return { ok: false, error: { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message } };
|
|
9
10
|
}
|
|
@@ -33,12 +34,16 @@ function manualValidationFinalSummaryDigest(body) {
|
|
|
33
34
|
const projection = heading
|
|
34
35
|
? body.slice(heading.index, heading.index + heading[0].length + (nextSection?.index ?? afterHeading.length)).replace(/\s+$/u, '')
|
|
35
36
|
: body;
|
|
36
|
-
const preimage = projection
|
|
37
|
+
const preimage = projection
|
|
38
|
+
.replace(/\n*<!--\s*canonical-pr-change-report\s*-->\s*/gu, '')
|
|
39
|
+
.replace(/<!--\s*manual-validation-receipt:\s*[\s\S]*?-->/gu, MANUAL_VALIDATION_RECEIPT_PLACEHOLDER)
|
|
40
|
+
.replace(/receipt=(?:[a-f0-9]{64}|<receipt>)/gu, 'receipt=<receipt>');
|
|
37
41
|
return createHash('sha256').update(preimage, 'utf8').digest('hex');
|
|
38
42
|
}
|
|
39
43
|
function manualValidationFinalSummaryProjectionMatches(body, receipt) {
|
|
40
44
|
const identity = `transaction=${receipt.transactionId}; receipt=${receipt.receiptDigest}; evidence=${receipt.evidenceDigest}; head=${receipt.prHeadSha}`;
|
|
41
|
-
return body.includes(identity)
|
|
45
|
+
return (body.includes(identity) || body.includes(MANUAL_VALIDATION_RECEIPT_PLACEHOLDER))
|
|
46
|
+
&& manualValidationFinalSummaryDigest(body) === receipt.finalSummaryDigest;
|
|
42
47
|
}
|
|
43
48
|
function validateManualValidationReceipt(value, expected) {
|
|
44
49
|
if (!isRecord(value))
|
|
@@ -101,5 +106,5 @@ function readManualValidationReceipt(taskDir, expected) {
|
|
|
101
106
|
}
|
|
102
107
|
return validateManualValidationReceipt(value, expected);
|
|
103
108
|
}
|
|
104
|
-
export { MANUAL_VALIDATION_RECEIPT_SCHEMA, MANUAL_VALIDATION_RECEIPT_VERSION, createManualValidationReceipt, manualValidationFinalSummaryDigest, manualValidationFinalSummaryProjectionMatches, manualValidationReceiptDigest, manualValidationReceiptPath, readManualValidationReceipt, validateManualValidationReceipt, writeManualValidationReceiptAtomic };
|
|
109
|
+
export { MANUAL_VALIDATION_RECEIPT_SCHEMA, MANUAL_VALIDATION_RECEIPT_VERSION, MANUAL_VALIDATION_RECEIPT_PLACEHOLDER, createManualValidationReceipt, manualValidationFinalSummaryDigest, manualValidationFinalSummaryProjectionMatches, manualValidationReceiptDigest, manualValidationReceiptPath, readManualValidationReceipt, validateManualValidationReceipt, writeManualValidationReceiptAtomic };
|
|
105
110
|
//# sourceMappingURL=manual-validation-receipt.js.map
|
|
@@ -1075,6 +1075,14 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
|
1075
1075
|
return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
1076
1076
|
}
|
|
1077
1077
|
}
|
|
1078
|
+
function recoveryReceiptMatches(receipt, taskId, event) {
|
|
1079
|
+
return receipt.taskId === taskId
|
|
1080
|
+
&& receipt.stage === event.stage
|
|
1081
|
+
&& receipt.round === event.round
|
|
1082
|
+
&& receipt.artifact === event.artifact
|
|
1083
|
+
&& receipt.childId === event.childId
|
|
1084
|
+
&& normalizeAgentToken(event.startedAgent) === normalizeAgentToken(receipt.client);
|
|
1085
|
+
}
|
|
1078
1086
|
function recoverActivatedOrchestrationDelegationUnderLock(taskRef, event, options = {}) {
|
|
1079
1087
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1080
1088
|
if (!resolved.ok)
|
|
@@ -1089,12 +1097,7 @@ function recoverActivatedOrchestrationDelegationUnderLock(taskRef, event, option
|
|
|
1089
1097
|
const receipt = matches.find((candidate) => candidate.id === event.receiptId);
|
|
1090
1098
|
if (!receipt)
|
|
1091
1099
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'recovery receipt does not exist', resolved.taskId);
|
|
1092
|
-
if (receipt.taskId
|
|
1093
|
-
|| receipt.stage !== event.stage
|
|
1094
|
-
|| receipt.round !== event.round
|
|
1095
|
-
|| receipt.artifact !== event.artifact
|
|
1096
|
-
|| receipt.childId !== event.childId
|
|
1097
|
-
|| normalizeAgentToken(event.startedAgent) !== normalizeAgentToken(receipt.client)) {
|
|
1100
|
+
if (!recoveryReceiptMatches(receipt, resolved.taskId, event)) {
|
|
1098
1101
|
return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'recovery selector does not match the delegation receipt', resolved.taskId);
|
|
1099
1102
|
}
|
|
1100
1103
|
if (receipt.status === 'aborted' && run.pendingDelegation === null) {
|
|
@@ -1106,6 +1109,9 @@ function recoverActivatedOrchestrationDelegationUnderLock(taskRef, event, option
|
|
|
1106
1109
|
if (run.receipts.some((candidate) => candidate.id === receipt.id)) {
|
|
1107
1110
|
return failed('ORCHESTRATION_RECEIPT_DUPLICATE', 'recovery receipt already exists in the completed receipt history', resolved.taskId);
|
|
1108
1111
|
}
|
|
1112
|
+
if (run.status !== 'running' || run.pause !== null) {
|
|
1113
|
+
return failed('ORCHESTRATION_RECOVERY_UNSAFE', 'activated recovery cannot replace an unrelated orchestration pause', resolved.taskId);
|
|
1114
|
+
}
|
|
1109
1115
|
const aborted = abortActivatedDelegation(receipt, {
|
|
1110
1116
|
childId: event.childId,
|
|
1111
1117
|
stopRevision: event.stopRevision,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.11.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.11.3",
|
|
4
|
+
"sourceInputHash": "99efbc575d475879773bf1676cc2de5ea628b9c193f69a9812a723313ba5ae7f",
|
|
5
|
+
"internalExecutableBuildHash": "6912a66863d013c476699b88cc1d4154082971164cbae3f98ea79dda39a46e7a"
|
|
6
6
|
}
|
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
AgentClientCapabilityMap,
|
|
8
8
|
AgentClientId
|
|
9
9
|
} from './types.ts';
|
|
10
|
+
import type {
|
|
11
|
+
OrchestrationOptions,
|
|
12
|
+
OrchestrationResult
|
|
13
|
+
} from '../task/orchestration.ts';
|
|
10
14
|
import {
|
|
11
15
|
SANDBOX_HOOK_PHASES
|
|
12
16
|
} from '../sandbox/tool-types.ts';
|
|
@@ -43,6 +47,41 @@ type AgentClientDelegationEvidence = Readonly<{
|
|
|
43
47
|
actualReasoningEffort: 'host-event' | 'app-server' | 'spawn-ack' | 'unavailable';
|
|
44
48
|
}>;
|
|
45
49
|
|
|
50
|
+
type AgentClientLifecycleRecoveryRequest = Readonly<{
|
|
51
|
+
taskRef: string;
|
|
52
|
+
intent: 'recover-started';
|
|
53
|
+
agent: string;
|
|
54
|
+
auto: true;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
type AgentClientLifecycleRecoveryResult = Readonly<{
|
|
58
|
+
status: 'applied' | 'no-op' | 'owner-unknown' | 'conflict';
|
|
59
|
+
changed: boolean;
|
|
60
|
+
targetState: 'active';
|
|
61
|
+
requestRef: string;
|
|
62
|
+
intent: 'recover-started';
|
|
63
|
+
taskId: string | null;
|
|
64
|
+
receiptId: string | null;
|
|
65
|
+
childId: string | null;
|
|
66
|
+
error: Readonly<{ code: string; message: string }> | null;
|
|
67
|
+
}>;
|
|
68
|
+
|
|
69
|
+
type AgentClientOrchestrationAdapter = Readonly<{
|
|
70
|
+
prepareDelegation?: (
|
|
71
|
+
taskRef: string,
|
|
72
|
+
input: Readonly<{
|
|
73
|
+
client: AgentClientId;
|
|
74
|
+
requestedModel?: string;
|
|
75
|
+
requestedReasoningEffort?: string;
|
|
76
|
+
}>,
|
|
77
|
+
options?: Readonly<{ repoRoot?: string; orchestrationOptions?: OrchestrationOptions }>
|
|
78
|
+
) => Promise<OrchestrationResult>;
|
|
79
|
+
recoverStarted?: (
|
|
80
|
+
request: AgentClientLifecycleRecoveryRequest,
|
|
81
|
+
options: Readonly<{ repoRoot: string }>
|
|
82
|
+
) => AgentClientLifecycleRecoveryResult | Promise<AgentClientLifecycleRecoveryResult>;
|
|
83
|
+
}>;
|
|
84
|
+
|
|
46
85
|
type AgentClientSeedCommand = Readonly<{
|
|
47
86
|
templates: Readonly<{
|
|
48
87
|
en: string;
|
|
@@ -75,6 +114,7 @@ type AgentClientAdapter = Readonly<{
|
|
|
75
114
|
capabilities: AgentClientCapabilities;
|
|
76
115
|
modelSelection: AgentClientModelSelectionContext;
|
|
77
116
|
delegationEvidence: AgentClientDelegationEvidence;
|
|
117
|
+
orchestrationAdapter?: AgentClientOrchestrationAdapter;
|
|
78
118
|
project: AgentClientProjectDescriptor;
|
|
79
119
|
sandbox: AgentClientSandboxDescriptor;
|
|
80
120
|
}>;
|
|
@@ -628,6 +668,9 @@ function defineAgentClientAdapter(
|
|
|
628
668
|
capabilities: Object.freeze(capabilities),
|
|
629
669
|
modelSelection: frozenModelSelection,
|
|
630
670
|
delegationEvidence: Object.freeze({ ...evidence }),
|
|
671
|
+
...(candidate.orchestrationAdapter === undefined
|
|
672
|
+
? {}
|
|
673
|
+
: { orchestrationAdapter: Object.freeze({ ...candidate.orchestrationAdapter }) }),
|
|
631
674
|
project: Object.freeze({
|
|
632
675
|
ownedPathPrefixes: Object.freeze(paths),
|
|
633
676
|
...projectAssets,
|
|
@@ -650,6 +693,9 @@ export type {
|
|
|
650
693
|
AgentClientCapabilities,
|
|
651
694
|
AgentClientCustomCommandDescriptor,
|
|
652
695
|
AgentClientDelegationEvidence,
|
|
696
|
+
AgentClientLifecycleRecoveryRequest,
|
|
697
|
+
AgentClientLifecycleRecoveryResult,
|
|
698
|
+
AgentClientOrchestrationAdapter,
|
|
653
699
|
AgentClientManifestEntry,
|
|
654
700
|
AgentClientModelSelectionContext,
|
|
655
701
|
AgentClientProjectDescriptor,
|