@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.
Files changed (154) hide show
  1. package/dist/bin/internal-cli.js +2 -2
  2. package/dist/lib/agent-clients/adapter.js +7 -1
  3. package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
  9. package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
  12. package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
  13. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
  14. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
  15. package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
  16. package/dist/lib/agent-clients/adapters/codex.js +40 -1
  17. package/dist/lib/agent-clients/adapters/index.js +2 -10
  18. package/dist/lib/agent-clients/tokens.js +3 -2
  19. package/dist/lib/internal/agent-client.js +132 -72
  20. package/dist/lib/internal/cli-route-inventory.js +2 -2
  21. package/dist/lib/internal/sandbox-control.js +1 -81
  22. package/dist/lib/internal/task-finalization.js +26 -19
  23. package/dist/lib/internal/task-lifecycle.js +1 -1
  24. package/dist/lib/internal/task-operation-registry.js +1 -1
  25. package/dist/lib/internal/task-validate.js +1 -1
  26. package/dist/lib/platform/github-client.js +33 -3
  27. package/dist/lib/platform/issues.js +18 -6
  28. package/dist/lib/runtime/agent-runtime.js +6 -29
  29. package/dist/lib/sandbox/commands/control-status.js +1 -0
  30. package/dist/lib/sandbox/commands/create.js +7 -2
  31. package/dist/lib/sandbox/commands/ls.js +4 -5
  32. package/dist/lib/sandbox/commands/rm.js +1 -1
  33. package/dist/lib/sandbox/control/agent-operation.js +34 -0
  34. package/dist/lib/sandbox/control/client.js +14 -201
  35. package/dist/lib/sandbox/control/executor.js +12 -125
  36. package/dist/lib/sandbox/control/lifecycle.js +66 -4
  37. package/dist/lib/sandbox/control/protocol.js +15 -54
  38. package/dist/lib/sandbox/control/server.js +216 -281
  39. package/dist/lib/sandbox/control/task-view.js +0 -13
  40. package/dist/lib/sandbox/mounts.js +9 -1
  41. package/dist/lib/sandbox/recovery.js +31 -10
  42. package/dist/lib/sandbox/removal.js +28 -5
  43. package/dist/lib/sandbox/workspace-identity.js +18 -6
  44. package/dist/lib/sandbox/workspace-view.js +56 -19
  45. package/dist/lib/task/commands/ls.js +3 -3
  46. package/dist/lib/task/control-authority.js +56 -264
  47. package/dist/lib/task/control-recovery.js +2 -3
  48. package/dist/lib/task/create.js +2 -2
  49. package/dist/lib/task/delegation-receipts.js +74 -161
  50. package/dist/lib/task/events.js +88 -44
  51. package/dist/lib/task/finalization.js +35 -6
  52. package/dist/lib/task/lifecycle.js +274 -115
  53. package/dist/lib/task/orchestration.js +201 -194
  54. package/dist/lib/task/runtime-paths.js +10 -0
  55. package/dist/lib/task/short-id.js +37 -27
  56. package/dist/lib/task/verification-engine.js +32 -26
  57. package/dist/lib/task/workspace-snapshot.js +1 -1
  58. package/dist/lifecycle-build-manifest.json +3 -3
  59. package/lib/agent-clients/adapter.ts +39 -0
  60. package/lib/agent-clients/adapters/claude-code.ts +5 -1
  61. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
  62. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
  63. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
  64. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
  65. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
  66. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
  67. package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
  68. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
  69. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
  70. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
  71. package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
  72. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
  73. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
  74. package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
  75. package/lib/agent-clients/adapters/codex.ts +44 -1
  76. package/lib/agent-clients/adapters/index.ts +7 -20
  77. package/lib/agent-clients/tokens.ts +4 -3
  78. package/lib/internal/agent-client.ts +142 -73
  79. package/lib/internal/cli-route-inventory.ts +2 -2
  80. package/lib/internal/sandbox-control.ts +0 -97
  81. package/lib/internal/task-finalization.ts +30 -18
  82. package/lib/internal/task-lifecycle.ts +1 -1
  83. package/lib/internal/task-operation-registry.ts +1 -1
  84. package/lib/internal/task-validate.ts +1 -1
  85. package/lib/platform/github-client.ts +34 -3
  86. package/lib/platform/issues.ts +18 -6
  87. package/lib/runtime/agent-runtime.ts +6 -41
  88. package/lib/sandbox/commands/control-status.ts +1 -0
  89. package/lib/sandbox/commands/create.ts +7 -1
  90. package/lib/sandbox/commands/ls.ts +4 -5
  91. package/lib/sandbox/commands/rm.ts +1 -1
  92. package/lib/sandbox/control/agent-operation.ts +49 -0
  93. package/lib/sandbox/control/client.ts +20 -254
  94. package/lib/sandbox/control/executor.ts +13 -136
  95. package/lib/sandbox/control/lifecycle.ts +67 -4
  96. package/lib/sandbox/control/protocol.ts +22 -66
  97. package/lib/sandbox/control/server.ts +21 -95
  98. package/lib/sandbox/control/task-view.ts +0 -19
  99. package/lib/sandbox/mounts.ts +10 -1
  100. package/lib/sandbox/recovery.ts +38 -17
  101. package/lib/sandbox/removal.ts +33 -7
  102. package/lib/sandbox/workspace-identity.ts +16 -6
  103. package/lib/sandbox/workspace-view.ts +50 -18
  104. package/lib/task/commands/ls.ts +3 -3
  105. package/lib/task/control-authority.ts +66 -378
  106. package/lib/task/control-recovery.ts +3 -4
  107. package/lib/task/create.ts +3 -3
  108. package/lib/task/delegation-receipts.ts +87 -231
  109. package/lib/task/events.ts +100 -42
  110. package/lib/task/finalization.ts +37 -7
  111. package/lib/task/lifecycle.ts +257 -109
  112. package/lib/task/orchestration.ts +260 -237
  113. package/lib/task/runtime-paths.ts +12 -0
  114. package/lib/task/short-id.ts +33 -40
  115. package/lib/task/verification-engine.ts +30 -25
  116. package/lib/task/workspace-snapshot.ts +1 -1
  117. package/package.json +1 -1
  118. package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
  119. package/templates/.agents/rules/README.en.md +2 -1
  120. package/templates/.agents/rules/README.zh-CN.md +2 -1
  121. package/templates/.agents/rules/next-step-output.en.md +6 -19
  122. package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
  123. package/templates/.agents/rules/strategy-pattern.en.md +25 -0
  124. package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
  125. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
  126. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
  127. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  128. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  129. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  130. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  131. package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
  132. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
  133. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
  134. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
  135. package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
  136. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
  137. package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
  138. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
  139. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  140. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
  141. package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
  142. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
  143. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  144. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
  145. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  146. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  147. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  148. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  149. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
  150. package/dist/lib/internal/codex-lifecycle.js +0 -385
  151. package/dist/lib/task/codex-orchestration.js +0 -206
  152. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
  153. package/lib/internal/codex-lifecycle.ts +0 -390
  154. 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 operationTaskId(context, taskRef) {
312
- if (context.source === 'sandbox-executor' && taskRef !== context.taskId) {
313
- throw new Error('TASK_CONTROL_CONTEXT_TASK_MISMATCH: operation task does not match the sandbox manifest');
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 applyTaskLifecycle(request, { repoRoot: context.repoRoot });
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
- operationTaskId(context, operation.taskRef);
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(operation.taskRef, {
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(operation.taskRef, options);
383
- case 'status': return statusOrchestration(operation.taskRef, options);
384
- case 'prepare': {
385
- const prepareInput = {
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(operation.taskRef, input.event, options);
403
- case 'recover-prepared': return recoverPreparedOrchestrationDelegation(operation.taskRef, options);
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(operation.taskRef, input.event, options);
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(operation.taskRef, input.event, options);
412
- case 'advance': return advanceOrchestration(operation.taskRef, options);
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(operation.taskRef, input.code, input.message, input.recoverable, options);
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
- operationTaskId(context, operation.request.taskRef);
431
- return applyLifecycleWithAuthority(context, operation.request);
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
- operationTaskId(context, operation.request.taskRef);
435
- return applyTaskFinalization(operation.request, {
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', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'];
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
- ...['open', 'close', 'verify'].map((intent) => ({ family: 'codex-controller', intent, class: 'codex-controller' }))
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');
@@ -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
- const AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'cursor'];
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, AGENTS, 'agent'),
94
+ agent: enumValue(candidate.agent, AGENT_TOKEN_VALUES, 'agent'),
95
95
  title,
96
96
  type: enumValue(candidate.type, TYPES, 'type'),
97
97
  branchSlug,