@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
|
@@ -56,11 +56,10 @@ function receiptIdentity(value: unknown, generation: string, requestId?: string)
|
|
|
56
56
|
const binding = receipt.controlBinding;
|
|
57
57
|
if (!binding || typeof binding !== 'object' || Array.isArray(binding)) return null;
|
|
58
58
|
const controlBinding = binding as Record<string, unknown>;
|
|
59
|
-
if (receipt.version !==
|
|
59
|
+
if (receipt.version !== 4 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
|
|
60
60
|
|| !Number.isSafeInteger(receipt.revision) || (receipt.revision as number) < 0
|
|
61
61
|
|| receipt.lifecycle !== 'done'
|
|
62
62
|
|| !['pending', 'done', 'skipped'].includes(String(receipt.summary))
|
|
63
|
-
|| !['pending', 'done', 'skipped'].includes(String(receipt.postSummaryVerification))
|
|
64
63
|
|| controlBinding.generation !== generation
|
|
65
64
|
|| typeof controlBinding.requestId !== 'string'
|
|
66
65
|
|| !/^[a-f0-9-]{16,64}$/u.test(controlBinding.requestId)
|
|
@@ -5,6 +5,7 @@ import fs from 'node:fs';
|
|
|
5
5
|
import { normalizeAgentToken } from '../agent-clients/tokens.ts';
|
|
6
6
|
import { isAgentClientId } from '../agent-clients/types.ts';
|
|
7
7
|
import type { AgentClientId } from '../agent-clients/types.ts';
|
|
8
|
+
import { getAgentClientAdapter } from '../agent-clients/registry.ts';
|
|
8
9
|
import { consumeHumanOverride, failureId, overrideDryRunConflict } from './human-override.ts';
|
|
9
10
|
import {
|
|
10
11
|
activateMatchingOrchestrationDelegation,
|
|
@@ -22,12 +23,7 @@ import {
|
|
|
22
23
|
sealOrchestrationDelegation,
|
|
23
24
|
statusOrchestration
|
|
24
25
|
} from './orchestration.ts';
|
|
25
|
-
import type {
|
|
26
|
-
OrchestrationDiagnosticLogger,
|
|
27
|
-
OrchestrationOptions,
|
|
28
|
-
OrchestrationResult
|
|
29
|
-
} from './orchestration.ts';
|
|
30
|
-
import { prepareCodexOrchestrationDelegation } from './codex-orchestration.ts';
|
|
26
|
+
import type { OrchestrationOptions, OrchestrationResult } from './orchestration.ts';
|
|
31
27
|
import {
|
|
32
28
|
applyTaskFinalization,
|
|
33
29
|
type TaskFinalizationRequest,
|
|
@@ -39,12 +35,10 @@ import {
|
|
|
39
35
|
type TaskLifecycleRequest,
|
|
40
36
|
type TaskLifecycleResult
|
|
41
37
|
} from './lifecycle.ts';
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
} from './lifecycle-recovery.ts';
|
|
47
|
-
import type { LifecycleRecoveryRequest } from './lifecycle-recovery.ts';
|
|
38
|
+
import type {
|
|
39
|
+
AgentClientLifecycleRecoveryRequest as LifecycleRecoveryRequest,
|
|
40
|
+
AgentClientLifecycleRecoveryResult as LifecycleRecoveryResult
|
|
41
|
+
} from '../agent-clients/adapter.ts';
|
|
48
42
|
import { locateActivityLog } from './activity-log.ts';
|
|
49
43
|
import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.ts';
|
|
50
44
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
@@ -542,7 +536,6 @@ export type TaskControlExecutionContext =
|
|
|
542
536
|
generation: string;
|
|
543
537
|
manifestPath: string;
|
|
544
538
|
requestId: string;
|
|
545
|
-
diagnosticLog?: OrchestrationDiagnosticLogger;
|
|
546
539
|
controllerBinding?: TaskControlControllerBinding | null;
|
|
547
540
|
lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
|
|
548
541
|
}>;
|
|
@@ -631,7 +624,6 @@ export function createSandboxExecutorExecutionContext(params: Readonly<{
|
|
|
631
624
|
generation: string;
|
|
632
625
|
manifestPath: string;
|
|
633
626
|
requestId: string;
|
|
634
|
-
diagnosticLog?: OrchestrationDiagnosticLogger;
|
|
635
627
|
controllerBinding?: TaskControlControllerBinding | null;
|
|
636
628
|
lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
|
|
637
629
|
}>): TaskControlExecutionContext {
|
|
@@ -652,7 +644,6 @@ export function createSandboxExecutorExecutionContext(params: Readonly<{
|
|
|
652
644
|
generation: params.generation,
|
|
653
645
|
manifestPath: absolute('manifestPath', params.manifestPath),
|
|
654
646
|
requestId: params.requestId,
|
|
655
|
-
...(params.diagnosticLog === undefined ? {} : { diagnosticLog: params.diagnosticLog }),
|
|
656
647
|
...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) }),
|
|
657
648
|
...(params.lifecycleRecoveryAttestation === undefined ? {} : {
|
|
658
649
|
lifecycleRecoveryAttestation: params.lifecycleRecoveryAttestation === null
|
|
@@ -697,11 +688,7 @@ function domainOptions(
|
|
|
697
688
|
): OrchestrationOptions {
|
|
698
689
|
return {
|
|
699
690
|
...options,
|
|
700
|
-
repoRoot: context.repoRoot
|
|
701
|
-
...(context.source === 'sandbox-executor' && context.diagnosticLog !== undefined
|
|
702
|
-
? { diagnosticLog: context.diagnosticLog }
|
|
703
|
-
: {}),
|
|
704
|
-
...(context.source === 'sandbox-executor' ? { gitWorktreeRoot: context.worktreeRoot } : {})
|
|
691
|
+
repoRoot: context.repoRoot
|
|
705
692
|
};
|
|
706
693
|
}
|
|
707
694
|
|
|
@@ -725,7 +712,17 @@ async function applyLifecycleWithAuthority(
|
|
|
725
712
|
}
|
|
726
713
|
const execute = async (): Promise<(TaskLifecycleResult | LifecycleRecoveryResult) & { humanOverride?: unknown }> => {
|
|
727
714
|
if (request.intent === 'recover-started') {
|
|
728
|
-
|
|
715
|
+
const adapter = isAgentClientId(request.agent)
|
|
716
|
+
? getAgentClientAdapter(request.agent).orchestrationAdapter
|
|
717
|
+
: undefined;
|
|
718
|
+
if (!adapter?.recoverStarted) {
|
|
719
|
+
return {
|
|
720
|
+
status: 'no-op', changed: false, targetState: 'active',
|
|
721
|
+
requestRef: request.taskRef, intent: 'recover-started', taskId: null,
|
|
722
|
+
receiptId: null, childId: null, error: null
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
return adapter.recoverStarted(request as LifecycleRecoveryRequest, { repoRoot: context.repoRoot });
|
|
729
726
|
}
|
|
730
727
|
const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
|
|
731
728
|
if (lifecycleResult.status !== 'failed' || !request.overrideTicket) return lifecycleResult;
|
|
@@ -761,7 +758,12 @@ async function applyLifecycleWithAuthority(
|
|
|
761
758
|
} catch (error) {
|
|
762
759
|
if (!(error instanceof TaskExecutionLockError)) throw error;
|
|
763
760
|
if (request.intent === 'recover-started') {
|
|
764
|
-
return
|
|
761
|
+
return {
|
|
762
|
+
status: 'owner-unknown', changed: false, targetState: 'active',
|
|
763
|
+
requestRef: request.taskRef, intent: 'recover-started', taskId,
|
|
764
|
+
receiptId: null, childId: null,
|
|
765
|
+
error: { code: error.code, message: error.message }
|
|
766
|
+
};
|
|
765
767
|
}
|
|
766
768
|
return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
|
|
767
769
|
}
|
|
@@ -790,8 +792,10 @@ function orchestration(
|
|
|
790
792
|
requestedModel: input.requestedModel as string | undefined,
|
|
791
793
|
requestedReasoningEffort: input.requestedReasoningEffort as string | undefined,
|
|
792
794
|
};
|
|
793
|
-
|
|
794
|
-
|
|
795
|
+
const prepareDelegation = getAgentClientAdapter(prepareInput.client)
|
|
796
|
+
.orchestrationAdapter?.prepareDelegation;
|
|
797
|
+
if (prepareDelegation) {
|
|
798
|
+
return prepareDelegation(operation.taskRef, prepareInput, {
|
|
795
799
|
repoRoot: context.repoRoot,
|
|
796
800
|
orchestrationOptions: options,
|
|
797
801
|
});
|
|
@@ -898,7 +902,7 @@ function operationInvalid(message: string): never {
|
|
|
898
902
|
|
|
899
903
|
const LIFECYCLE_FLAGS = new Set([
|
|
900
904
|
'--agent', '--reason', '--unblock-condition', '--note', '--alert-number', '--staging-dir', '--issue-number',
|
|
901
|
-
'--
|
|
905
|
+
'--auto', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
|
|
902
906
|
]);
|
|
903
907
|
|
|
904
908
|
const FINALIZATION_FLAGS = new Set(['--agent']);
|
|
@@ -909,7 +913,7 @@ const ORCHESTRATION_FLAGS = new Set([
|
|
|
909
913
|
'--parent-id', '--before-fingerprint', '--stage', '--round', '--artifact', '--role',
|
|
910
914
|
'--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
|
|
911
915
|
'--model-fallback-reason', '--reasoning-effort-fallback-reason', '--exit-code', '--after-fingerprint',
|
|
912
|
-
'--changed-paths', '--code', '--message', '--recoverable'
|
|
916
|
+
'--changed-paths', '--code', '--message', '--recoverable'
|
|
913
917
|
]);
|
|
914
918
|
|
|
915
919
|
function parseValues(
|
|
@@ -920,7 +924,7 @@ function parseValues(
|
|
|
920
924
|
const values: Record<string, string | boolean> = {};
|
|
921
925
|
for (let index = start; index < args.length; index += 1) {
|
|
922
926
|
const flag = args[index]!;
|
|
923
|
-
if (flag === '--dry-run') {
|
|
927
|
+
if (flag === '--dry-run' || flag === '--auto') {
|
|
924
928
|
if (!flags.has(flag)) operationInvalid(`unknown option '${flag}'`);
|
|
925
929
|
if (values[flag] !== undefined) operationInvalid(`duplicate option '${flag}'`);
|
|
926
930
|
values[flag] = true;
|
|
@@ -978,28 +982,18 @@ export function parseTaskControlOperation(
|
|
|
978
982
|
...(value(values, '--override-scope') ? { overrideScope: value(values, '--override-scope') } : {}),
|
|
979
983
|
...(value(values, '--alert-number') ? { alertNumber: Number(value(values, '--alert-number')) } : {}),
|
|
980
984
|
...(value(values, '--issue-number') ? { issueNumber: Number(value(values, '--issue-number')) } : {}),
|
|
981
|
-
...(
|
|
982
|
-
...(value(values, '--round') ? { round: Number(value(values, '--round')) } : {}),
|
|
983
|
-
...(value(values, '--artifact') ? { artifact: value(values, '--artifact') } : {}),
|
|
985
|
+
...(values['--auto'] === true ? { auto: true } : {}),
|
|
984
986
|
...(values['--dry-run'] === true ? { dryRun: true } : {})
|
|
985
987
|
};
|
|
986
988
|
if (intent === 'recover-started') {
|
|
987
|
-
const recoveryFlags = new Set(['--agent', '--
|
|
989
|
+
const recoveryFlags = new Set(['--agent', '--auto']);
|
|
988
990
|
for (const flag of Object.keys(values)) {
|
|
989
991
|
if (!recoveryFlags.has(flag)) operationInvalid(`option '${flag}' is not supported for recover-started`);
|
|
990
992
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
const round = Number(value(values, '--round'));
|
|
997
|
-
if (!Number.isSafeInteger(round) || round < 1) operationInvalid('--round must be a positive integer');
|
|
998
|
-
if (value(values, '--reason')!.includes('\n') || value(values, '--reason')!.includes('\r')) {
|
|
999
|
-
operationInvalid('--reason must be a single line');
|
|
1000
|
-
}
|
|
1001
|
-
} else if (values['--stage'] !== undefined || values['--round'] !== undefined || values['--artifact'] !== undefined) {
|
|
1002
|
-
operationInvalid('--stage, --round, and --artifact are only supported for recover-started');
|
|
993
|
+
if (values['--auto'] !== true) operationInvalid('--auto is required for recover-started');
|
|
994
|
+
return { family, request: input as unknown as TaskLifecycleControlRequest };
|
|
995
|
+
} else if (values['--auto'] !== undefined) {
|
|
996
|
+
operationInvalid('--auto is only supported for recover-started');
|
|
1003
997
|
}
|
|
1004
998
|
return { family, request: input as unknown as TaskLifecycleControlRequest };
|
|
1005
999
|
}
|
|
@@ -1092,8 +1086,5 @@ export function parseTaskControlOperation(
|
|
|
1092
1086
|
if (recoverable !== 'true' && recoverable !== 'false') operationInvalid('--recoverable must be true or false');
|
|
1093
1087
|
input.code = value(values, '--code'); input.message = value(values, '--message'); input.recoverable = recoverable === 'true';
|
|
1094
1088
|
}
|
|
1095
|
-
|
|
1096
|
-
? undefined
|
|
1097
|
-
: { gitWorktreeRoot: value(values, '--git-worktree-root') };
|
|
1098
|
-
return { family, taskRef, intent: parsedIntent, input, ...(options ? { options } : {}) };
|
|
1089
|
+
return { family, taskRef, intent: parsedIntent, input };
|
|
1099
1090
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { isCompletionEvidence } from './orchestration.ts';
|
|
3
|
-
import { TASK_WORKFLOW_OPERATIONS } from './workflow-command.ts';
|
|
4
|
-
import { sameRecoveryWarning } from './recovery-warning.ts';
|
|
5
2
|
|
|
6
3
|
export type ControlRecoveryOutcome = 'not-executed' | 'in-progress' | 'success' | 'failure' | 'unknown' | 'rejected';
|
|
7
4
|
|
|
@@ -16,7 +13,7 @@ export function parseControlOutput(output: string | null): Record<string, unknow
|
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
export type ControlRecoveryOperation = Readonly<{
|
|
19
|
-
family: 'task-lifecycle' | 'task-finalization' | 'task-
|
|
16
|
+
family: 'task-lifecycle' | 'task-finalization' | 'task-create' | 'codex-controller';
|
|
20
17
|
intent: string;
|
|
21
18
|
class: string;
|
|
22
19
|
}>;
|
|
@@ -55,21 +52,10 @@ export type ControlRecoveryDecision = Readonly<{
|
|
|
55
52
|
}>;
|
|
56
53
|
|
|
57
54
|
const LIFECYCLE_INTENTS = ['block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'] as const;
|
|
58
|
-
const ORCHESTRATION_INTENTS = [
|
|
59
|
-
'begin-or-resume', 'route.read', 'route.clean-completion', 'status', 'prepare', 'dispatch',
|
|
60
|
-
'await-activation', 'recover-prepared', 'hook-start', 'hook-stop', 'advance', 'pause'
|
|
61
|
-
] as const;
|
|
62
|
-
|
|
63
55
|
export const SANDBOX_CONTROL_RECOVERY_OPERATIONS: readonly ControlRecoveryOperation[] = Object.freeze([
|
|
64
56
|
...LIFECYCLE_INTENTS.map((intent) => ({ family: 'task-lifecycle' as const, intent, class: 'lifecycle-mutation' })),
|
|
65
57
|
{ family: 'task-finalization', intent: 'complete', class: 'finalization' },
|
|
66
|
-
...ORCHESTRATION_INTENTS.map((intent) => ({
|
|
67
|
-
family: 'task-orchestration' as const,
|
|
68
|
-
intent,
|
|
69
|
-
class: intent === 'route.clean-completion' ? 'route.clean-completion' : intent === 'route.read' || intent === 'status' ? 'read-only' : 'orchestration'
|
|
70
|
-
})),
|
|
71
58
|
{ family: 'task-create', intent: 'create', class: 'task-create' },
|
|
72
|
-
...TASK_WORKFLOW_OPERATIONS.map((intent) => ({ family: 'task-workflow' as const, intent, class: intent === 'artifact-inspect' || intent === 'decision-next-id' ? 'read-only' : 'workflow' })),
|
|
73
59
|
...(['open', 'close', 'verify'] as const).map((intent) => ({ family: 'codex-controller' as const, intent, class: 'codex-controller' }))
|
|
74
60
|
]);
|
|
75
61
|
|
|
@@ -95,29 +81,7 @@ function domainEvidenceMatches(
|
|
|
95
81
|
): boolean {
|
|
96
82
|
if (!domain) return false;
|
|
97
83
|
if (domain.consistent !== true) return false;
|
|
98
|
-
if (operation.
|
|
99
|
-
const completion = domain.completionEvidence as Record<string, unknown> | undefined;
|
|
100
|
-
const snapshot = domain.snapshot as Record<string, unknown> | undefined;
|
|
101
|
-
const lastReviewedCommit = domain.lastReviewedCommit;
|
|
102
|
-
return result.status === 'completed'
|
|
103
|
-
&& domain.status === 'completed'
|
|
104
|
-
&& domain.pendingDelegation === null
|
|
105
|
-
&& isCompletionEvidence(completion)
|
|
106
|
-
&& snapshot?.head === completion.head
|
|
107
|
-
&& snapshot.headTree === completion.headTree
|
|
108
|
-
&& snapshot.worktreeTree === completion.worktreeTree
|
|
109
|
-
&& lastReviewedCommit === completion.lastReviewedCommit;
|
|
110
|
-
}
|
|
111
|
-
if (operation.family === 'task-lifecycle' && operation.intent === 'recover-started') {
|
|
112
|
-
const retryRequired = domain.recoveryState === 'retry-required'
|
|
113
|
-
&& sameRecoveryWarning(domain.warning, result.warning);
|
|
114
|
-
return result.targetState === 'active'
|
|
115
|
-
&& ['applied', 'no-op'].includes(String(result.status))
|
|
116
|
-
&& domain.recovery === true
|
|
117
|
-
&& domain.targetState === 'active'
|
|
118
|
-
&& (domain.recoveryState === 'released' || retryRequired);
|
|
119
|
-
}
|
|
120
|
-
if (operation.class === 'read-only') return result.changed === false && domain.snapshotValid === true;
|
|
84
|
+
if (operation.family === 'task-lifecycle' && operation.intent === 'recover-started') return false;
|
|
121
85
|
return true;
|
|
122
86
|
}
|
|
123
87
|
|
|
@@ -167,11 +131,7 @@ export function classifySandboxControlRecovery(input: ControlRecoveryInput): Con
|
|
|
167
131
|
return {
|
|
168
132
|
outcome: 'success',
|
|
169
133
|
responseReconstructable: true,
|
|
170
|
-
reasonCode:
|
|
171
|
-
? 'RECOVERY_ROUTE_COMPLETION_NOOP'
|
|
172
|
-
: input.operation.intent === 'recover-started' && input.domain?.recoveryState === 'retry-required'
|
|
173
|
-
? 'RECOVERY_RELEASE_RETRY_REQUIRED'
|
|
174
|
-
: 'RECOVERY_TERMINAL_AND_DOMAIN_MATCH'
|
|
134
|
+
reasonCode: 'RECOVERY_TERMINAL_AND_DOMAIN_MATCH'
|
|
175
135
|
};
|
|
176
136
|
}
|
|
177
137
|
|
|
@@ -248,7 +248,7 @@ function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
|
|
|
248
248
|
&& host !== undefined
|
|
249
249
|
&& Number.isSafeInteger(host.stopRevision)
|
|
250
250
|
&& (host.stopRevision as number) > host.startRevision
|
|
251
|
-
&& host.consumer ===
|
|
251
|
+
&& host.consumer === receipt.id
|
|
252
252
|
&& exactText(host.consumedAt);
|
|
253
253
|
}
|
|
254
254
|
|
|
@@ -562,7 +562,7 @@ function abortActivatedDelegation(
|
|
|
562
562
|
event.childId !== receipt.childId
|
|
563
563
|
|| !Number.isSafeInteger(event.stopRevision)
|
|
564
564
|
|| event.stopRevision <= receipt.hostEvidence.startRevision
|
|
565
|
-
|| event.consumer !==
|
|
565
|
+
|| event.consumer !== receipt.id
|
|
566
566
|
|| !exactText(event.consumedAt)
|
|
567
567
|
) {
|
|
568
568
|
return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'recovery stop evidence does not match the activated delegation');
|
|
@@ -6,7 +6,7 @@ export type TaskControlBindingEvidence = (taskId: string, binding: TaskControlBi
|
|
|
6
6
|
export function taskFinalizationReceiptState(receipt: TaskFinalizationReceipt): 'pending' | 'unresolved' | 'complete' {
|
|
7
7
|
if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
|
|
8
8
|
|| receipt.verification === 'pending' || receipt.summary === 'pending'
|
|
9
|
-
|| receipt.
|
|
9
|
+
|| receipt.warningProjection !== 'done') return 'pending';
|
|
10
10
|
if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open')) return 'unresolved';
|
|
11
11
|
return 'complete';
|
|
12
12
|
}
|