@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
|
@@ -7,44 +7,6 @@ import { normalizeAgentToken } from '../agent-clients/tokens.ts';
|
|
|
7
7
|
type DelegationRole = 'executor' | 'reviewer';
|
|
8
8
|
type DelegationStage = 'analysis' | 'review-analysis' | 'plan' | 'review-plan' | 'code' | 'review-code' | 'commit';
|
|
9
9
|
type DelegationStatus = 'prepared' | 'activated' | 'stage-completed' | 'sealed' | 'consumed' | 'aborted' | 'expired';
|
|
10
|
-
type DelegationHostEvidence = Readonly<{
|
|
11
|
-
kind: 'codex-lifecycle-v1' | 'codex-lifecycle-v2';
|
|
12
|
-
hookDefinitionHash: string;
|
|
13
|
-
startRevision: number;
|
|
14
|
-
stopRevision: number | null;
|
|
15
|
-
consumer: string | null;
|
|
16
|
-
consumedAt: string | null;
|
|
17
|
-
protocolVersion?: number;
|
|
18
|
-
packageVersion?: string;
|
|
19
|
-
internalExecutableBuildHash?: string;
|
|
20
|
-
lifecycleContractHash?: string;
|
|
21
|
-
hookSource?: 'project' | 'managed' | 'isolated-user';
|
|
22
|
-
hookSourcePathDigest?: string;
|
|
23
|
-
hookSourceHash?: string;
|
|
24
|
-
capabilitySessionId?: string;
|
|
25
|
-
capabilityTurnId?: string;
|
|
26
|
-
capabilityToolUseId?: string;
|
|
27
|
-
spawnToolUseId?: string;
|
|
28
|
-
spawnObservedAt?: string;
|
|
29
|
-
controllerInstanceDigest?: string | null;
|
|
30
|
-
controlGeneration?: string | null;
|
|
31
|
-
}>;
|
|
32
|
-
type DelegationLifecycleProvenance = Readonly<{
|
|
33
|
-
protocolVersion: number;
|
|
34
|
-
packageVersion: string;
|
|
35
|
-
internalExecutableBuildHash: string;
|
|
36
|
-
lifecycleContractHash: string;
|
|
37
|
-
hookDefinitionHash: string;
|
|
38
|
-
hookSource: 'project' | 'managed' | 'isolated-user';
|
|
39
|
-
hookSourcePathDigest: string;
|
|
40
|
-
hookSourceHash: string;
|
|
41
|
-
capabilitySessionId: string;
|
|
42
|
-
capabilityTurnId: string;
|
|
43
|
-
capabilityToolUseId: string;
|
|
44
|
-
controllerInstanceDigest: string | null;
|
|
45
|
-
controlGeneration: string | null;
|
|
46
|
-
}>;
|
|
47
|
-
|
|
48
10
|
type DelegationReceipt = Readonly<{
|
|
49
11
|
id: string;
|
|
50
12
|
taskId: string;
|
|
@@ -66,8 +28,7 @@ type DelegationReceipt = Readonly<{
|
|
|
66
28
|
agent: string | null;
|
|
67
29
|
status: DelegationStatus;
|
|
68
30
|
workspaceSnapshotScope?: 'task';
|
|
69
|
-
|
|
70
|
-
hostEvidence?: DelegationHostEvidence | null;
|
|
31
|
+
adapterEvidence?: Readonly<Record<string, unknown>>;
|
|
71
32
|
beforeFingerprint: string;
|
|
72
33
|
afterFingerprint: string | null;
|
|
73
34
|
changedPaths: readonly string[];
|
|
@@ -116,76 +77,35 @@ function nullableSafeInteger(value: unknown): value is number | null {
|
|
|
116
77
|
return value === null || (Number.isSafeInteger(value) && (value as number) >= 0);
|
|
117
78
|
}
|
|
118
79
|
|
|
119
|
-
const LIFECYCLE_PROVENANCE_KEYS = [
|
|
120
|
-
'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
|
|
121
|
-
'hookDefinitionHash', 'hookSource', 'hookSourcePathDigest', 'hookSourceHash',
|
|
122
|
-
'capabilitySessionId', 'capabilityTurnId', 'capabilityToolUseId',
|
|
123
|
-
'controllerInstanceDigest', 'controlGeneration'
|
|
124
|
-
] as const;
|
|
125
|
-
|
|
126
|
-
function isDelegationLifecycleProvenance(value: unknown): value is DelegationLifecycleProvenance {
|
|
127
|
-
if (!hasExactKeys(value, LIFECYCLE_PROVENANCE_KEYS)) return false;
|
|
128
|
-
return Number.isSafeInteger(value.protocolVersion)
|
|
129
|
-
&& (value.protocolVersion as number) > 0
|
|
130
|
-
&& exactText(value.packageVersion)
|
|
131
|
-
&& exactText(value.internalExecutableBuildHash)
|
|
132
|
-
&& exactText(value.lifecycleContractHash)
|
|
133
|
-
&& exactText(value.hookDefinitionHash)
|
|
134
|
-
&& ['project', 'managed', 'isolated-user'].includes(value.hookSource as string)
|
|
135
|
-
&& exactText(value.hookSourcePathDigest)
|
|
136
|
-
&& exactText(value.hookSourceHash)
|
|
137
|
-
&& exactText(value.capabilitySessionId)
|
|
138
|
-
&& exactText(value.capabilityTurnId)
|
|
139
|
-
&& exactText(value.capabilityToolUseId)
|
|
140
|
-
&& nullableText(value.controllerInstanceDigest)
|
|
141
|
-
&& nullableText(value.controlGeneration);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const HOST_EVIDENCE_REQUIRED_KEYS = [
|
|
145
|
-
'kind', 'hookDefinitionHash', 'startRevision', 'stopRevision', 'consumer', 'consumedAt'
|
|
146
|
-
] as const;
|
|
147
|
-
const HOST_EVIDENCE_OPTIONAL_KEYS = [
|
|
148
|
-
'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
|
|
149
|
-
'hookSource', 'hookSourcePathDigest', 'hookSourceHash', 'capabilitySessionId',
|
|
150
|
-
'capabilityTurnId', 'capabilityToolUseId', 'spawnToolUseId', 'spawnObservedAt',
|
|
151
|
-
'controllerInstanceDigest', 'controlGeneration'
|
|
152
|
-
] as const;
|
|
153
|
-
|
|
154
|
-
function isDelegationHostEvidence(value: unknown): value is DelegationHostEvidence {
|
|
155
|
-
if (!hasExactKeys(value, HOST_EVIDENCE_REQUIRED_KEYS, HOST_EVIDENCE_OPTIONAL_KEYS)) return false;
|
|
156
|
-
if (!['codex-lifecycle-v1', 'codex-lifecycle-v2'].includes(value.kind as string)) return false;
|
|
157
|
-
if (!exactText(value.hookDefinitionHash)
|
|
158
|
-
|| !Number.isSafeInteger(value.startRevision) || (value.startRevision as number) < 1
|
|
159
|
-
|| !nullableSafeInteger(value.stopRevision)
|
|
160
|
-
|| !nullableText(value.consumer)
|
|
161
|
-
|| !nullableText(value.consumedAt)) return false;
|
|
162
|
-
if (value.kind === 'codex-lifecycle-v1') return true;
|
|
163
|
-
return Number.isSafeInteger(value.protocolVersion)
|
|
164
|
-
&& (value.protocolVersion as number) > 0
|
|
165
|
-
&& exactText(value.packageVersion)
|
|
166
|
-
&& exactText(value.internalExecutableBuildHash)
|
|
167
|
-
&& exactText(value.lifecycleContractHash)
|
|
168
|
-
&& ['project', 'managed', 'isolated-user'].includes(value.hookSource as string)
|
|
169
|
-
&& exactText(value.hookSourcePathDigest)
|
|
170
|
-
&& exactText(value.hookSourceHash)
|
|
171
|
-
&& exactText(value.capabilitySessionId)
|
|
172
|
-
&& exactText(value.capabilityTurnId)
|
|
173
|
-
&& exactText(value.spawnToolUseId)
|
|
174
|
-
&& exactText(value.spawnObservedAt)
|
|
175
|
-
&& nullableText(value.controllerInstanceDigest)
|
|
176
|
-
&& nullableText(value.controlGeneration);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
80
|
const RECEIPT_KEYS = [
|
|
180
81
|
'id', 'taskId', 'runId', 'role', 'stage', 'round', 'artifact', 'client',
|
|
181
82
|
'requestedModel', 'requestedReasoningEffort', 'actualModel', 'actualReasoningEffort',
|
|
182
83
|
'modelFallbackReason', 'reasoningEffortFallbackReason', 'parentId', 'childId',
|
|
183
|
-
'spawnMode', 'agent', 'status', 'workspaceSnapshotScope',
|
|
184
|
-
'
|
|
84
|
+
'spawnMode', 'agent', 'status', 'workspaceSnapshotScope',
|
|
85
|
+
'beforeFingerprint', 'afterFingerprint', 'changedPaths', 'createdAt',
|
|
185
86
|
'preparedMonotonicMs', 'spawnDispatchMonotonicMs', 'activationDeadlineMonotonicMs',
|
|
186
87
|
'spawnDispatchedAt', 'activationDeadlineAt', 'startEvidenceMonotonicMs',
|
|
187
88
|
'activatedMonotonicMs', 'activatedAt', 'sealedAt', 'consumedAt'
|
|
188
89
|
] as const;
|
|
90
|
+
const RECEIPT_OPTIONAL_KEYS = ['adapterEvidence'] as const;
|
|
91
|
+
|
|
92
|
+
function isJsonRecord(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
93
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(JSON.stringify(value)) !== null;
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function withClientEvidence(receipt: DelegationReceipt, evidence: unknown): Readonly<Record<string, unknown>> | null {
|
|
102
|
+
if (evidence === undefined) return receipt.adapterEvidence ?? Object.freeze({});
|
|
103
|
+
if (!isJsonRecord(evidence)) return null;
|
|
104
|
+
return Object.freeze({
|
|
105
|
+
...(receipt.adapterEvidence ?? {}),
|
|
106
|
+
[receipt.client]: JSON.parse(JSON.stringify(evidence)) as unknown
|
|
107
|
+
});
|
|
108
|
+
}
|
|
189
109
|
|
|
190
110
|
function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
|
|
191
111
|
const dispatchFields = [
|
|
@@ -209,7 +129,6 @@ function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
|
|
|
209
129
|
&& receipt.modelFallbackReason === null
|
|
210
130
|
&& receipt.reasoningEffortFallbackReason === null
|
|
211
131
|
&& receipt.agent === null
|
|
212
|
-
&& receipt.hostEvidence === null
|
|
213
132
|
&& receipt.startEvidenceMonotonicMs === null
|
|
214
133
|
&& receipt.activatedMonotonicMs === null
|
|
215
134
|
&& receipt.activatedAt === null
|
|
@@ -238,18 +157,12 @@ function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
|
|
|
238
157
|
}
|
|
239
158
|
|
|
240
159
|
if (receipt.status === 'aborted') {
|
|
241
|
-
const host = receipt.hostEvidence;
|
|
242
160
|
return receipt.agent === null
|
|
243
161
|
&& receipt.afterFingerprint === null
|
|
244
162
|
&& receipt.changedPaths.length === 0
|
|
245
163
|
&& receipt.sealedAt === null
|
|
246
164
|
&& receipt.consumedAt === null
|
|
247
|
-
&&
|
|
248
|
-
&& host !== undefined
|
|
249
|
-
&& Number.isSafeInteger(host.stopRevision)
|
|
250
|
-
&& (host.stopRevision as number) > host.startRevision
|
|
251
|
-
&& host.consumer === receipt.id
|
|
252
|
-
&& exactText(host.consumedAt);
|
|
165
|
+
&& receipt.childId !== null;
|
|
253
166
|
}
|
|
254
167
|
|
|
255
168
|
if (
|
|
@@ -271,7 +184,7 @@ function hasStatusBoundEvidence(receipt: DelegationReceipt): boolean {
|
|
|
271
184
|
}
|
|
272
185
|
|
|
273
186
|
function isDelegationReceipt(value: unknown): value is DelegationReceipt {
|
|
274
|
-
if (!hasExactKeys(value, RECEIPT_KEYS)) return false;
|
|
187
|
+
if (!hasExactKeys(value, RECEIPT_KEYS, RECEIPT_OPTIONAL_KEYS)) return false;
|
|
275
188
|
const structurallyValid = exactText(value.id)
|
|
276
189
|
&& exactText(value.taskId)
|
|
277
190
|
&& exactText(value.runId)
|
|
@@ -293,8 +206,7 @@ function isDelegationReceipt(value: unknown): value is DelegationReceipt {
|
|
|
293
206
|
&& nullableText(value.agent)
|
|
294
207
|
&& ['prepared', 'activated', 'stage-completed', 'sealed', 'consumed', 'aborted', 'expired'].includes(value.status as string)
|
|
295
208
|
&& value.workspaceSnapshotScope === 'task'
|
|
296
|
-
&& (value.
|
|
297
|
-
&& (value.hostEvidence === null || isDelegationHostEvidence(value.hostEvidence))
|
|
209
|
+
&& (value.adapterEvidence === undefined || isJsonRecord(value.adapterEvidence))
|
|
298
210
|
&& exactText(value.beforeFingerprint)
|
|
299
211
|
&& nullableText(value.afterFingerprint)
|
|
300
212
|
&& Array.isArray(value.changedPaths)
|
|
@@ -314,13 +226,6 @@ function isDelegationReceipt(value: unknown): value is DelegationReceipt {
|
|
|
314
226
|
if (!structurallyValid) return false;
|
|
315
227
|
const receipt = value as unknown as DelegationReceipt;
|
|
316
228
|
if (!hasStatusBoundEvidence(receipt)) return false;
|
|
317
|
-
const host = receipt.hostEvidence;
|
|
318
|
-
if (receipt.client === 'codex' && receipt.activatedAt !== null) {
|
|
319
|
-
if (host?.kind !== 'codex-lifecycle-v2' || host.capabilitySessionId !== receipt.parentId) return false;
|
|
320
|
-
if (['sealed', 'consumed'].includes(receipt.status)) {
|
|
321
|
-
return Number.isSafeInteger(host.stopRevision) && host.stopRevision! > host.startRevision;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
229
|
return true;
|
|
325
230
|
}
|
|
326
231
|
|
|
@@ -342,13 +247,42 @@ function foldBlankToNull(value: string | undefined): string | null {
|
|
|
342
247
|
return value?.trim() ? value : null;
|
|
343
248
|
}
|
|
344
249
|
|
|
250
|
+
function validateSealRequest(
|
|
251
|
+
receipt: DelegationReceipt,
|
|
252
|
+
event: Parameters<typeof sealDelegation>[1]
|
|
253
|
+
): ReceiptFailure | null {
|
|
254
|
+
if (receipt.status !== 'stage-completed') {
|
|
255
|
+
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected stage-completed`);
|
|
256
|
+
}
|
|
257
|
+
if (event.childId !== receipt.childId || event.exitCode !== 0) {
|
|
258
|
+
return fail('DELEGATION_STOP_INVALID', 'native stop identity or exit status is invalid');
|
|
259
|
+
}
|
|
260
|
+
const reviewerPathFailure = validateReviewerSealPaths(receipt, event.changedPaths);
|
|
261
|
+
return reviewerPathFailure;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function validateReviewerSealPaths(receipt: DelegationReceipt, changedPaths: readonly string[]): ReceiptFailure | null {
|
|
265
|
+
if (receipt.role !== 'reviewer') return null;
|
|
266
|
+
const taskRoot = `.agents/workspace/active/${receipt.taskId}/`;
|
|
267
|
+
const allowed = new Set([
|
|
268
|
+
`${taskRoot}${receipt.artifact}`,
|
|
269
|
+
`${taskRoot}task.md`,
|
|
270
|
+
`${taskRoot}.runtime/orchestration.json`
|
|
271
|
+
]);
|
|
272
|
+
const disallowed = changedPaths.find((entry) => !allowed.has(entry));
|
|
273
|
+
return disallowed
|
|
274
|
+
? fail('DELEGATION_REVIEWER_WRITE_FORBIDDEN', `reviewer changed forbidden path '${disallowed}'`)
|
|
275
|
+
: null;
|
|
276
|
+
}
|
|
277
|
+
|
|
345
278
|
function prepareDelegation(
|
|
346
|
-
input: Omit<DelegationReceipt, 'id' | 'requestedModel' | 'requestedReasoningEffort' | 'actualModel' | 'actualReasoningEffort' | 'modelFallbackReason' | 'reasoningEffortFallbackReason' | 'parentId' | 'childId' | 'spawnMode' | 'agent' | 'status' | '
|
|
279
|
+
input: Omit<DelegationReceipt, 'id' | 'requestedModel' | 'requestedReasoningEffort' | 'actualModel' | 'actualReasoningEffort' | 'modelFallbackReason' | 'reasoningEffortFallbackReason' | 'parentId' | 'childId' | 'spawnMode' | 'agent' | 'status' | 'afterFingerprint' | 'changedPaths' | 'createdAt' | 'preparedMonotonicMs' | 'spawnDispatchMonotonicMs' | 'activationDeadlineMonotonicMs' | 'spawnDispatchedAt' | 'activationDeadlineAt' | 'startEvidenceMonotonicMs' | 'activatedMonotonicMs' | 'activatedAt' | 'sealedAt' | 'consumedAt'> & Readonly<{ requestedModel: string; requestedReasoningEffort: string }>,
|
|
347
280
|
options: { id?: () => string; now?: () => string; monotonicNow?: () => number } = {}
|
|
348
281
|
): DelegationReceipt {
|
|
349
282
|
const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1_000_000n)))();
|
|
350
283
|
return Object.freeze({
|
|
351
284
|
...input,
|
|
285
|
+
adapterEvidence: Object.freeze({ ...(input.adapterEvidence ?? {}) }),
|
|
352
286
|
id: (options.id ?? randomUUID)(),
|
|
353
287
|
actualModel: null,
|
|
354
288
|
actualReasoningEffort: null,
|
|
@@ -359,7 +293,6 @@ function prepareDelegation(
|
|
|
359
293
|
spawnMode: null,
|
|
360
294
|
agent: null,
|
|
361
295
|
status: 'prepared' as const,
|
|
362
|
-
hostEvidence: null,
|
|
363
296
|
afterFingerprint: null,
|
|
364
297
|
changedPaths: Object.freeze([]),
|
|
365
298
|
createdAt: (options.now ?? (() => new Date().toISOString()))(),
|
|
@@ -409,27 +342,18 @@ function activateDelegation(
|
|
|
409
342
|
actualReasoningEffort?: string;
|
|
410
343
|
modelFallbackReason?: string;
|
|
411
344
|
reasoningEffortFallbackReason?: string;
|
|
412
|
-
|
|
413
|
-
kind: 'codex-lifecycle-v1' | 'codex-lifecycle-v2';
|
|
414
|
-
hookDefinitionHash: string;
|
|
415
|
-
startRevision: number;
|
|
416
|
-
protocolVersion?: number;
|
|
417
|
-
packageVersion?: string;
|
|
418
|
-
internalExecutableBuildHash?: string;
|
|
419
|
-
lifecycleContractHash?: string;
|
|
420
|
-
hookSource?: 'project' | 'managed' | 'isolated-user';
|
|
421
|
-
hookSourcePathDigest?: string;
|
|
422
|
-
hookSourceHash?: string;
|
|
423
|
-
capabilitySessionId?: string;
|
|
424
|
-
capabilityTurnId?: string;
|
|
425
|
-
capabilityToolUseId?: string;
|
|
426
|
-
spawnToolUseId?: string;
|
|
427
|
-
spawnObservedAt?: string;
|
|
428
|
-
controllerInstanceDigest?: string | null;
|
|
429
|
-
controlGeneration?: string | null;
|
|
430
|
-
}>;
|
|
345
|
+
clientEvidence?: unknown;
|
|
431
346
|
}>,
|
|
432
|
-
options: {
|
|
347
|
+
options: {
|
|
348
|
+
now?: () => string;
|
|
349
|
+
monotonicNow?: () => number;
|
|
350
|
+
evidencePolicy?: Readonly<{
|
|
351
|
+
spawnModeRequired?: boolean;
|
|
352
|
+
actualModelRequired?: boolean;
|
|
353
|
+
actualReasoningEffortRequired?: boolean;
|
|
354
|
+
fallbackReasonRequired?: boolean;
|
|
355
|
+
}>;
|
|
356
|
+
} = {}
|
|
433
357
|
): ReceiptResult {
|
|
434
358
|
const managedRole = managedDelegationRole(event.nativeAgent);
|
|
435
359
|
if (!managedRole) return fail('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
|
|
@@ -453,34 +377,29 @@ function activateDelegation(
|
|
|
453
377
|
if (!event.parentId || (receipt.parentId !== null && event.parentId !== receipt.parentId) || !event.childId || event.childId === event.parentId) {
|
|
454
378
|
return fail('DELEGATION_IDENTITY_INVALID', 'native parent/child identity does not match the prepared delegation');
|
|
455
379
|
}
|
|
456
|
-
|
|
457
|
-
if (
|
|
380
|
+
const evidencePolicy = options.evidencePolicy ?? {};
|
|
381
|
+
if (evidencePolicy.spawnModeRequired !== false && event.spawnMode !== 'fresh') {
|
|
458
382
|
return fail('DELEGATION_FORK_FORBIDDEN', `spawn mode '${event.spawnMode}' is not fresh`);
|
|
459
383
|
}
|
|
460
|
-
// model/effort 维度:claude-code 如实记录、不做门禁;其余 client 维持现状硬校验(HDR-2)
|
|
461
384
|
const actualModel = foldBlankToNull(event.actualModel);
|
|
462
385
|
const actualReasoningEffort = foldBlankToNull(event.actualReasoningEffort);
|
|
463
|
-
if (
|
|
386
|
+
if (evidencePolicy.actualModelRequired !== false) {
|
|
464
387
|
if (!actualModel || actualModel.trim() !== actualModel) {
|
|
465
388
|
return fail('DELEGATION_MODEL_IDENTITY_MISSING', 'native start event must provide a non-empty actual model identity');
|
|
466
389
|
}
|
|
467
390
|
}
|
|
468
391
|
if (actualModel !== null && actualModel !== receipt.requestedModel) {
|
|
469
|
-
if (
|
|
392
|
+
if (evidencePolicy.fallbackReasonRequired !== false && (!event.modelFallbackReason || event.modelFallbackReason.trim() === '')) {
|
|
470
393
|
return fail('DELEGATION_MODEL_FALLBACK_UNRECORDED', 'actual model differs from requested model without a fallback reason');
|
|
471
394
|
}
|
|
472
395
|
}
|
|
473
|
-
if (
|
|
396
|
+
if (evidencePolicy.actualReasoningEffortRequired !== false) {
|
|
474
397
|
if (!actualReasoningEffort || actualReasoningEffort.trim() !== actualReasoningEffort) {
|
|
475
398
|
return fail('DELEGATION_REASONING_EFFORT_MISSING', 'native start event must provide a non-empty actual reasoning effort');
|
|
476
399
|
}
|
|
477
400
|
}
|
|
478
|
-
if (
|
|
479
|
-
receipt.client === 'codex'
|
|
480
|
-
&& event.hostEvidence?.kind !== 'codex-lifecycle-v2'
|
|
481
|
-
) return fail('DELEGATION_HOST_EVIDENCE_REQUIRED', 'Codex activation requires trusted lifecycle-v2 host evidence');
|
|
482
401
|
if (actualReasoningEffort !== null && actualReasoningEffort !== receipt.requestedReasoningEffort) {
|
|
483
|
-
if (
|
|
402
|
+
if (evidencePolicy.fallbackReasonRequired !== false && (!event.reasoningEffortFallbackReason || event.reasoningEffortFallbackReason.trim() === '')) {
|
|
484
403
|
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_UNRECORDED', 'actual reasoning effort differs from requested effort without a fallback reason');
|
|
485
404
|
}
|
|
486
405
|
}
|
|
@@ -495,19 +414,8 @@ function activateDelegation(
|
|
|
495
414
|
) {
|
|
496
415
|
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_INVALID', 'reasoning-effort fallback reason is only valid when actual effort differs');
|
|
497
416
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
&& (
|
|
501
|
-
receipt.client !== 'codex'
|
|
502
|
-
|| !event.hostEvidence.hookDefinitionHash.trim()
|
|
503
|
-
|| !Number.isSafeInteger(event.hostEvidence.startRevision)
|
|
504
|
-
|| event.hostEvidence.startRevision < 1
|
|
505
|
-
)
|
|
506
|
-
) return fail('DELEGATION_HOST_EVIDENCE_INVALID', 'Codex start evidence reference is invalid');
|
|
507
|
-
if (event.hostEvidence?.kind === 'codex-lifecycle-v2'
|
|
508
|
-
&& event.hostEvidence.capabilitySessionId !== event.parentId) {
|
|
509
|
-
return fail('DELEGATION_HOST_EVIDENCE_INVALID', 'Codex start evidence does not match the observed parent');
|
|
510
|
-
}
|
|
417
|
+
const adapterEvidence = withClientEvidence(receipt, event.clientEvidence);
|
|
418
|
+
if (!adapterEvidence) return fail('DELEGATION_ADAPTER_EVIDENCE_INVALID', 'adapter evidence must be a JSON object');
|
|
511
419
|
return { ok: true, receipt: Object.freeze({
|
|
512
420
|
...receipt,
|
|
513
421
|
status: 'activated',
|
|
@@ -518,12 +426,7 @@ function activateDelegation(
|
|
|
518
426
|
actualReasoningEffort,
|
|
519
427
|
modelFallbackReason: event.modelFallbackReason ?? null,
|
|
520
428
|
reasoningEffortFallbackReason: event.reasoningEffortFallbackReason ?? null,
|
|
521
|
-
|
|
522
|
-
...event.hostEvidence,
|
|
523
|
-
stopRevision: null,
|
|
524
|
-
consumer: null,
|
|
525
|
-
consumedAt: null
|
|
526
|
-
}) : receipt.hostEvidence ?? null,
|
|
429
|
+
adapterEvidence,
|
|
527
430
|
startEvidenceMonotonicMs: monotonic,
|
|
528
431
|
activatedMonotonicMs: (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1_000_000n)))(),
|
|
529
432
|
activatedAt: new Date(wallNow).toISOString()
|
|
@@ -542,31 +445,14 @@ function abortPreparedDelegation(receipt: DelegationReceipt): ReceiptResult {
|
|
|
542
445
|
|
|
543
446
|
function abortActivatedDelegation(
|
|
544
447
|
receipt: DelegationReceipt,
|
|
545
|
-
event: Readonly<{ childId: string;
|
|
448
|
+
event: Readonly<{ childId: string; clientEvidence?: unknown }>
|
|
546
449
|
): ReceiptResult {
|
|
547
450
|
if (receipt.status !== 'activated') {
|
|
548
451
|
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected activated`);
|
|
549
452
|
}
|
|
550
|
-
if (
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|| receipt.hostEvidence?.kind !== 'codex-lifecycle-v2'
|
|
554
|
-
|| typeof receipt.lifecycleProvenance.controllerInstanceDigest !== 'string'
|
|
555
|
-
|| typeof receipt.lifecycleProvenance.controlGeneration !== 'string'
|
|
556
|
-
|| typeof receipt.hostEvidence.controllerInstanceDigest !== 'string'
|
|
557
|
-
|| typeof receipt.hostEvidence.controlGeneration !== 'string'
|
|
558
|
-
) {
|
|
559
|
-
return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'activated delegation lacks the current Codex controller binding');
|
|
560
|
-
}
|
|
561
|
-
if (
|
|
562
|
-
event.childId !== receipt.childId
|
|
563
|
-
|| !Number.isSafeInteger(event.stopRevision)
|
|
564
|
-
|| event.stopRevision <= receipt.hostEvidence.startRevision
|
|
565
|
-
|| event.consumer !== receipt.id
|
|
566
|
-
|| !exactText(event.consumedAt)
|
|
567
|
-
) {
|
|
568
|
-
return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'recovery stop evidence does not match the activated delegation');
|
|
569
|
-
}
|
|
453
|
+
if (event.childId !== receipt.childId) return fail('DELEGATION_RECOVERY_EVIDENCE_INVALID', 'recovery child does not match the activated delegation');
|
|
454
|
+
const adapterEvidence = withClientEvidence(receipt, event.clientEvidence);
|
|
455
|
+
if (!adapterEvidence) return fail('DELEGATION_ADAPTER_EVIDENCE_INVALID', 'adapter evidence must be a JSON object');
|
|
570
456
|
return {
|
|
571
457
|
ok: true,
|
|
572
458
|
receipt: Object.freeze({
|
|
@@ -577,12 +463,7 @@ function abortActivatedDelegation(
|
|
|
577
463
|
changedPaths: Object.freeze([]),
|
|
578
464
|
sealedAt: null,
|
|
579
465
|
consumedAt: null,
|
|
580
|
-
|
|
581
|
-
...receipt.hostEvidence,
|
|
582
|
-
stopRevision: event.stopRevision,
|
|
583
|
-
consumer: event.consumer,
|
|
584
|
-
consumedAt: event.consumedAt
|
|
585
|
-
})
|
|
466
|
+
adapterEvidence
|
|
586
467
|
})
|
|
587
468
|
};
|
|
588
469
|
}
|
|
@@ -612,34 +493,14 @@ function sealDelegation(
|
|
|
612
493
|
actualReasoningEffort?: string;
|
|
613
494
|
modelFallbackReason?: string;
|
|
614
495
|
reasoningEffortFallbackReason?: string;
|
|
615
|
-
|
|
496
|
+
clientEvidence?: unknown;
|
|
616
497
|
}>,
|
|
617
|
-
options: { now?: () => string
|
|
498
|
+
options: { now?: () => string } = {}
|
|
618
499
|
): ReceiptResult {
|
|
619
|
-
|
|
620
|
-
if (
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (event.childId !== receipt.childId || event.exitCode !== 0) return fail('DELEGATION_STOP_INVALID', 'native stop identity or exit status is invalid');
|
|
624
|
-
if (receipt.role === 'reviewer') {
|
|
625
|
-
const taskRoot = `.agents/workspace/active/${receipt.taskId}/`;
|
|
626
|
-
const allowed = new Set([
|
|
627
|
-
`${taskRoot}${receipt.artifact}`,
|
|
628
|
-
`${taskRoot}task.md`,
|
|
629
|
-
`${taskRoot}orchestration.json`
|
|
630
|
-
]);
|
|
631
|
-
const disallowed = event.changedPaths.find((entry) => !allowed.has(entry));
|
|
632
|
-
if (disallowed) return fail('DELEGATION_REVIEWER_WRITE_FORBIDDEN', `reviewer changed forbidden path '${disallowed}'`);
|
|
633
|
-
}
|
|
634
|
-
if (event.hostEvidence) {
|
|
635
|
-
if (
|
|
636
|
-
!receipt.hostEvidence
|
|
637
|
-
|| !Number.isSafeInteger(event.hostEvidence.stopRevision)
|
|
638
|
-
|| event.hostEvidence.stopRevision <= receipt.hostEvidence.startRevision
|
|
639
|
-
|| event.hostEvidence.consumer !== receipt.id
|
|
640
|
-
|| !event.hostEvidence.consumedAt.trim()
|
|
641
|
-
) return fail('DELEGATION_HOST_EVIDENCE_INVALID', 'Codex stop evidence reference is invalid');
|
|
642
|
-
}
|
|
500
|
+
const validationFailure = validateSealRequest(receipt, event);
|
|
501
|
+
if (validationFailure) return validationFailure;
|
|
502
|
+
const adapterEvidence = withClientEvidence(receipt, event.clientEvidence);
|
|
503
|
+
if (!adapterEvidence) return fail('DELEGATION_ADAPTER_EVIDENCE_INVALID', 'adapter evidence must be a JSON object');
|
|
643
504
|
return { ok: true, receipt: Object.freeze({
|
|
644
505
|
...receipt,
|
|
645
506
|
status: 'sealed',
|
|
@@ -649,10 +510,7 @@ function sealDelegation(
|
|
|
649
510
|
actualReasoningEffort: receipt.actualReasoningEffort ?? foldBlankToNull(event.actualReasoningEffort),
|
|
650
511
|
modelFallbackReason: receipt.modelFallbackReason ?? event.modelFallbackReason ?? null,
|
|
651
512
|
reasoningEffortFallbackReason: receipt.reasoningEffortFallbackReason ?? event.reasoningEffortFallbackReason ?? null,
|
|
652
|
-
|
|
653
|
-
...receipt.hostEvidence,
|
|
654
|
-
...event.hostEvidence
|
|
655
|
-
}) : receipt.hostEvidence ?? null,
|
|
513
|
+
adapterEvidence,
|
|
656
514
|
sealedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
657
515
|
}) };
|
|
658
516
|
}
|
|
@@ -681,8 +539,6 @@ export {
|
|
|
681
539
|
sealDelegation
|
|
682
540
|
};
|
|
683
541
|
export type {
|
|
684
|
-
DelegationHostEvidence,
|
|
685
|
-
DelegationLifecycleProvenance,
|
|
686
542
|
DelegationReceipt,
|
|
687
543
|
DelegationRole,
|
|
688
544
|
DelegationStage,
|