@fitlab-ai/agent-infra 0.11.1 → 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/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +22 -27
- package/dist/lib/internal/platform-comment.js +59 -0
- package/dist/lib/internal/sandbox-control.js +2 -171
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +7 -2
- package/dist/lib/internal/task-review.js +5 -5
- package/dist/lib/internal/task-validate.js +0 -6
- package/dist/lib/platform/comment-safety.js +7 -1
- package/dist/lib/platform/github-provider.js +13 -3
- package/dist/lib/platform/issue-comments.js +93 -15
- package/dist/lib/platform/pr-summary.js +7 -2
- package/dist/lib/platform/provider-contract.d.ts +2 -0
- package/dist/lib/platform/provider-validation.js +7 -2
- package/dist/lib/platform/verification-sync.js +19 -2
- package/dist/lib/sandbox/control/client.js +3 -44
- package/dist/lib/sandbox/control/executor.js +14 -117
- package/dist/lib/sandbox/control/finalization-response.js +8 -2
- 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 +2 -1
- package/dist/lib/task/artifact-command.js +6 -6
- package/dist/lib/task/codex-orchestration.js +10 -208
- package/dist/lib/task/commit-operation.js +3 -18
- package/dist/lib/task/control-authority.js +35 -44
- package/dist/lib/task/control-recovery.js +3 -42
- package/dist/lib/task/delegation-receipts.js +16 -78
- package/dist/lib/task/events.js +89 -237
- package/dist/lib/task/finalization-artifacts.js +1 -3
- package/dist/lib/task/finalization-state.js +2 -1
- package/dist/lib/task/finalization.js +208 -57
- package/dist/lib/task/lifecycle-execution.js +2 -11
- package/dist/lib/task/lifecycle.js +1 -1
- package/dist/lib/task/local-artifact-finalization.js +25 -242
- package/dist/lib/task/manual-validation-receipt.js +8 -3
- package/dist/lib/task/orchestration.js +12 -24
- package/dist/lib/task/review-command.js +0 -1
- package/dist/lib/task/review-finalization.js +46 -211
- 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/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +22 -26
- package/lib/internal/platform-comment.ts +38 -0
- package/lib/internal/sandbox-control.ts +1 -185
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +8 -4
- package/lib/internal/task-review.ts +5 -5
- package/lib/internal/task-validate.ts +0 -5
- package/lib/platform/comment-safety.ts +8 -0
- package/lib/platform/github-provider.ts +11 -2
- package/lib/platform/issue-comments.ts +102 -17
- package/lib/platform/pr-summary.ts +10 -2
- package/lib/platform/provider-contract.ts +2 -0
- package/lib/platform/provider-validation.ts +6 -2
- package/lib/platform/verification-sync.ts +19 -2
- package/lib/sandbox/control/client.ts +5 -68
- package/lib/sandbox/control/executor.ts +14 -125
- package/lib/sandbox/control/finalization-response.ts +8 -2
- 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 +2 -1
- package/lib/task/artifact-command.ts +6 -7
- package/lib/task/codex-orchestration.ts +8 -241
- package/lib/task/commit-operation.ts +3 -23
- package/lib/task/control-authority.ts +39 -55
- package/lib/task/control-recovery.ts +3 -43
- package/lib/task/delegation-receipts.ts +14 -81
- package/lib/task/events.ts +106 -255
- package/lib/task/finalization-artifacts.ts +1 -3
- package/lib/task/finalization-state.ts +2 -1
- package/lib/task/finalization.ts +221 -65
- package/lib/task/lifecycle-execution.ts +10 -21
- package/lib/task/lifecycle.ts +2 -2
- package/lib/task/local-artifact-finalization.ts +23 -281
- package/lib/task/manual-validation-receipt.ts +8 -2
- package/lib/task/orchestration.ts +30 -53
- package/lib/task/review-command.ts +0 -1
- package/lib/task/review-finalization.ts +45 -193
- package/package.json +2 -2
- package/templates/.agents/rules/lifecycle-orchestration.en.md +23 -9
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +23 -9
- package/templates/.agents/rules/local-artifact-repair.en.md +14 -60
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -60
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
- 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 +7 -8
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- 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 +10 -9
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +10 -9
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
- package/dist/lib/sandbox/control/task-workflow.js +0 -74
- package/dist/lib/sandbox/control/workflow-executor.js +0 -157
- 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 -18
- package/dist/lib/task/workflow-dispatch.js +0 -161
- package/lib/sandbox/control/task-workflow.ts +0 -95
- package/lib/sandbox/control/workflow-executor.ts +0 -183
- package/lib/task/lifecycle-recovery.ts +0 -691
- package/lib/task/recovery-warning.ts +0 -21
- package/lib/task/workflow-command.ts +0 -19
- package/lib/task/workflow-dispatch.ts +0 -171
|
@@ -1,39 +1,15 @@
|
|
|
1
1
|
import { preflightCodexLifecycleEvidence, resolveCodexSpawnedChild, resolveCodexTerminal, resolveCodexThread } from '../agent-clients/adapters/codex-lifecycle/app-server.js';
|
|
2
2
|
import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.js';
|
|
3
|
-
import {
|
|
4
|
-
import { computeLifecycleBuildIdentity, verifyLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
5
|
-
import { verifyCodexSandboxControllerContextWithWarnings as verifySandboxControllerWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
|
|
6
|
-
import { verifyCodexSandboxControllerContextWithWarnings as verifyControllerContextFileWithWarnings } from '../agent-clients/adapters/codex-lifecycle/controller-context.js';
|
|
3
|
+
import { computeLifecycleBuildIdentity, } from '../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
7
4
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
8
|
-
import { activateMatchingOrchestrationDelegation, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, pauseMatchingOrchestrationDelegation, prepareOrchestrationDelegation,
|
|
9
|
-
import { randomUUID, createHash } from 'node:crypto';
|
|
10
|
-
import { consumeLifecycleRecoveryAttestation, issueLifecycleRecoveryAttestation, validateLifecycleRecoveryAttestation } from './control-authority.js';
|
|
11
|
-
function buildIdentityDigest(value) {
|
|
12
|
-
return createHash('sha256').update(JSON.stringify(value), 'utf8').digest('hex');
|
|
13
|
-
}
|
|
5
|
+
import { activateMatchingOrchestrationDelegation, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, pauseMatchingOrchestrationDelegation, prepareOrchestrationDelegation, reconcileMatchingOrchestrationDelegation, sealMatchingOrchestrationDelegationWithHostEvidence } from './orchestration.js';
|
|
14
6
|
function coreOptions(options) {
|
|
15
7
|
return { ...options.orchestrationOptions, repoRoot: options.repoRoot ?? options.orchestrationOptions?.repoRoot };
|
|
16
8
|
}
|
|
17
|
-
function bridgeFailure(code, message
|
|
9
|
+
function bridgeFailure(code, message) {
|
|
18
10
|
return {
|
|
19
11
|
status: 'failed', changed: false, taskId: null, run: null, next: null,
|
|
20
|
-
error: { code, message
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
function capabilityFailure(error) {
|
|
24
|
-
const detailValue = error instanceof Error
|
|
25
|
-
? error.detail
|
|
26
|
-
: undefined;
|
|
27
|
-
return {
|
|
28
|
-
code: error instanceof Error && error.name.startsWith('CODEX_CAPABILITY_')
|
|
29
|
-
? error.name
|
|
30
|
-
: 'ORCHESTRATION_CLIENT_PREFLIGHT_FAILED',
|
|
31
|
-
message: error instanceof Error ? error.message : String(error),
|
|
32
|
-
...(error instanceof Error
|
|
33
|
-
&& error.name === 'CODEX_CAPABILITY_PROVENANCE_MISMATCH'
|
|
34
|
-
&& isCodexCapabilityProvenanceDetail(detailValue)
|
|
35
|
-
? { detail: detailValue }
|
|
36
|
-
: {})
|
|
12
|
+
error: { code, message }
|
|
37
13
|
};
|
|
38
14
|
}
|
|
39
15
|
function pauseBridge(code, message, options) {
|
|
@@ -47,28 +23,6 @@ function requiredStore(options) {
|
|
|
47
23
|
throw new Error('Codex lifecycle store is required for orchestration bridge events');
|
|
48
24
|
return options.store;
|
|
49
25
|
}
|
|
50
|
-
function brokerControllerBinding() {
|
|
51
|
-
const raw = process.env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING;
|
|
52
|
-
if (!raw)
|
|
53
|
-
return null;
|
|
54
|
-
let value;
|
|
55
|
-
try {
|
|
56
|
-
value = JSON.parse(raw);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BINDING_INVALID');
|
|
60
|
-
}
|
|
61
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
62
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BINDING_INVALID');
|
|
63
|
-
}
|
|
64
|
-
const binding = value;
|
|
65
|
-
if (Object.keys(binding).sort().join(',') !== 'controlGeneration,instanceDigest'
|
|
66
|
-
|| typeof binding.instanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(binding.instanceDigest)
|
|
67
|
-
|| typeof binding.controlGeneration !== 'string' || binding.controlGeneration.length === 0) {
|
|
68
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BINDING_INVALID');
|
|
69
|
-
}
|
|
70
|
-
return binding;
|
|
71
|
-
}
|
|
72
26
|
async function prepareCodexOrchestrationDelegation(taskRef, input, options = {}) {
|
|
73
27
|
if (input.client !== 'codex')
|
|
74
28
|
return prepareOrchestrationDelegation(taskRef, input, coreOptions(options));
|
|
@@ -77,152 +31,13 @@ async function prepareCodexOrchestrationDelegation(taskRef, input, options = {})
|
|
|
77
31
|
const resolved = resolveTaskRef(taskRef, { repoRoot });
|
|
78
32
|
if (!resolved.ok)
|
|
79
33
|
return bridgeFailure(resolved.code, resolved.message);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (!capabilityRef) {
|
|
83
|
-
return bridgeFailure('ORCHESTRATION_CODEX_CAPABILITY_REQUIRED', 'Codex prepare requires a current-session capability reference');
|
|
84
|
-
}
|
|
85
|
-
const buildIdentity = options.buildIdentity ?? computeLifecycleBuildIdentity(repoRoot);
|
|
86
|
-
const brokerController = options.controllerBinding ?? brokerControllerBinding();
|
|
87
|
-
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
88
|
-
const contextVerification = contextPath
|
|
89
|
-
? (brokerController
|
|
90
|
-
? verifyControllerContextFileWithWarnings(contextPath, {
|
|
91
|
-
repoRoot,
|
|
92
|
-
generation: process.env.AGENT_INFRA_CONTROL_GENERATION ?? brokerController.controlGeneration
|
|
93
|
-
})
|
|
94
|
-
: verifySandboxControllerWithWarnings(contextPath, { repoRoot }))
|
|
95
|
-
: null;
|
|
96
|
-
const identityWarnings = [...(contextVerification?.warnings ?? [])];
|
|
97
|
-
const localController = contextVerification?.context ?? null;
|
|
98
|
-
if (localController && brokerController
|
|
99
|
-
&& (localController.controllerInstanceDigest !== brokerController.instanceDigest
|
|
100
|
-
|| localController.controlGeneration !== brokerController.controlGeneration)) {
|
|
101
|
-
return bridgeFailure('CODEX_SANDBOX_CONTROLLER_BINDING_MISMATCH', 'broker and local controller bindings do not match');
|
|
102
|
-
}
|
|
103
|
-
const controller = brokerController ?? (localController ? {
|
|
104
|
-
instanceDigest: localController.controllerInstanceDigest,
|
|
105
|
-
controlGeneration: localController.controlGeneration
|
|
106
|
-
} : null);
|
|
107
|
-
const capabilityStore = options.capabilityStore ?? createCodexCapabilityStore();
|
|
108
|
-
const attested = capabilityStore.inspectReference(capabilityRef);
|
|
109
|
-
const capabilityIdentity = verifyLifecycleBuildIdentity(attested.buildIdentity, buildIdentity);
|
|
110
|
-
if (!capabilityIdentity.ok) {
|
|
111
|
-
return bridgeFailure(capabilityIdentity.code, capabilityIdentity.message);
|
|
112
|
-
}
|
|
113
|
-
identityWarnings.push(...capabilityIdentity.warnings);
|
|
114
|
-
const lifecycleProvenance = {
|
|
115
|
-
...attested.buildIdentity,
|
|
116
|
-
hookDefinitionHash: preflight.hookDefinitionHash,
|
|
117
|
-
...preflight.hookProvenance,
|
|
118
|
-
capabilitySessionId: attested.sessionId,
|
|
119
|
-
capabilityTurnId: attested.turnId,
|
|
120
|
-
capabilityToolUseId: attested.toolUseId,
|
|
121
|
-
controllerInstanceDigest: controller?.instanceDigest ?? null,
|
|
122
|
-
controlGeneration: controller?.controlGeneration ?? null
|
|
123
|
-
};
|
|
124
|
-
const capabilityExpected = {
|
|
125
|
-
taskId: resolved.taskId,
|
|
126
|
-
hookDefinitionHash: preflight.hookDefinitionHash,
|
|
127
|
-
buildIdentity,
|
|
128
|
-
...(controller ? { controller: {
|
|
129
|
-
instanceDigest: controller.instanceDigest,
|
|
130
|
-
controlGeneration: controller.controlGeneration
|
|
131
|
-
} } : {})
|
|
132
|
-
};
|
|
133
|
-
if (input.lifecycleRecoveryAttestation) {
|
|
134
|
-
try {
|
|
135
|
-
validateLifecycleRecoveryAttestation(input.lifecycleRecoveryAttestation);
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
return bridgeFailure('LIFECYCLE_AUTHORITY_ATTESTATION_INVALID', error instanceof Error ? error.message : String(error));
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
let lifecycleRecoveryAttestation = input.lifecycleRecoveryAttestation ?? null;
|
|
142
|
-
const routed = input.capabilityRef && !lifecycleRecoveryAttestation
|
|
143
|
-
? routeOrchestration(taskRef, coreOptions(options))
|
|
144
|
-
: null;
|
|
145
|
-
if (routed && !routed.next)
|
|
146
|
-
return routed;
|
|
147
|
-
if (routed?.next && ['analysis', 'plan', 'code'].includes(routed.next.stage)) {
|
|
148
|
-
const recoveryRequest = {
|
|
149
|
-
version: 1,
|
|
150
|
-
requestId: randomUUID(),
|
|
151
|
-
operationId: randomUUID(),
|
|
152
|
-
phase: 'orchestration.prepare',
|
|
153
|
-
taskId: resolved.taskId,
|
|
154
|
-
family: routed.next.stage === 'analysis' ? 'analysis'
|
|
155
|
-
: routed.next.stage === 'plan' ? 'plan' : 'code',
|
|
156
|
-
artifact: routed.next.artifact,
|
|
157
|
-
round: routed.next.round,
|
|
158
|
-
lifecycleRequestId: randomUUID(),
|
|
159
|
-
authorityRef: capabilityRef,
|
|
160
|
-
expectedControlGeneration: controller?.controlGeneration ?? '',
|
|
161
|
-
expectedControllerInstanceDigest: controller?.instanceDigest ?? '0'.repeat(64),
|
|
162
|
-
expectedBuildIdentityDigest: buildIdentityDigest(buildIdentity),
|
|
163
|
-
expectedHookDefinitionHash: preflight.hookDefinitionHash
|
|
164
|
-
};
|
|
165
|
-
const authority = issueLifecycleRecoveryAttestation(recoveryRequest, {
|
|
166
|
-
capabilityStore,
|
|
167
|
-
...(controller ? { controllerBinding: controller } : {}),
|
|
168
|
-
buildIdentity
|
|
169
|
-
});
|
|
170
|
-
if (authority.status === 'rejected' || !authority.attestation) {
|
|
171
|
-
return bridgeFailure(authority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED', authority.error?.message ?? 'lifecycle authority was rejected');
|
|
172
|
-
}
|
|
173
|
-
lifecycleRecoveryAttestation = authority.attestation;
|
|
174
|
-
}
|
|
175
|
-
const prepared = prepareOrchestrationDelegation(taskRef, {
|
|
176
|
-
...input,
|
|
177
|
-
lifecycleProvenance,
|
|
178
|
-
lifecycleRecoveryAttestation
|
|
179
|
-
}, {
|
|
180
|
-
...coreOptions(options),
|
|
181
|
-
validateLifecycleCapability: () => {
|
|
182
|
-
try {
|
|
183
|
-
const validated = capabilityStore.validateReference(capabilityRef, capabilityExpected);
|
|
184
|
-
if (validated.sessionId !== attested.sessionId
|
|
185
|
-
|| validated.turnId !== attested.turnId
|
|
186
|
-
|| validated.toolUseId !== attested.toolUseId) {
|
|
187
|
-
return {
|
|
188
|
-
code: 'CODEX_CAPABILITY_IDENTITY_CHANGED',
|
|
189
|
-
message: 'capability identity changed during validation'
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
return capabilityFailure(error);
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
consumeLifecycleCapability: () => null
|
|
199
|
-
});
|
|
200
|
-
if (lifecycleRecoveryAttestation && !options.deferLifecycleRecoveryConsumption && prepared.status !== 'failed') {
|
|
201
|
-
try {
|
|
202
|
-
consumeLifecycleRecoveryAttestation(lifecycleRecoveryAttestation);
|
|
203
|
-
}
|
|
204
|
-
catch (error) {
|
|
205
|
-
return bridgeFailure(error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED', error instanceof Error ? error.message : String(error));
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return identityWarnings.length > 0 && prepared.status !== 'failed'
|
|
209
|
-
? { ...prepared, warnings: Object.freeze(identityWarnings) }
|
|
210
|
-
: prepared;
|
|
34
|
+
await (options.preflight ?? preflightCodexLifecycleEvidence)(repoRoot);
|
|
35
|
+
return prepareOrchestrationDelegation(taskRef, input, coreOptions(options));
|
|
211
36
|
}
|
|
212
37
|
catch (error) {
|
|
213
38
|
if (error instanceof OrchestrationStateError)
|
|
214
39
|
return bridgeFailure(error.code, error.message);
|
|
215
|
-
|
|
216
|
-
? error.detail
|
|
217
|
-
: undefined;
|
|
218
|
-
const detail = error instanceof Error
|
|
219
|
-
&& error.name === 'CODEX_CAPABILITY_PROVENANCE_MISMATCH'
|
|
220
|
-
&& isCodexCapabilityProvenanceDetail(detailValue)
|
|
221
|
-
? detailValue
|
|
222
|
-
: undefined;
|
|
223
|
-
return bridgeFailure(error instanceof Error && error.name.startsWith('CODEX_CAPABILITY_')
|
|
224
|
-
? error.name
|
|
225
|
-
: 'ORCHESTRATION_CLIENT_PREFLIGHT_FAILED', error instanceof Error ? error.message : String(error), detail);
|
|
40
|
+
return bridgeFailure('ORCHESTRATION_CLIENT_PREFLIGHT_FAILED', error instanceof Error ? error.message : String(error));
|
|
226
41
|
}
|
|
227
42
|
}
|
|
228
43
|
async function activateCodexOrchestrationDelegation(childThreadId, options = {}) {
|
|
@@ -248,16 +63,6 @@ async function activateCodexOrchestrationDelegation(childThreadId, options = {})
|
|
|
248
63
|
toolUseId: evidence.spawnToolUseId
|
|
249
64
|
});
|
|
250
65
|
const buildIdentity = options.buildIdentity ?? computeLifecycleBuildIdentity(repoRoot);
|
|
251
|
-
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
252
|
-
const contextVerification = contextPath
|
|
253
|
-
? verifySandboxControllerWithWarnings(contextPath, { repoRoot })
|
|
254
|
-
: null;
|
|
255
|
-
const controller = options.controllerBinding
|
|
256
|
-
?? brokerControllerBinding()
|
|
257
|
-
?? (contextVerification?.context ? {
|
|
258
|
-
instanceDigest: contextVerification.context.controllerInstanceDigest,
|
|
259
|
-
controlGeneration: contextVerification.context.controlGeneration
|
|
260
|
-
} : null);
|
|
261
66
|
const activated = activateMatchingOrchestrationDelegation('codex', {
|
|
262
67
|
nativeAgent: evidence.nativeAgent,
|
|
263
68
|
childId: evidence.childThreadId,
|
|
@@ -279,14 +84,11 @@ async function activateCodexOrchestrationDelegation(childThreadId, options = {})
|
|
|
279
84
|
capabilityTurnId: evidence.parentTurnId,
|
|
280
85
|
spawnToolUseId: evidence.spawnToolUseId,
|
|
281
86
|
spawnObservedAt: record.spawnObservedAt ?? undefined,
|
|
282
|
-
controllerInstanceDigest:
|
|
283
|
-
controlGeneration:
|
|
87
|
+
controllerInstanceDigest: null,
|
|
88
|
+
controlGeneration: null
|
|
284
89
|
}
|
|
285
90
|
}, coreOptions(options));
|
|
286
|
-
|
|
287
|
-
return warnings.length > 0 && activated.status !== 'failed'
|
|
288
|
-
? { ...activated, warnings: Object.freeze(warnings) }
|
|
289
|
-
: activated;
|
|
91
|
+
return activated;
|
|
290
92
|
}
|
|
291
93
|
catch (error) {
|
|
292
94
|
if (error instanceof OrchestrationStateError)
|
|
@@ -181,16 +181,6 @@ function checkpointIdentity(input, task, delivery) {
|
|
|
181
181
|
function checkpointTimestamp() {
|
|
182
182
|
return new Date().toISOString();
|
|
183
183
|
}
|
|
184
|
-
function samePreparedCheckpointIdentity(pending, identity) {
|
|
185
|
-
return pending.state === 'prepared'
|
|
186
|
-
&& pending.taskId === identity.taskId
|
|
187
|
-
&& pending.branch === identity.branch
|
|
188
|
-
&& pending.mode === identity.mode
|
|
189
|
-
&& pending.expectedHead === identity.expectedHead
|
|
190
|
-
&& pending.message === identity.message
|
|
191
|
-
&& pending.round === identity.round
|
|
192
|
-
&& JSON.stringify(pending.paths) === JSON.stringify(identity.paths);
|
|
193
|
-
}
|
|
194
184
|
function executeUnlocked(input, task, mode) {
|
|
195
185
|
const inspected = inspectGitWorkflow(input.cwd);
|
|
196
186
|
if (!inspected.snapshot)
|
|
@@ -225,16 +215,11 @@ function executeUnlocked(input, task, mode) {
|
|
|
225
215
|
};
|
|
226
216
|
}
|
|
227
217
|
if (pendingIntent && !sameCheckpointIntent(pendingIntent, identity)) {
|
|
228
|
-
|
|
229
|
-
return {
|
|
230
|
-
status: 'failed', changed: false, snapshot: inspected.snapshot, operations: [],
|
|
231
|
-
error: { code: 'COMMIT_INTENT_CONFLICT', message: 'A different checkpoint intent is still pending for this task' }
|
|
232
|
-
};
|
|
218
|
+
const timestamp = checkpointTimestamp();
|
|
233
219
|
pendingIntent = {
|
|
234
|
-
...
|
|
235
|
-
expectedTree: identity.expectedTree,
|
|
220
|
+
version: 1, ...identity,
|
|
236
221
|
digest: checkpointIntentDigest(identity),
|
|
237
|
-
updatedAt:
|
|
222
|
+
state: 'prepared', committedHead: null, createdAt: timestamp, updatedAt: timestamp
|
|
238
223
|
};
|
|
239
224
|
try {
|
|
240
225
|
writeCheckpointIntent(task.repoRoot, pendingIntent);
|
|
@@ -3,12 +3,11 @@ import crypto from 'node:crypto';
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
5
5
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
6
|
+
import { getAgentClientAdapter } from '../agent-clients/registry.js';
|
|
6
7
|
import { consumeHumanOverride, failureId, overrideDryRunConflict } from './human-override.js';
|
|
7
8
|
import { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, awaitOrchestrationDelegationActivation, beginOrResumeOrchestration, dispatchOrchestrationDelegation, OrchestrationStateError, pauseOrchestration, prepareOrchestrationDelegation, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration } from './orchestration.js';
|
|
8
|
-
import { prepareCodexOrchestrationDelegation } from './codex-orchestration.js';
|
|
9
9
|
import { applyTaskFinalization } from './finalization.js';
|
|
10
10
|
import { applyTaskLifecycle, taskLifecycleFailure } from './lifecycle.js';
|
|
11
|
-
import { recoverStartedLifecycleUnderLock, recoveryFailure } from './lifecycle-recovery.js';
|
|
12
11
|
import { locateActivityLog } from './activity-log.js';
|
|
13
12
|
import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
|
|
14
13
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
@@ -407,7 +406,6 @@ export function createSandboxExecutorExecutionContext(params) {
|
|
|
407
406
|
generation: params.generation,
|
|
408
407
|
manifestPath: absolute('manifestPath', params.manifestPath),
|
|
409
408
|
requestId: params.requestId,
|
|
410
|
-
...(params.diagnosticLog === undefined ? {} : { diagnosticLog: params.diagnosticLog }),
|
|
411
409
|
...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) }),
|
|
412
410
|
...(params.lifecycleRecoveryAttestation === undefined ? {} : {
|
|
413
411
|
lifecycleRecoveryAttestation: params.lifecycleRecoveryAttestation === null
|
|
@@ -450,11 +448,7 @@ function operationTaskId(context, taskRef) {
|
|
|
450
448
|
function domainOptions(context, options) {
|
|
451
449
|
return {
|
|
452
450
|
...options,
|
|
453
|
-
repoRoot: context.repoRoot
|
|
454
|
-
...(context.source === 'sandbox-executor' && context.diagnosticLog !== undefined
|
|
455
|
-
? { diagnosticLog: context.diagnosticLog }
|
|
456
|
-
: {}),
|
|
457
|
-
...(context.source === 'sandbox-executor' ? { gitWorktreeRoot: context.worktreeRoot } : {})
|
|
451
|
+
repoRoot: context.repoRoot
|
|
458
452
|
};
|
|
459
453
|
}
|
|
460
454
|
function lifecycleTaskId(request, repoRoot) {
|
|
@@ -475,7 +469,17 @@ async function applyLifecycleWithAuthority(context, request) {
|
|
|
475
469
|
}
|
|
476
470
|
const execute = async () => {
|
|
477
471
|
if (request.intent === 'recover-started') {
|
|
478
|
-
|
|
472
|
+
const adapter = isAgentClientId(request.agent)
|
|
473
|
+
? getAgentClientAdapter(request.agent).orchestrationAdapter
|
|
474
|
+
: undefined;
|
|
475
|
+
if (!adapter?.recoverStarted) {
|
|
476
|
+
return {
|
|
477
|
+
status: 'no-op', changed: false, targetState: 'active',
|
|
478
|
+
requestRef: request.taskRef, intent: 'recover-started', taskId: null,
|
|
479
|
+
receiptId: null, childId: null, error: null
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return adapter.recoverStarted(request, { repoRoot: context.repoRoot });
|
|
479
483
|
}
|
|
480
484
|
const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
|
|
481
485
|
if (lifecycleResult.status !== 'failed' || !request.overrideTicket)
|
|
@@ -511,7 +515,12 @@ async function applyLifecycleWithAuthority(context, request) {
|
|
|
511
515
|
if (!(error instanceof TaskExecutionLockError))
|
|
512
516
|
throw error;
|
|
513
517
|
if (request.intent === 'recover-started') {
|
|
514
|
-
return
|
|
518
|
+
return {
|
|
519
|
+
status: 'owner-unknown', changed: false, targetState: 'active',
|
|
520
|
+
requestRef: request.taskRef, intent: 'recover-started', taskId,
|
|
521
|
+
receiptId: null, childId: null,
|
|
522
|
+
error: { code: error.code, message: error.message }
|
|
523
|
+
};
|
|
515
524
|
}
|
|
516
525
|
return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
|
|
517
526
|
}
|
|
@@ -535,17 +544,13 @@ function orchestration(context, operation) {
|
|
|
535
544
|
client: input.client,
|
|
536
545
|
requestedModel: input.requestedModel,
|
|
537
546
|
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
538
|
-
...(input.capabilityRef === undefined ? {} : { capabilityRef: input.capabilityRef }),
|
|
539
|
-
...(context.lifecycleRecoveryAttestation === undefined
|
|
540
|
-
? {}
|
|
541
|
-
: { lifecycleRecoveryAttestation: context.lifecycleRecoveryAttestation })
|
|
542
547
|
};
|
|
543
|
-
|
|
544
|
-
|
|
548
|
+
const prepareDelegation = getAgentClientAdapter(prepareInput.client)
|
|
549
|
+
.orchestrationAdapter?.prepareDelegation;
|
|
550
|
+
if (prepareDelegation) {
|
|
551
|
+
return prepareDelegation(operation.taskRef, prepareInput, {
|
|
545
552
|
repoRoot: context.repoRoot,
|
|
546
553
|
orchestrationOptions: options,
|
|
547
|
-
deferLifecycleRecoveryConsumption: context.source === 'sandbox-executor',
|
|
548
|
-
...(context.controllerBinding ? { controllerBinding: context.controllerBinding } : {})
|
|
549
554
|
});
|
|
550
555
|
}
|
|
551
556
|
return prepareOrchestrationDelegation(operation.taskRef, prepareInput, options);
|
|
@@ -623,22 +628,22 @@ function operationInvalid(message) {
|
|
|
623
628
|
}
|
|
624
629
|
const LIFECYCLE_FLAGS = new Set([
|
|
625
630
|
'--agent', '--reason', '--unblock-condition', '--note', '--alert-number', '--staging-dir', '--issue-number',
|
|
626
|
-
'--
|
|
631
|
+
'--auto', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
|
|
627
632
|
]);
|
|
628
633
|
const FINALIZATION_FLAGS = new Set(['--agent']);
|
|
629
634
|
const ORCHESTRATION_FLAGS = new Set([
|
|
630
635
|
'--agent', '--max-steps', '--executor-model', '--executor-reasoning-effort', '--reviewer-model',
|
|
631
636
|
'--reviewer-reasoning-effort', '--client', '--requested-model', '--requested-reasoning-effort',
|
|
632
|
-
'--
|
|
637
|
+
'--parent-id', '--before-fingerprint', '--stage', '--round', '--artifact', '--role',
|
|
633
638
|
'--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
|
|
634
639
|
'--model-fallback-reason', '--reasoning-effort-fallback-reason', '--exit-code', '--after-fingerprint',
|
|
635
|
-
'--changed-paths', '--code', '--message', '--recoverable'
|
|
640
|
+
'--changed-paths', '--code', '--message', '--recoverable'
|
|
636
641
|
]);
|
|
637
642
|
function parseValues(args, start, flags) {
|
|
638
643
|
const values = {};
|
|
639
644
|
for (let index = start; index < args.length; index += 1) {
|
|
640
645
|
const flag = args[index];
|
|
641
|
-
if (flag === '--dry-run') {
|
|
646
|
+
if (flag === '--dry-run' || flag === '--auto') {
|
|
642
647
|
if (!flags.has(flag))
|
|
643
648
|
operationInvalid(`unknown option '${flag}'`);
|
|
644
649
|
if (values[flag] !== undefined)
|
|
@@ -699,31 +704,21 @@ export function parseTaskControlOperation(family, args) {
|
|
|
699
704
|
...(value(values, '--override-scope') ? { overrideScope: value(values, '--override-scope') } : {}),
|
|
700
705
|
...(value(values, '--alert-number') ? { alertNumber: Number(value(values, '--alert-number')) } : {}),
|
|
701
706
|
...(value(values, '--issue-number') ? { issueNumber: Number(value(values, '--issue-number')) } : {}),
|
|
702
|
-
...(
|
|
703
|
-
...(value(values, '--round') ? { round: Number(value(values, '--round')) } : {}),
|
|
704
|
-
...(value(values, '--artifact') ? { artifact: value(values, '--artifact') } : {}),
|
|
707
|
+
...(values['--auto'] === true ? { auto: true } : {}),
|
|
705
708
|
...(values['--dry-run'] === true ? { dryRun: true } : {})
|
|
706
709
|
};
|
|
707
710
|
if (intent === 'recover-started') {
|
|
708
|
-
const recoveryFlags = new Set(['--agent', '--
|
|
711
|
+
const recoveryFlags = new Set(['--agent', '--auto']);
|
|
709
712
|
for (const flag of Object.keys(values)) {
|
|
710
713
|
if (!recoveryFlags.has(flag))
|
|
711
714
|
operationInvalid(`option '${flag}' is not supported for recover-started`);
|
|
712
715
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
operationInvalid('--stage must be a supported lifecycle stage');
|
|
717
|
-
}
|
|
718
|
-
const round = Number(value(values, '--round'));
|
|
719
|
-
if (!Number.isSafeInteger(round) || round < 1)
|
|
720
|
-
operationInvalid('--round must be a positive integer');
|
|
721
|
-
if (value(values, '--reason').includes('\n') || value(values, '--reason').includes('\r')) {
|
|
722
|
-
operationInvalid('--reason must be a single line');
|
|
723
|
-
}
|
|
716
|
+
if (values['--auto'] !== true)
|
|
717
|
+
operationInvalid('--auto is required for recover-started');
|
|
718
|
+
return { family, request: input };
|
|
724
719
|
}
|
|
725
|
-
else if (values['--
|
|
726
|
-
operationInvalid('--
|
|
720
|
+
else if (values['--auto'] !== undefined) {
|
|
721
|
+
operationInvalid('--auto is only supported for recover-started');
|
|
727
722
|
}
|
|
728
723
|
return { family, request: input };
|
|
729
724
|
}
|
|
@@ -769,7 +764,6 @@ export function parseTaskControlOperation(family, args) {
|
|
|
769
764
|
if (parsedIntent === 'prepare') {
|
|
770
765
|
input.requestedModel = value(values, '--requested-model');
|
|
771
766
|
input.requestedReasoningEffort = value(values, '--requested-reasoning-effort');
|
|
772
|
-
input.capabilityRef = value(values, '--capability-ref');
|
|
773
767
|
}
|
|
774
768
|
if (parsedIntent === 'await-activation') {
|
|
775
769
|
required(values, ['--stage', '--round', '--artifact', '--role']);
|
|
@@ -827,9 +821,6 @@ export function parseTaskControlOperation(family, args) {
|
|
|
827
821
|
input.message = value(values, '--message');
|
|
828
822
|
input.recoverable = recoverable === 'true';
|
|
829
823
|
}
|
|
830
|
-
|
|
831
|
-
? undefined
|
|
832
|
-
: { gitWorktreeRoot: value(values, '--git-worktree-root') };
|
|
833
|
-
return { family, taskRef, intent: parsedIntent, input, ...(options ? { options } : {}) };
|
|
824
|
+
return { family, taskRef, intent: parsedIntent, input };
|
|
834
825
|
}
|
|
835
826
|
//# sourceMappingURL=control-authority.js.map
|
|
@@ -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) {
|