@fitlab-ai/agent-infra 0.11.11 → 0.11.13
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 +2 -2
- package/dist/lib/agent-clients/adapter.js +7 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
- package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
- package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
- package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
- package/dist/lib/agent-clients/adapters/codex.js +40 -1
- package/dist/lib/agent-clients/adapters/index.js +2 -10
- package/dist/lib/agent-clients/tokens.js +3 -2
- package/dist/lib/internal/agent-client.js +132 -72
- package/dist/lib/internal/cli-route-inventory.js +2 -2
- package/dist/lib/internal/sandbox-control.js +1 -81
- package/dist/lib/internal/task-finalization.js +26 -19
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +1 -1
- package/dist/lib/internal/task-validate.js +1 -1
- package/dist/lib/platform/github-client.js +33 -3
- package/dist/lib/platform/issues.js +18 -6
- package/dist/lib/runtime/agent-runtime.js +6 -29
- package/dist/lib/sandbox/commands/control-status.js +1 -0
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/ls.js +4 -5
- package/dist/lib/sandbox/commands/rm.js +1 -1
- package/dist/lib/sandbox/control/agent-operation.js +34 -0
- package/dist/lib/sandbox/control/client.js +14 -201
- package/dist/lib/sandbox/control/executor.js +12 -125
- package/dist/lib/sandbox/control/lifecycle.js +66 -4
- package/dist/lib/sandbox/control/protocol.js +15 -54
- package/dist/lib/sandbox/control/server.js +216 -281
- package/dist/lib/sandbox/control/task-view.js +0 -13
- package/dist/lib/sandbox/mounts.js +9 -1
- package/dist/lib/sandbox/recovery.js +31 -10
- package/dist/lib/sandbox/removal.js +28 -5
- package/dist/lib/sandbox/workspace-identity.js +18 -6
- package/dist/lib/sandbox/workspace-view.js +56 -19
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/control-authority.js +56 -264
- package/dist/lib/task/control-recovery.js +2 -3
- package/dist/lib/task/create.js +2 -2
- package/dist/lib/task/delegation-receipts.js +74 -161
- package/dist/lib/task/events.js +88 -44
- package/dist/lib/task/finalization.js +35 -6
- package/dist/lib/task/lifecycle.js +274 -115
- package/dist/lib/task/orchestration.js +201 -194
- package/dist/lib/task/runtime-paths.js +10 -0
- package/dist/lib/task/short-id.js +37 -27
- package/dist/lib/task/verification-engine.js +32 -26
- package/dist/lib/task/workspace-snapshot.js +1 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +39 -0
- package/lib/agent-clients/adapters/claude-code.ts +5 -1
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
- package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
- package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
- package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
- package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
- package/lib/agent-clients/adapters/codex.ts +44 -1
- package/lib/agent-clients/adapters/index.ts +7 -20
- package/lib/agent-clients/tokens.ts +4 -3
- package/lib/internal/agent-client.ts +142 -73
- package/lib/internal/cli-route-inventory.ts +2 -2
- package/lib/internal/sandbox-control.ts +0 -97
- package/lib/internal/task-finalization.ts +30 -18
- package/lib/internal/task-lifecycle.ts +1 -1
- package/lib/internal/task-operation-registry.ts +1 -1
- package/lib/internal/task-validate.ts +1 -1
- package/lib/platform/github-client.ts +34 -3
- package/lib/platform/issues.ts +18 -6
- package/lib/runtime/agent-runtime.ts +6 -41
- package/lib/sandbox/commands/control-status.ts +1 -0
- package/lib/sandbox/commands/create.ts +7 -1
- package/lib/sandbox/commands/ls.ts +4 -5
- package/lib/sandbox/commands/rm.ts +1 -1
- package/lib/sandbox/control/agent-operation.ts +49 -0
- package/lib/sandbox/control/client.ts +20 -254
- package/lib/sandbox/control/executor.ts +13 -136
- package/lib/sandbox/control/lifecycle.ts +67 -4
- package/lib/sandbox/control/protocol.ts +22 -66
- package/lib/sandbox/control/server.ts +21 -95
- package/lib/sandbox/control/task-view.ts +0 -19
- package/lib/sandbox/mounts.ts +10 -1
- package/lib/sandbox/recovery.ts +38 -17
- package/lib/sandbox/removal.ts +33 -7
- package/lib/sandbox/workspace-identity.ts +16 -6
- package/lib/sandbox/workspace-view.ts +50 -18
- package/lib/task/commands/ls.ts +3 -3
- package/lib/task/control-authority.ts +66 -378
- package/lib/task/control-recovery.ts +3 -4
- package/lib/task/create.ts +3 -3
- package/lib/task/delegation-receipts.ts +87 -231
- package/lib/task/events.ts +100 -42
- package/lib/task/finalization.ts +37 -7
- package/lib/task/lifecycle.ts +257 -109
- package/lib/task/orchestration.ts +260 -237
- package/lib/task/runtime-paths.ts +12 -0
- package/lib/task/short-id.ts +33 -40
- package/lib/task/verification-engine.ts +30 -25
- package/lib/task/workspace-snapshot.ts +1 -1
- package/package.json +1 -1
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
- package/templates/.agents/rules/README.en.md +2 -1
- package/templates/.agents/rules/README.zh-CN.md +2 -1
- package/templates/.agents/rules/next-step-output.en.md +6 -19
- package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
- package/templates/.agents/rules/strategy-pattern.en.md +25 -0
- package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
- package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
- package/dist/lib/internal/codex-lifecycle.js +0 -385
- package/dist/lib/task/codex-orchestration.js +0 -206
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
- package/lib/internal/codex-lifecycle.ts +0 -390
- package/lib/task/codex-orchestration.ts +0 -276
|
@@ -1,223 +1,13 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import crypto from 'node:crypto';
|
|
3
2
|
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
4
3
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
5
4
|
import { getAgentClientAdapter } from '../agent-clients/registry.js';
|
|
6
5
|
import { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, awaitOrchestrationDelegationActivation, beginOrResumeOrchestration, dispatchOrchestrationDelegation, OrchestrationStateError, pauseOrchestration, prepareOrchestrationDelegation, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration } from './orchestration.js';
|
|
7
6
|
import { applyTaskFinalization } from './finalization.js';
|
|
8
|
-
import { applyTaskLifecycle, taskLifecycleFailure } from './lifecycle.js';
|
|
9
|
-
import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
|
|
7
|
+
import { applyTaskLifecycle, applyTaskLifecycleWithinTaskLock, taskLifecycleFailure } from './lifecycle.js';
|
|
8
|
+
import { resolveTaskContext, resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
|
|
10
9
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
11
10
|
import { verifyTaskEvent } from './verification.js';
|
|
12
|
-
import { createCodexCapabilityStore } from '../agent-clients/adapters/codex-lifecycle/capability-store.js';
|
|
13
|
-
import { computeLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
14
|
-
const lifecycleAuthorityPhases = [
|
|
15
|
-
'orchestration.prepare', 'artifact.finalize-local', 'task-event.completed'
|
|
16
|
-
];
|
|
17
|
-
const lifecycleAuthorityFamilies = ['analysis', 'plan', 'code'];
|
|
18
|
-
const lifecycleAuthorityArtifact = /^(?:analysis|plan|code)(?:-r[1-9]\d*)?\.md$/u;
|
|
19
|
-
const lifecycleAuthorityDigest = (value) => crypto.createHash('sha256').update(value, 'utf8').digest('hex');
|
|
20
|
-
const lifecycleAuthorityBuildDigest = (value) => lifecycleAuthorityDigest(JSON.stringify(value));
|
|
21
|
-
export function lifecycleRecoveryAttestationDigest(value) {
|
|
22
|
-
return lifecycleAuthorityDigest(JSON.stringify(value));
|
|
23
|
-
}
|
|
24
|
-
function authorityExactKeys(value, keys) {
|
|
25
|
-
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
26
|
-
}
|
|
27
|
-
export function validateLifecycleAuthorityRequest(value) {
|
|
28
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
29
|
-
throw new Error('LIFECYCLE_AUTHORITY_REQUEST_INVALID');
|
|
30
|
-
const request = value;
|
|
31
|
-
if (!authorityExactKeys(request, [
|
|
32
|
-
'artifact', 'authorityRef', 'expectedBuildIdentityDigest', 'expectedControlGeneration',
|
|
33
|
-
'expectedControllerInstanceDigest', 'expectedHookDefinitionHash', 'family', 'lifecycleRequestId',
|
|
34
|
-
'operationId', 'phase', 'requestId', 'round', 'taskId', 'version'
|
|
35
|
-
])
|
|
36
|
-
|| request.version !== 1
|
|
37
|
-
|| typeof request.requestId !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(request.requestId)
|
|
38
|
-
|| typeof request.operationId !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(request.operationId)
|
|
39
|
-
|| !lifecycleAuthorityPhases.includes(request.phase)
|
|
40
|
-
|| typeof request.taskId !== 'string' || !/^TASK-\d{8}-\d{6}$/u.test(request.taskId)
|
|
41
|
-
|| !lifecycleAuthorityFamilies.includes(request.family)
|
|
42
|
-
|| typeof request.artifact !== 'string' || !lifecycleAuthorityArtifact.test(request.artifact)
|
|
43
|
-
|| !Number.isSafeInteger(request.round) || request.round < 1
|
|
44
|
-
|| typeof request.lifecycleRequestId !== 'string' || !request.lifecycleRequestId || /[\r\n]/u.test(request.lifecycleRequestId)
|
|
45
|
-
|| typeof request.authorityRef !== 'string' || !request.authorityRef || /[\r\n]/u.test(request.authorityRef)
|
|
46
|
-
|| typeof request.expectedControlGeneration !== 'string' || !request.expectedControlGeneration
|
|
47
|
-
|| typeof request.expectedControllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(request.expectedControllerInstanceDigest)
|
|
48
|
-
|| typeof request.expectedBuildIdentityDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(request.expectedBuildIdentityDigest)
|
|
49
|
-
|| typeof request.expectedHookDefinitionHash !== 'string' || !/^[a-f0-9]{64}$/u.test(request.expectedHookDefinitionHash)) {
|
|
50
|
-
throw new Error('LIFECYCLE_AUTHORITY_REQUEST_INVALID');
|
|
51
|
-
}
|
|
52
|
-
const expectedArtifactFamily = request.artifact;
|
|
53
|
-
if (!expectedArtifactFamily.startsWith(request.family))
|
|
54
|
-
throw new Error('LIFECYCLE_AUTHORITY_REQUEST_INVALID');
|
|
55
|
-
return request;
|
|
56
|
-
}
|
|
57
|
-
export function validateLifecycleRecoveryAttestation(value, now = Date.now()) {
|
|
58
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
59
|
-
throw new Error('LIFECYCLE_AUTHORITY_ATTESTATION_INVALID');
|
|
60
|
-
const attestation = value;
|
|
61
|
-
if (!authorityExactKeys(attestation, [
|
|
62
|
-
'artifact', 'attestationId', 'authorityRefDigest', 'buildIdentityDigest', 'controlGeneration',
|
|
63
|
-
'controllerInstanceDigest', 'expiresAt', 'family', 'hookDefinitionHash', 'issuedAt',
|
|
64
|
-
'lifecycleRequestId', 'operationId', 'phase', 'requestId', 'round', 'sessionId',
|
|
65
|
-
'taskId', 'toolUseId', 'turnId', 'version'
|
|
66
|
-
])
|
|
67
|
-
|| attestation.version !== 1
|
|
68
|
-
|| typeof attestation.attestationId !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(attestation.attestationId)
|
|
69
|
-
|| typeof attestation.authorityRefDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(attestation.authorityRefDigest)
|
|
70
|
-
|| typeof attestation.operationId !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(attestation.operationId)
|
|
71
|
-
|| !lifecycleAuthorityPhases.includes(attestation.phase)
|
|
72
|
-
|| typeof attestation.requestId !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(attestation.requestId)
|
|
73
|
-
|| typeof attestation.lifecycleRequestId !== 'string' || !attestation.lifecycleRequestId
|
|
74
|
-
|| typeof attestation.taskId !== 'string' || !/^TASK-\d{8}-\d{6}$/u.test(attestation.taskId)
|
|
75
|
-
|| !lifecycleAuthorityFamilies.includes(attestation.family)
|
|
76
|
-
|| typeof attestation.artifact !== 'string' || !lifecycleAuthorityArtifact.test(attestation.artifact)
|
|
77
|
-
|| !Number.isSafeInteger(attestation.round) || attestation.round < 1
|
|
78
|
-
|| typeof attestation.controlGeneration !== 'string' || !attestation.controlGeneration
|
|
79
|
-
|| typeof attestation.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(attestation.controllerInstanceDigest)
|
|
80
|
-
|| typeof attestation.sessionId !== 'string' || !attestation.sessionId
|
|
81
|
-
|| typeof attestation.turnId !== 'string' || !attestation.turnId
|
|
82
|
-
|| typeof attestation.toolUseId !== 'string' || !attestation.toolUseId
|
|
83
|
-
|| typeof attestation.buildIdentityDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(attestation.buildIdentityDigest)
|
|
84
|
-
|| typeof attestation.hookDefinitionHash !== 'string' || !/^[a-f0-9]{64}$/u.test(attestation.hookDefinitionHash)
|
|
85
|
-
|| !Number.isSafeInteger(attestation.issuedAt) || !Number.isSafeInteger(attestation.expiresAt)
|
|
86
|
-
|| attestation.expiresAt <= attestation.issuedAt
|
|
87
|
-
|| attestation.expiresAt <= now) {
|
|
88
|
-
throw new Error('LIFECYCLE_AUTHORITY_ATTESTATION_INVALID');
|
|
89
|
-
}
|
|
90
|
-
return attestation;
|
|
91
|
-
}
|
|
92
|
-
const authorityOperationMap = new WeakMap();
|
|
93
|
-
const authorityRecords = new Map();
|
|
94
|
-
const consumedAuthorityPhases = new Set();
|
|
95
|
-
export function issueLifecycleRecoveryAttestation(input, options = {}) {
|
|
96
|
-
let request;
|
|
97
|
-
try {
|
|
98
|
-
request = validateLifecycleAuthorityRequest(input);
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
return { version: 1, requestId: String(input?.requestId ?? ''), operationId: String(input?.operationId ?? ''), phase: input?.phase ?? 'artifact.finalize-local', status: 'rejected', attestation: null, error: { code: 'LIFECYCLE_AUTHORITY_REQUEST_INVALID', message: error instanceof Error ? error.message : String(error) } };
|
|
102
|
-
}
|
|
103
|
-
const binding = options.controllerBinding ?? null;
|
|
104
|
-
if (!binding)
|
|
105
|
-
return authorityRejected(request, 'LOCAL_EXECUTION_AUTHORITY_UNAVAILABLE', 'current controller authority is unavailable');
|
|
106
|
-
if (binding.controlGeneration !== request.expectedControlGeneration
|
|
107
|
-
|| binding.instanceDigest !== request.expectedControllerInstanceDigest) {
|
|
108
|
-
return authorityRejected(request, 'LIFECYCLE_AUTHORITY_CONTROLLER_MISMATCH', 'current controller binding does not match the request');
|
|
109
|
-
}
|
|
110
|
-
const buildIdentity = options.buildIdentity ?? computeLifecycleBuildIdentity(process.cwd());
|
|
111
|
-
if (lifecycleAuthorityBuildDigest(buildIdentity) !== request.expectedBuildIdentityDigest) {
|
|
112
|
-
return authorityRejected(request, 'LIFECYCLE_AUTHORITY_BUILD_MISMATCH', 'current lifecycle build does not match the request');
|
|
113
|
-
}
|
|
114
|
-
const store = options.capabilityStore ?? createCodexCapabilityStore();
|
|
115
|
-
const registry = options.operationAttestations ?? authorityOperationMap.get(store) ?? new Map();
|
|
116
|
-
authorityOperationMap.set(store, registry);
|
|
117
|
-
const key = `${request.requestId}\0${request.operationId}\0${request.phase}`;
|
|
118
|
-
const prior = registry.get(key);
|
|
119
|
-
if (prior)
|
|
120
|
-
return { version: 1, requestId: request.requestId, operationId: request.operationId, phase: request.phase, status: 'already-completed', attestation: prior, error: null };
|
|
121
|
-
try {
|
|
122
|
-
const expected = {
|
|
123
|
-
taskId: request.taskId,
|
|
124
|
-
hookDefinitionHash: request.expectedHookDefinitionHash,
|
|
125
|
-
buildIdentity,
|
|
126
|
-
controller: binding
|
|
127
|
-
};
|
|
128
|
-
const record = store.reserveReference(request.authorityRef, request.operationId, expected);
|
|
129
|
-
if (!record.sessionId || !record.turnId || !record.toolUseId)
|
|
130
|
-
throw new Error('CODEX_CAPABILITY_IDENTITY_INVALID: capability hook attestation is incomplete');
|
|
131
|
-
const now = (options.now ?? Date.now)();
|
|
132
|
-
const attestation = validateLifecycleRecoveryAttestation({
|
|
133
|
-
version: 1,
|
|
134
|
-
attestationId: crypto.randomUUID(),
|
|
135
|
-
authorityRefDigest: lifecycleAuthorityDigest(request.authorityRef),
|
|
136
|
-
operationId: request.operationId,
|
|
137
|
-
phase: request.phase,
|
|
138
|
-
requestId: request.requestId,
|
|
139
|
-
lifecycleRequestId: request.lifecycleRequestId,
|
|
140
|
-
taskId: request.taskId,
|
|
141
|
-
family: request.family,
|
|
142
|
-
artifact: request.artifact,
|
|
143
|
-
round: request.round,
|
|
144
|
-
controlGeneration: binding.controlGeneration,
|
|
145
|
-
controllerInstanceDigest: binding.instanceDigest,
|
|
146
|
-
sessionId: record.sessionId,
|
|
147
|
-
turnId: record.turnId,
|
|
148
|
-
toolUseId: record.toolUseId,
|
|
149
|
-
buildIdentityDigest: lifecycleAuthorityBuildDigest(buildIdentity),
|
|
150
|
-
hookDefinitionHash: request.expectedHookDefinitionHash,
|
|
151
|
-
issuedAt: now,
|
|
152
|
-
expiresAt: Math.min(record.expiresAt, now + 30_000)
|
|
153
|
-
}, now);
|
|
154
|
-
store.reserveRecoveryPhase(request.authorityRef, request.operationId, request.phase, request.requestId, expected);
|
|
155
|
-
registry.set(key, attestation);
|
|
156
|
-
authorityRecords.set(attestation.attestationId, { store, capabilityRef: request.authorityRef, expected });
|
|
157
|
-
return { version: 1, requestId: request.requestId, operationId: request.operationId, phase: request.phase, status: 'issued', attestation, error: null };
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
return authorityRejected(request, error instanceof Error && error.name.startsWith('CODEX_') ? error.name : 'LIFECYCLE_AUTHORITY_REJECTED', error instanceof Error ? error.message : String(error));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
export function consumeLifecycleRecoveryAttestation(value, now = Date.now()) {
|
|
164
|
-
const attestation = validateLifecycleRecoveryAttestation(value, now);
|
|
165
|
-
if (consumedAuthorityPhases.has(attestation.attestationId))
|
|
166
|
-
return;
|
|
167
|
-
const record = authorityRecords.get(attestation.attestationId);
|
|
168
|
-
if (!record)
|
|
169
|
-
throw new Error('LIFECYCLE_AUTHORITY_ATTESTATION_UNKNOWN');
|
|
170
|
-
record.store.consumeRecoveryPhase(record.capabilityRef, attestation.operationId, attestation.phase, attestation.requestId, record.expected);
|
|
171
|
-
if (attestation.phase === 'task-event.completed') {
|
|
172
|
-
record.store.consumeReference(record.capabilityRef, attestation.operationId, record.expected);
|
|
173
|
-
}
|
|
174
|
-
consumedAuthorityPhases.add(attestation.attestationId);
|
|
175
|
-
}
|
|
176
|
-
export function queryLifecycleRecoveryOperation(operationId, options = {}) {
|
|
177
|
-
if (!/^[a-f0-9-]{16,64}$/u.test(operationId)) {
|
|
178
|
-
throw new Error('LIFECYCLE_RECOVERY_OPERATION_INVALID');
|
|
179
|
-
}
|
|
180
|
-
const store = options.capabilityStore ?? createCodexCapabilityStore();
|
|
181
|
-
const registry = authorityOperationMap.get(store);
|
|
182
|
-
const known = [...(registry?.values() ?? [])].filter((value) => value.operationId === operationId);
|
|
183
|
-
const capabilities = store.findByRecoveryOperation(operationId);
|
|
184
|
-
const phaseMap = new Map();
|
|
185
|
-
for (const capability of capabilities) {
|
|
186
|
-
for (const phase of capability.recoveryPhases) {
|
|
187
|
-
phaseMap.set(`${phase.phase}\0${phase.requestId}`, {
|
|
188
|
-
phase: phase.phase,
|
|
189
|
-
requestId: phase.requestId,
|
|
190
|
-
attestationId: null,
|
|
191
|
-
state: phase.state === 'consumed' ? 'consumed' : 'observed'
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
for (const attestation of known) {
|
|
196
|
-
const key = `${attestation.phase}\0${attestation.requestId}`;
|
|
197
|
-
const durable = phaseMap.get(key);
|
|
198
|
-
phaseMap.set(key, {
|
|
199
|
-
phase: attestation.phase,
|
|
200
|
-
requestId: attestation.requestId,
|
|
201
|
-
attestationId: attestation.attestationId,
|
|
202
|
-
state: durable?.state === 'consumed' || consumedAuthorityPhases.has(attestation.attestationId)
|
|
203
|
-
? 'consumed' : 'issued'
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
const capabilityState = capabilities.some((record) => record.recoveryState === 'consumed')
|
|
207
|
-
? 'consumed'
|
|
208
|
-
: capabilities.some((record) => record.recoveryState === 'reserved') ? 'reserved' : 'unknown';
|
|
209
|
-
const committed = capabilityState === 'consumed';
|
|
210
|
-
return {
|
|
211
|
-
version: 1,
|
|
212
|
-
operationId,
|
|
213
|
-
status: committed ? 'committed' : capabilityState === 'reserved' || phaseMap.size > 0 ? 'in-progress' : 'unknown',
|
|
214
|
-
capabilityState,
|
|
215
|
-
phases: Object.freeze([...phaseMap.values()])
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
function authorityRejected(request, code, message) {
|
|
219
|
-
return { version: 1, requestId: request.requestId, operationId: request.operationId, phase: request.phase, status: 'rejected', attestation: null, error: { code, message } };
|
|
220
|
-
}
|
|
221
11
|
export function taskControlOperationKey(operation) {
|
|
222
12
|
if (operation.family === 'task-finalization')
|
|
223
13
|
return 'complete';
|
|
@@ -250,10 +40,6 @@ export function createDirectHostExecutionContext(params) {
|
|
|
250
40
|
repoRoot: absolute('repoRoot', params.repoRoot),
|
|
251
41
|
...(params.runtimeDir === undefined ? {} : { runtimeDir: absolute('runtimeDir', params.runtimeDir) }),
|
|
252
42
|
...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) }),
|
|
253
|
-
...(params.lifecycleRecoveryAttestation === undefined ? {} : {
|
|
254
|
-
lifecycleRecoveryAttestation: params.lifecycleRecoveryAttestation === null
|
|
255
|
-
? null : validateLifecycleRecoveryAttestation(params.lifecycleRecoveryAttestation)
|
|
256
|
-
})
|
|
257
43
|
};
|
|
258
44
|
}
|
|
259
45
|
export function createSandboxExecutorExecutionContext(params) {
|
|
@@ -275,10 +61,6 @@ export function createSandboxExecutorExecutionContext(params) {
|
|
|
275
61
|
manifestPath: absolute('manifestPath', params.manifestPath),
|
|
276
62
|
requestId: params.requestId,
|
|
277
63
|
...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) }),
|
|
278
|
-
...(params.lifecycleRecoveryAttestation === undefined ? {} : {
|
|
279
|
-
lifecycleRecoveryAttestation: params.lifecycleRecoveryAttestation === null
|
|
280
|
-
? null : validateLifecycleRecoveryAttestation(params.lifecycleRecoveryAttestation)
|
|
281
|
-
})
|
|
282
64
|
};
|
|
283
65
|
}
|
|
284
66
|
export function assertTaskControlExecutionContext(context) {
|
|
@@ -289,8 +71,6 @@ export function assertTaskControlExecutionContext(context) {
|
|
|
289
71
|
if (context.runtimeDir !== undefined)
|
|
290
72
|
absolute('runtimeDir', context.runtimeDir);
|
|
291
73
|
binding(context.controllerBinding);
|
|
292
|
-
if (context.lifecycleRecoveryAttestation)
|
|
293
|
-
validateLifecycleRecoveryAttestation(context.lifecycleRecoveryAttestation);
|
|
294
74
|
return;
|
|
295
75
|
}
|
|
296
76
|
if (context.mode !== 'task-bound-sandbox' || !/^TASK-\d{8}-\d{6}$/u.test(context.taskId)
|
|
@@ -305,13 +85,17 @@ export function assertTaskControlExecutionContext(context) {
|
|
|
305
85
|
invalidContext('runtimeDir is not bound to the manifest control root');
|
|
306
86
|
}
|
|
307
87
|
binding(context.controllerBinding);
|
|
308
|
-
if (context.lifecycleRecoveryAttestation)
|
|
309
|
-
validateLifecycleRecoveryAttestation(context.lifecycleRecoveryAttestation);
|
|
310
88
|
}
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
89
|
+
function boundTaskRef(context, taskRef) {
|
|
90
|
+
return context.source === 'sandbox-executor' ? context.taskId : taskRef;
|
|
91
|
+
}
|
|
92
|
+
function autoTaskOptions(context, options) {
|
|
93
|
+
if (context.source === 'sandbox-executor')
|
|
94
|
+
return { ...options, taskId: context.taskId };
|
|
95
|
+
const resolved = resolveTaskContext(undefined, { repoRoot: context.repoRoot });
|
|
96
|
+
if (!resolved.ok)
|
|
97
|
+
throw new Error(`${resolved.code}: ${resolved.message}`);
|
|
98
|
+
return { ...options, taskId: resolved.taskId };
|
|
315
99
|
}
|
|
316
100
|
function domainOptions(context, options) {
|
|
317
101
|
return {
|
|
@@ -331,7 +115,7 @@ function lifecycleTaskId(request, repoRoot) {
|
|
|
331
115
|
}
|
|
332
116
|
}
|
|
333
117
|
async function applyLifecycleWithAuthority(context, request) {
|
|
334
|
-
const execute = async () => {
|
|
118
|
+
const execute = async (underTaskLock = false) => {
|
|
335
119
|
if (request.intent === 'recover-started') {
|
|
336
120
|
const adapter = isAgentClientId(request.agent)
|
|
337
121
|
? getAgentClientAdapter(request.agent).orchestrationAdapter
|
|
@@ -345,13 +129,15 @@ async function applyLifecycleWithAuthority(context, request) {
|
|
|
345
129
|
}
|
|
346
130
|
return adapter.recoverStarted(request, { repoRoot: context.repoRoot });
|
|
347
131
|
}
|
|
348
|
-
return
|
|
132
|
+
return underTaskLock
|
|
133
|
+
? applyTaskLifecycleWithinTaskLock(request, { repoRoot: context.repoRoot }, taskId)
|
|
134
|
+
: applyTaskLifecycle(request, { repoRoot: context.repoRoot });
|
|
349
135
|
};
|
|
350
136
|
const taskId = lifecycleTaskId(request, context.repoRoot);
|
|
351
137
|
if (!taskId)
|
|
352
138
|
return await execute();
|
|
353
139
|
try {
|
|
354
|
-
return await withTaskExecutionLock(context.repoRoot, taskId, `task-lifecycle.${request.intent}`, execute);
|
|
140
|
+
return await withTaskExecutionLock(context.repoRoot, taskId, `task-lifecycle.${request.intent}`, () => execute(true));
|
|
355
141
|
}
|
|
356
142
|
catch (error) {
|
|
357
143
|
if (!(error instanceof TaskExecutionLockError))
|
|
@@ -368,52 +154,52 @@ async function applyLifecycleWithAuthority(context, request) {
|
|
|
368
154
|
}
|
|
369
155
|
}
|
|
370
156
|
function orchestration(context, operation) {
|
|
371
|
-
|
|
157
|
+
const taskRef = boundTaskRef(context, operation.taskRef);
|
|
372
158
|
const input = operation.input;
|
|
373
159
|
const options = domainOptions(context, operation.options);
|
|
374
160
|
switch (operation.intent) {
|
|
375
161
|
case 'begin-or-resume':
|
|
376
|
-
return beginOrResumeOrchestration(
|
|
162
|
+
return beginOrResumeOrchestration(taskRef, {
|
|
377
163
|
...options,
|
|
378
164
|
client: input.client,
|
|
379
165
|
maxSteps: input.maxSteps,
|
|
380
166
|
modelPolicy: input.modelPolicy
|
|
381
167
|
});
|
|
382
|
-
case 'route': return routeOrchestration(
|
|
383
|
-
case 'status': return statusOrchestration(
|
|
384
|
-
case 'prepare': {
|
|
385
|
-
|
|
386
|
-
client: input.client,
|
|
387
|
-
requestedModel: input.requestedModel,
|
|
388
|
-
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
389
|
-
};
|
|
390
|
-
const prepareDelegation = getAgentClientAdapter(prepareInput.client)
|
|
391
|
-
.orchestrationAdapter?.prepareDelegation;
|
|
392
|
-
if (prepareDelegation) {
|
|
393
|
-
return prepareDelegation(operation.taskRef, prepareInput, {
|
|
394
|
-
repoRoot: context.repoRoot,
|
|
395
|
-
orchestrationOptions: options,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
return prepareOrchestrationDelegation(operation.taskRef, prepareInput, options);
|
|
399
|
-
}
|
|
400
|
-
case 'dispatch': return dispatchOrchestrationDelegation(operation.taskRef, options);
|
|
168
|
+
case 'route': return routeOrchestration(taskRef, options);
|
|
169
|
+
case 'status': return statusOrchestration(taskRef, options);
|
|
170
|
+
case 'prepare': return prepareTaskOrchestration(context, { ...operation, taskRef }, options);
|
|
171
|
+
case 'dispatch': return dispatchOrchestrationDelegation(taskRef, options);
|
|
401
172
|
case 'await-activation':
|
|
402
|
-
return awaitOrchestrationDelegationActivation(
|
|
403
|
-
case 'recover-prepared': return recoverPreparedOrchestrationDelegation(
|
|
173
|
+
return awaitOrchestrationDelegationActivation(taskRef, input.event, options);
|
|
174
|
+
case 'recover-prepared': return recoverPreparedOrchestrationDelegation(taskRef, options);
|
|
404
175
|
case 'hook-start':
|
|
405
176
|
return input.auto === true
|
|
406
|
-
? activateMatchingOrchestrationDelegation(input.client, input.event, options)
|
|
407
|
-
: activateOrchestrationDelegation(
|
|
177
|
+
? activateMatchingOrchestrationDelegation(input.client, input.event, autoTaskOptions(context, options))
|
|
178
|
+
: activateOrchestrationDelegation(taskRef, input.event, options);
|
|
408
179
|
case 'hook-stop':
|
|
409
180
|
return input.auto === true
|
|
410
|
-
? sealMatchingOrchestrationDelegation(input.client, input.event, options)
|
|
411
|
-
: sealOrchestrationDelegation(
|
|
412
|
-
case 'advance': return advanceOrchestration(
|
|
181
|
+
? sealMatchingOrchestrationDelegation(input.client, input.event, autoTaskOptions(context, options))
|
|
182
|
+
: sealOrchestrationDelegation(taskRef, input.event, undefined, options);
|
|
183
|
+
case 'advance': return advanceOrchestration(taskRef, options);
|
|
413
184
|
case 'pause':
|
|
414
|
-
return pauseOrchestration(
|
|
185
|
+
return pauseOrchestration(taskRef, input.code, input.message, input.recoverable, options);
|
|
415
186
|
}
|
|
416
187
|
}
|
|
188
|
+
function prepareTaskOrchestration(context, operation, options) {
|
|
189
|
+
const input = operation.input;
|
|
190
|
+
const prepareInput = {
|
|
191
|
+
client: input.client,
|
|
192
|
+
requestedModel: input.requestedModel,
|
|
193
|
+
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
194
|
+
};
|
|
195
|
+
const prepareDelegation = getAgentClientAdapter(prepareInput.client).orchestrationAdapter?.prepareDelegation;
|
|
196
|
+
return prepareDelegation
|
|
197
|
+
? prepareDelegation(operation.taskRef, prepareInput, {
|
|
198
|
+
repoRoot: context.repoRoot,
|
|
199
|
+
orchestrationOptions: options,
|
|
200
|
+
})
|
|
201
|
+
: prepareOrchestrationDelegation(operation.taskRef, prepareInput, options);
|
|
202
|
+
}
|
|
417
203
|
function orchestrationFailure(error) {
|
|
418
204
|
if (error instanceof OrchestrationStateError) {
|
|
419
205
|
return {
|
|
@@ -427,12 +213,16 @@ export function dispatchTaskControlOperation(context, operation) {
|
|
|
427
213
|
assertTaskControlExecutionContext(context);
|
|
428
214
|
assertTaskControlOperation(operation);
|
|
429
215
|
if (operation.family === 'task-lifecycle') {
|
|
430
|
-
|
|
431
|
-
|
|
216
|
+
return applyLifecycleWithAuthority(context, {
|
|
217
|
+
...operation.request,
|
|
218
|
+
taskRef: boundTaskRef(context, operation.request.taskRef)
|
|
219
|
+
});
|
|
432
220
|
}
|
|
433
221
|
if (operation.family === 'task-finalization') {
|
|
434
|
-
|
|
435
|
-
|
|
222
|
+
return applyTaskFinalization({
|
|
223
|
+
...operation.request,
|
|
224
|
+
taskRef: boundTaskRef(context, operation.request.taskRef)
|
|
225
|
+
}, {
|
|
436
226
|
repoRoot: context.repoRoot,
|
|
437
227
|
...(context.source === 'sandbox-executor' ? {
|
|
438
228
|
controlBinding: { generation: context.generation, requestId: context.requestId },
|
|
@@ -530,6 +320,8 @@ export function parseTaskControlOperation(family, args) {
|
|
|
530
320
|
const taskRef = args[0];
|
|
531
321
|
const intent = args[1];
|
|
532
322
|
if (family === 'task-lifecycle') {
|
|
323
|
+
if (intent === 'complete')
|
|
324
|
+
operationInvalid('complete must use task-finalization');
|
|
533
325
|
const values = parseValues(args, 2, LIFECYCLE_FLAGS);
|
|
534
326
|
const agent = normalizeAgentToken(value(values, '--agent') ?? '');
|
|
535
327
|
if (!agent)
|
|
@@ -16,12 +16,11 @@ export function parseControlOutput(output) {
|
|
|
16
16
|
export const SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES = Object.freeze([
|
|
17
17
|
'completed', 'evidence-written', 'publish-authorized'
|
|
18
18
|
]);
|
|
19
|
-
const LIFECYCLE_INTENTS = ['block', 'activate', 'cancel', '
|
|
19
|
+
const LIFECYCLE_INTENTS = ['block', 'activate', 'cancel', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'];
|
|
20
20
|
export const SANDBOX_CONTROL_RECOVERY_OPERATIONS = Object.freeze([
|
|
21
21
|
...LIFECYCLE_INTENTS.map((intent) => ({ family: 'task-lifecycle', intent, class: 'lifecycle-mutation' })),
|
|
22
|
-
{ family: 'task-finalization', intent: 'complete', class: 'finalization' },
|
|
23
22
|
{ family: 'task-create', intent: 'create', class: 'task-create' },
|
|
24
|
-
...['
|
|
23
|
+
...['controller.close', 'controller.verify'].map((intent) => ({ family: 'agent-client', intent, class: 'agent-client-operation' }))
|
|
25
24
|
]);
|
|
26
25
|
export function digestControlRecoveryIntent(family, intent) {
|
|
27
26
|
return createHash('sha256').update(`${family}\0${intent}`, 'utf8').digest('hex');
|
package/dist/lib/task/create.js
CHANGED
|
@@ -10,7 +10,7 @@ import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.js';
|
|
|
10
10
|
import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, parseTaskQualification } from './qualification-audit.js';
|
|
11
11
|
import { enumerateAllTaskDirs } from './resolve-ref.js';
|
|
12
12
|
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
13
|
-
|
|
13
|
+
import { AGENT_TOKEN_VALUES } from '../agent-clients/tokens.js';
|
|
14
14
|
const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'];
|
|
15
15
|
const PRIORITIES = ['Urgent', 'High', 'Medium', 'Low'];
|
|
16
16
|
const EFFORTS = ['High', 'Medium', 'Low'];
|
|
@@ -91,7 +91,7 @@ function validateTaskCreateCandidate(value) {
|
|
|
91
91
|
return {
|
|
92
92
|
version: 1,
|
|
93
93
|
idempotencyKey,
|
|
94
|
-
agent: enumValue(candidate.agent,
|
|
94
|
+
agent: enumValue(candidate.agent, AGENT_TOKEN_VALUES, 'agent'),
|
|
95
95
|
title,
|
|
96
96
|
type: enumValue(candidate.type, TYPES, 'type'),
|
|
97
97
|
branchSlug,
|