@fitlab-ai/agent-infra 0.11.1 → 0.11.2
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/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/platform-comment.js +59 -0
- package/dist/lib/internal/sandbox-control.js +4 -131
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +1 -0
- package/dist/lib/internal/task-review.js +5 -5
- package/dist/lib/internal/task-validate.js +0 -6
- package/dist/lib/platform/comment-safety.js +7 -1
- package/dist/lib/platform/github-provider.js +13 -3
- package/dist/lib/platform/issue-comments.js +120 -13
- package/dist/lib/platform/provider-contract.d.ts +2 -0
- package/dist/lib/platform/provider-validation.js +7 -2
- package/dist/lib/platform/verification-sync.js +19 -2
- package/dist/lib/sandbox/control/executor.js +0 -38
- package/dist/lib/sandbox/control/finalization-response.js +10 -2
- package/dist/lib/sandbox/control/task-view.js +3 -1
- package/dist/lib/sandbox/control/workflow-executor.js +28 -43
- package/dist/lib/task/artifact-command.js +6 -6
- package/dist/lib/task/codex-orchestration.js +10 -208
- package/dist/lib/task/commit-operation.js +3 -18
- package/dist/lib/task/control-authority.js +1 -8
- package/dist/lib/task/delegation-receipts.js +14 -76
- package/dist/lib/task/events.js +89 -237
- package/dist/lib/task/finalization-state.js +2 -1
- package/dist/lib/task/finalization.js +273 -49
- package/dist/lib/task/lifecycle-execution.js +2 -11
- package/dist/lib/task/local-artifact-finalization.js +25 -242
- package/dist/lib/task/orchestration.js +0 -18
- package/dist/lib/task/review-command.js +0 -1
- package/dist/lib/task/review-finalization.js +46 -211
- package/dist/lib/task/workflow-command.js +4 -1
- package/dist/lib/task/workflow-dispatch.js +55 -3
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/platform-comment.ts +38 -0
- package/lib/internal/sandbox-control.ts +3 -145
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-operation-registry.ts +1 -0
- package/lib/internal/task-review.ts +5 -5
- package/lib/internal/task-validate.ts +0 -5
- package/lib/platform/comment-safety.ts +8 -0
- package/lib/platform/github-provider.ts +11 -2
- package/lib/platform/issue-comments.ts +132 -13
- package/lib/platform/provider-contract.ts +2 -0
- package/lib/platform/provider-validation.ts +6 -2
- package/lib/platform/verification-sync.ts +19 -2
- package/lib/sandbox/control/executor.ts +0 -44
- package/lib/sandbox/control/finalization-response.ts +10 -2
- package/lib/sandbox/control/task-view.ts +3 -1
- package/lib/sandbox/control/workflow-executor.ts +23 -50
- package/lib/task/artifact-command.ts +6 -7
- package/lib/task/codex-orchestration.ts +8 -241
- package/lib/task/commit-operation.ts +3 -23
- package/lib/task/control-authority.ts +1 -8
- package/lib/task/delegation-receipts.ts +12 -79
- package/lib/task/events.ts +106 -255
- package/lib/task/finalization-state.ts +2 -1
- package/lib/task/finalization.ts +277 -57
- package/lib/task/lifecycle-execution.ts +10 -21
- package/lib/task/local-artifact-finalization.ts +23 -281
- package/lib/task/orchestration.ts +0 -34
- package/lib/task/review-command.ts +0 -1
- package/lib/task/review-finalization.ts +45 -193
- package/lib/task/workflow-command.ts +4 -1
- package/lib/task/workflow-dispatch.ts +46 -3
- package/package.json +2 -2
- package/templates/.agents/rules/lifecycle-orchestration.en.md +7 -7
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +14 -60
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -60
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
1
|
import {
|
|
5
2
|
recoverSandboxControl,
|
|
6
3
|
requestSandboxControl,
|
|
7
4
|
requestSandboxTaskFinalization,
|
|
8
|
-
requestSandboxTaskControl,
|
|
9
5
|
requestSandboxTaskWorkflow,
|
|
10
6
|
resolveVisibleActiveShortId,
|
|
11
7
|
SandboxControlClientError
|
|
@@ -13,129 +9,9 @@ import {
|
|
|
13
9
|
import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
|
|
14
10
|
import { serveSandboxControl } from '../sandbox/control/server.ts';
|
|
15
11
|
import { runSandboxControlExecutor } from '../sandbox/control/executor.ts';
|
|
16
|
-
import {
|
|
17
|
-
controllerProofFromContext,
|
|
18
|
-
verifyCodexSandboxControllerContextWithWarnings
|
|
19
|
-
} from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
|
|
20
12
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
21
13
|
import { parseTaskCreateResult, taskCreateExitCode } from '../task/create-service.ts';
|
|
22
14
|
import { createTaskWorkflowRequest } from '../sandbox/control/task-workflow.ts';
|
|
23
|
-
import { computeLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.ts';
|
|
24
|
-
import { routeOrchestration } from '../task/orchestration.ts';
|
|
25
|
-
import { parseArtifactName } from '../task/artifact-name.ts';
|
|
26
|
-
import type { LifecycleAuthorityRequestV1 } from '../task/control-authority.ts';
|
|
27
|
-
|
|
28
|
-
function valueAfter(args: readonly string[], flag: string): string | undefined {
|
|
29
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
30
|
-
if (args[index] === flag) return args[index + 1];
|
|
31
|
-
if (args[index]?.startsWith(`${flag}=`)) return args[index]!.slice(flag.length + 1);
|
|
32
|
-
}
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function withoutValue(args: readonly string[], flag: string): string[] {
|
|
37
|
-
const result: string[] = [];
|
|
38
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
39
|
-
if (args[index] === flag) { index += 1; continue; }
|
|
40
|
-
if (args[index]?.startsWith(`${flag}=`)) continue;
|
|
41
|
-
result.push(args[index]!);
|
|
42
|
-
}
|
|
43
|
-
return result;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function lifecycleIds(authorityRef: string, taskId: string): Readonly<{ operationId: string; lifecycleRequestId: string }> {
|
|
47
|
-
const digest = (label: string) => createHash('sha256')
|
|
48
|
-
.update(`${label}\0${taskId}\0${authorityRef}`, 'utf8')
|
|
49
|
-
.digest('hex');
|
|
50
|
-
return { operationId: digest('operation'), lifecycleRequestId: digest('lifecycle-request') };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function lifecycleAuthorityIdentity(
|
|
54
|
-
authorityRef: string,
|
|
55
|
-
taskId: string,
|
|
56
|
-
requestId: string,
|
|
57
|
-
phase: LifecycleAuthorityRequestV1['phase'],
|
|
58
|
-
family: LifecycleAuthorityRequestV1['family'],
|
|
59
|
-
artifact: string,
|
|
60
|
-
round: number
|
|
61
|
-
): LifecycleAuthorityRequestV1 | undefined {
|
|
62
|
-
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
63
|
-
if (!authorityRef || !contextPath) return undefined;
|
|
64
|
-
const routed = verifyCodexSandboxControllerContextWithWarnings(contextPath, { repoRoot: process.cwd() });
|
|
65
|
-
const buildIdentity = computeLifecycleBuildIdentity(process.cwd());
|
|
66
|
-
const hookDefinitionHash = createHash('sha256')
|
|
67
|
-
.update(readFileSync(path.join(process.cwd(), '.codex', 'hooks.json')))
|
|
68
|
-
.digest('hex');
|
|
69
|
-
const ids = lifecycleIds(authorityRef, taskId);
|
|
70
|
-
return {
|
|
71
|
-
version: 1,
|
|
72
|
-
requestId,
|
|
73
|
-
operationId: ids.operationId,
|
|
74
|
-
phase,
|
|
75
|
-
taskId,
|
|
76
|
-
family,
|
|
77
|
-
artifact,
|
|
78
|
-
round,
|
|
79
|
-
lifecycleRequestId: ids.lifecycleRequestId,
|
|
80
|
-
authorityRef,
|
|
81
|
-
expectedControlGeneration: routed.context.controlGeneration,
|
|
82
|
-
expectedControllerInstanceDigest: routed.context.controllerInstanceDigest,
|
|
83
|
-
expectedBuildIdentityDigest: createHash('sha256').update(JSON.stringify(buildIdentity)).digest('hex'),
|
|
84
|
-
expectedHookDefinitionHash: hookDefinitionHash
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function lifecycleAuthoritySelector(args: readonly string[]): LifecycleAuthorityRequestV1 | undefined {
|
|
89
|
-
if (!isCanonicalCodexPrepare(args)) return undefined;
|
|
90
|
-
const authorityRef = valueAfter(args, '--capability-ref');
|
|
91
|
-
const taskId = args[0];
|
|
92
|
-
if (!authorityRef || !taskId) return undefined;
|
|
93
|
-
const routed = routeOrchestration(taskId);
|
|
94
|
-
if (!routed.next || !['analysis', 'plan', 'code'].includes(routed.next.stage)) return undefined;
|
|
95
|
-
return lifecycleAuthorityIdentity(
|
|
96
|
-
authorityRef,
|
|
97
|
-
taskId,
|
|
98
|
-
randomUUID(),
|
|
99
|
-
'orchestration.prepare',
|
|
100
|
-
routed.next.stage === 'analysis' ? 'analysis' : routed.next.stage === 'plan' ? 'plan' : 'code',
|
|
101
|
-
routed.next.artifact,
|
|
102
|
-
routed.next.round
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function lifecycleWorkflowAuthoritySelector(
|
|
107
|
-
originalArgs: readonly string[],
|
|
108
|
-
workflow: ReturnType<typeof createTaskWorkflowRequest>
|
|
109
|
-
): LifecycleAuthorityRequestV1 | undefined {
|
|
110
|
-
const authorityRef = valueAfter(originalArgs, '--capability-ref');
|
|
111
|
-
if (!authorityRef) return undefined;
|
|
112
|
-
if (workflow.operation === 'artifact-finalize-local') {
|
|
113
|
-
const family = valueAfter(workflow.args, '--family');
|
|
114
|
-
const artifact = valueAfter(workflow.args, '--artifact');
|
|
115
|
-
const parsed = artifact ? parseArtifactName(artifact) : null;
|
|
116
|
-
if ((family !== 'analysis' && family !== 'plan' && family !== 'code')
|
|
117
|
-
|| !parsed || parsed.family !== family) return undefined;
|
|
118
|
-
return lifecycleAuthorityIdentity(authorityRef, workflow.taskId, workflow.id,
|
|
119
|
-
'artifact.finalize-local', family, artifact!, parsed.round);
|
|
120
|
-
}
|
|
121
|
-
if (workflow.operation !== 'event') return undefined;
|
|
122
|
-
const completed = /^(analysis|plan|code)\.completed$/u.exec(workflow.args[1] ?? '');
|
|
123
|
-
const artifact = valueAfter(workflow.args, '--artifact');
|
|
124
|
-
const parsed = artifact ? parseArtifactName(artifact) : null;
|
|
125
|
-
if (!completed || !parsed || parsed.family !== completed[1]) return undefined;
|
|
126
|
-
return lifecycleAuthorityIdentity(authorityRef, workflow.taskId, workflow.id,
|
|
127
|
-
'task-event.completed', completed[1] as LifecycleAuthorityRequestV1['family'], artifact!, parsed.round);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function isCanonicalCodexPrepare(args: readonly string[]): boolean {
|
|
131
|
-
if (args[1] !== 'prepare') return false;
|
|
132
|
-
const clients: string[] = [];
|
|
133
|
-
for (let index = 2; index < args.length; index += 1) {
|
|
134
|
-
if (args[index] === '--client') clients.push(args[index + 1] ?? '');
|
|
135
|
-
else if (args[index]?.startsWith('--client=')) clients.push(args[index]!.slice('--client='.length));
|
|
136
|
-
}
|
|
137
|
-
return clients.length === 1 && clients[0] === 'codex';
|
|
138
|
-
}
|
|
139
15
|
|
|
140
16
|
type FinalizationStatus = 'completed' | 'failed' | 'blocked' | 'unknown';
|
|
141
17
|
|
|
@@ -274,7 +150,7 @@ async function sandboxControl(args: string[]): Promise<void> {
|
|
|
274
150
|
}
|
|
275
151
|
let workflow;
|
|
276
152
|
try {
|
|
277
|
-
const args =
|
|
153
|
+
const args = commandArgs.slice(1);
|
|
278
154
|
if (/^\d+$/.test(args[0] ?? '')) {
|
|
279
155
|
const resolved = resolveVisibleActiveShortId(args[0]!);
|
|
280
156
|
if (resolved !== taskId) throw new Error('SANDBOX_TASK_REF_MISMATCH');
|
|
@@ -289,11 +165,7 @@ async function sandboxControl(args: string[]): Promise<void> {
|
|
|
289
165
|
}
|
|
290
166
|
let response;
|
|
291
167
|
try {
|
|
292
|
-
|
|
293
|
-
response = requestSandboxTaskWorkflow({
|
|
294
|
-
workflow,
|
|
295
|
-
...(authority ? { authority } : {})
|
|
296
|
-
});
|
|
168
|
+
response = requestSandboxTaskWorkflow({ workflow });
|
|
297
169
|
}
|
|
298
170
|
catch (error) {
|
|
299
171
|
if (!(error instanceof SandboxControlClientError)) throw error;
|
|
@@ -308,21 +180,7 @@ async function sandboxControl(args: string[]): Promise<void> {
|
|
|
308
180
|
}
|
|
309
181
|
let response;
|
|
310
182
|
try {
|
|
311
|
-
|
|
312
|
-
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
313
|
-
const proof = contextPath
|
|
314
|
-
? controllerProofFromContext(verifyCodexSandboxControllerContextWithWarnings(contextPath).context)
|
|
315
|
-
: null;
|
|
316
|
-
const authority = lifecycleAuthoritySelector(commandArgs);
|
|
317
|
-
response = requestSandboxTaskControl({
|
|
318
|
-
family,
|
|
319
|
-
args: commandArgs,
|
|
320
|
-
controllerProof: authority ? null : proof,
|
|
321
|
-
...(authority ? { authority } : {})
|
|
322
|
-
});
|
|
323
|
-
} else {
|
|
324
|
-
response = requestSandboxControl({ family, args: commandArgs });
|
|
325
|
-
}
|
|
183
|
+
response = requestSandboxControl({ family, args: commandArgs });
|
|
326
184
|
} catch (error) {
|
|
327
185
|
if (!(error instanceof SandboxControlClientError)) throw error;
|
|
328
186
|
writeClientError(error);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseArtifactCommand, executeArtifactCommand } from '../task/artifact-command.ts';
|
|
2
2
|
import { ensureInternalHandlerRoute } from './cli-route-inventory.ts';
|
|
3
3
|
|
|
4
|
-
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family>\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact
|
|
4
|
+
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family>\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact>\n\nInspect, initialize, preflight, or finalize a workflow artifact without changing task state.\n`;
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
function taskArtifact(args: string[] = []): void {
|
|
@@ -90,6 +90,7 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
|
|
|
90
90
|
descriptor('internal', 'platform-comment', 'recover', 'non-task', 'recovery', 'none'),
|
|
91
91
|
descriptor('internal', 'platform-comment', 'owner', 'task-bound', 'diagnostic'),
|
|
92
92
|
descriptor('internal', 'platform-comment', 'backfill', 'task-bound', 'remote-write'),
|
|
93
|
+
descriptor('internal', 'platform-comment', 'stage-summary', 'task-bound', 'artifact-write'),
|
|
93
94
|
descriptor('internal', 'platform-comment', 'sync', 'task-bound', 'remote-write'),
|
|
94
95
|
descriptor('internal', 'platform-issue', 'inspect', 'task-bound', 'diagnostic'),
|
|
95
96
|
descriptor('internal', 'platform-issue', 'create', 'task-bound', 'remote-write'),
|
|
@@ -5,7 +5,7 @@ import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
|
5
5
|
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
|
|
6
6
|
import { ensureInternalHandlerRoute } from './cli-route-inventory.ts';
|
|
7
7
|
|
|
8
|
-
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> <preflight|finalize-summary> --stage <analysis|plan|code> --artifact <review-*.md> [--
|
|
8
|
+
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> <preflight|finalize-summary> --stage <analysis|plan|code> --artifact <review-*.md> [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
|
|
9
9
|
|
|
10
10
|
function failUsage(message: string): void {
|
|
11
11
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -27,7 +27,7 @@ async function taskReview(args: string[] = []): Promise<void> {
|
|
|
27
27
|
let request;
|
|
28
28
|
try { request = parseReviewCommand(args); }
|
|
29
29
|
catch (error) { failUsage(error instanceof Error ? error.message : String(error)); return; }
|
|
30
|
-
const { stage, artifact,
|
|
30
|
+
const { stage, artifact, dryRun, orchestrated, overrideTicket, overrideTarget, overrideScope } = request;
|
|
31
31
|
const operation = args[1]!;
|
|
32
32
|
const dryRunConflict = overrideDryRunConflict({ dryRun, overrideTicket, overrideTarget, overrideScope });
|
|
33
33
|
if (dryRunConflict) { failUsage(dryRunConflict.message); return; }
|
|
@@ -38,8 +38,8 @@ async function taskReview(args: string[] = []): Promise<void> {
|
|
|
38
38
|
try {
|
|
39
39
|
result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-review.${operation}`, async () => {
|
|
40
40
|
let current = operation === 'preflight'
|
|
41
|
-
? preflightReviewSummary({ taskRef: args[0]!, stage, artifact,
|
|
42
|
-
: finalizeReviewSummary({ taskRef: args[0]!, stage, artifact,
|
|
41
|
+
? preflightReviewSummary({ taskRef: args[0]!, stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true })
|
|
42
|
+
: finalizeReviewSummary({ taskRef: args[0]!, stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true });
|
|
43
43
|
if (operation === 'preflight') return current;
|
|
44
44
|
if (current.status !== 'failed' || !overrideTicket) return current;
|
|
45
45
|
if (!overrideTarget || !overrideScope) { failUsage('override ticket requires target and scope'); return current; }
|
|
@@ -49,7 +49,7 @@ async function taskReview(args: string[] = []): Promise<void> {
|
|
|
49
49
|
target: overrideTarget, scope: overrideScope
|
|
50
50
|
}, {
|
|
51
51
|
effectExecutor: (capability) => {
|
|
52
|
-
const retried = finalizeReviewSummary({ taskRef: args[0]!, stage, artifact,
|
|
52
|
+
const retried = finalizeReviewSummary({ taskRef: args[0]!, stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true, manualOverride: capability });
|
|
53
53
|
current = retried;
|
|
54
54
|
return retried.status === 'failed' || retried.status === 'planned'
|
|
55
55
|
? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no review effect was committed' : `${retried.error?.code ?? 'REVIEW_FAILED'}: ${retried.error?.message ?? 'manual review effect failed'}` }
|
|
@@ -140,11 +140,6 @@ function snapshotValidation(repoRoot: string, commit: string, options: ValidateO
|
|
|
140
140
|
cleanupError = error;
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
-
try {
|
|
144
|
-
git(repoRoot, ['worktree', 'prune']);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
cleanupError ??= error;
|
|
147
|
-
}
|
|
148
143
|
try {
|
|
149
144
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
150
145
|
} catch (error) {
|
|
@@ -189,6 +189,13 @@ function canonicalizeCommentBody(value: string): SanitizationResult<string> {
|
|
|
189
189
|
return sanitizeMarkdownDocument(value, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
function canonicalizeSummaryBody(value: string): SanitizationResult<string> {
|
|
193
|
+
const sanitized = canonicalizeCommentBody(value);
|
|
194
|
+
return sanitized.ok
|
|
195
|
+
? ok(`${sanitized.value.replace(/\n+$/, '')}\n`)
|
|
196
|
+
: sanitized;
|
|
197
|
+
}
|
|
198
|
+
|
|
192
199
|
function renderSafeCodeFence(value: string, language = '', reservedMarkers: readonly RegExp[] = [CONTROL_MARKER_PATTERN]): string {
|
|
193
200
|
const content = markerSafe(normalized(value), reservedMarkers);
|
|
194
201
|
const longestRun = Math.max(0, ...(content.match(/`+/g) || []).map((run) => run.length));
|
|
@@ -249,6 +256,7 @@ export {
|
|
|
249
256
|
fenceRanges,
|
|
250
257
|
renderSafeCodeFence,
|
|
251
258
|
canonicalizeCommentBody,
|
|
259
|
+
canonicalizeSummaryBody,
|
|
252
260
|
sanitizeMarkdownDocument,
|
|
253
261
|
splitDocumentPlaceholder
|
|
254
262
|
};
|
|
@@ -331,13 +331,14 @@ function issueSnapshot(value: any, graph?: { type: any; values: Array<{ name: st
|
|
|
331
331
|
};
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
function commentSnapshot(value: any): RemoteCommentSnapshot {
|
|
334
|
+
function commentSnapshot(value: any, createdSequence: number | null = null): RemoteCommentSnapshot {
|
|
335
335
|
return {
|
|
336
336
|
id: String(value.id),
|
|
337
337
|
author: value.user?.login ? { id: String(value.user.login), name: String(value.user.login) } : null,
|
|
338
338
|
body: String(value.body || ''),
|
|
339
339
|
createdAt: String(value.created_at || ''),
|
|
340
340
|
updatedAt: String(value.updated_at || value.created_at || '')
|
|
341
|
+
,createdSequence
|
|
341
342
|
};
|
|
342
343
|
}
|
|
343
344
|
|
|
@@ -603,7 +604,15 @@ function createGitHubOperations(client: GitHubClient): Pick<PlatformProvider, 'i
|
|
|
603
604
|
const response = client.json<any>(['api', '--paginate', '--slurp', `repos/${repository(context)}/issues/${number}/comments?per_page=100`], { cwd: context.workingDirectory });
|
|
604
605
|
if (!response.ok) return response;
|
|
605
606
|
const values = Array.isArray(response.value) ? response.value.flatMap((entry: any) => Array.isArray(entry) ? entry : [entry]) : [];
|
|
606
|
-
|
|
607
|
+
const comments = values.filter((entry: any) => entry && typeof entry === 'object');
|
|
608
|
+
let previous = 0;
|
|
609
|
+
const ordered = comments.length === values.length && comments.every((entry: any) => {
|
|
610
|
+
const id = Number(entry.id);
|
|
611
|
+
if (!Number.isSafeInteger(id) || id <= previous) return false;
|
|
612
|
+
previous = id;
|
|
613
|
+
return true;
|
|
614
|
+
});
|
|
615
|
+
return { ok: true, value: comments.map((comment: any) => commentSnapshot(comment, ordered ? Number(comment.id) : null)) };
|
|
607
616
|
},
|
|
608
617
|
async write({ context, parent, body, existingComment }) {
|
|
609
618
|
const number = resourceIdentityNumber(parent);
|
|
@@ -27,13 +27,19 @@ import {
|
|
|
27
27
|
import { projectTaskComment } from './task-comment-projection.ts';
|
|
28
28
|
import {
|
|
29
29
|
canonicalizeCommentBody,
|
|
30
|
+
canonicalizeSummaryBody,
|
|
30
31
|
escapeHtmlText,
|
|
31
32
|
fenceRanges,
|
|
32
33
|
renderSafeCodeFence
|
|
33
34
|
} from './comment-safety.ts';
|
|
34
35
|
import type { FenceRange } from './comment-safety.ts';
|
|
35
36
|
|
|
36
|
-
type RemoteComment = {
|
|
37
|
+
type RemoteComment = {
|
|
38
|
+
id: number | string;
|
|
39
|
+
body: string;
|
|
40
|
+
user?: { login?: string };
|
|
41
|
+
createdSequence?: number | null;
|
|
42
|
+
};
|
|
37
43
|
type RenderedChunk = { marker: string; body: string; content: string; part: number; total: number };
|
|
38
44
|
type CommentKind = 'task' | 'artifact' | 'summary' | 'cancel';
|
|
39
45
|
type SyncOptions = {
|
|
@@ -45,6 +51,8 @@ type SyncOptions = {
|
|
|
45
51
|
backfill?: boolean;
|
|
46
52
|
client?: PlatformClient;
|
|
47
53
|
runtimeVersion?: string;
|
|
54
|
+
summaryAuthorization?: { sha256: string };
|
|
55
|
+
verifyOnly?: boolean;
|
|
48
56
|
};
|
|
49
57
|
|
|
50
58
|
function providerCommentId(id: string, provider: { identity?: { comment?: string } }): number | string {
|
|
@@ -457,6 +465,49 @@ function relatedComments(comments: RemoteComment[], prefix: string): RemoteComme
|
|
|
457
465
|
});
|
|
458
466
|
}
|
|
459
467
|
|
|
468
|
+
function taskManagedComment(comment: RemoteComment, taskId: string): boolean {
|
|
469
|
+
const marker = normalizeCommentContent(comment.body).split('\n', 1)[0] || '';
|
|
470
|
+
return marker === MARKERS.task(taskId)
|
|
471
|
+
|| marker === MARKERS.summary(taskId)
|
|
472
|
+
|| marker.startsWith(`<!-- sync-issue:${taskId}:`);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function summaryPosition(comments: RemoteComment[], taskId: string): { ok: boolean; known: boolean; summary: RemoteComment | null } {
|
|
476
|
+
const managed = comments.filter((comment) => taskManagedComment(comment, taskId));
|
|
477
|
+
const summary = managed.filter((comment) => normalizeCommentContent(comment.body).split('\n', 1)[0] === MARKERS.summary(taskId));
|
|
478
|
+
if (summary.length !== 1) return { ok: false, known: false, summary: null };
|
|
479
|
+
if (managed.some((comment) => !Number.isSafeInteger(comment.createdSequence) || Number(comment.createdSequence) < 1)) {
|
|
480
|
+
return { ok: false, known: false, summary: summary[0]! };
|
|
481
|
+
}
|
|
482
|
+
const sequence = summary[0]!.createdSequence!;
|
|
483
|
+
return { ok: managed.every((comment) => sequence >= comment.createdSequence!), known: true, summary: summary[0]! };
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function summaryDigest(comment: RemoteComment): string {
|
|
487
|
+
const body = normalizeCommentContent(comment.body)
|
|
488
|
+
.replace(/^<!-- sync-issue:[^\n]+:summary -->\n## [^\n]+\n\n> [^\n]+\n\n/u, '')
|
|
489
|
+
.replace(/^<details><summary>恢复元数据<\/summary>[\s\S]*?<\/details>\n\n/u, '')
|
|
490
|
+
.replace(/\n---\n\*[^\n]*\*$/u, '');
|
|
491
|
+
const canonical = canonicalizeSummaryBody(body);
|
|
492
|
+
return canonical.ok ? createHash('sha256').update(canonical.value).digest('hex') : '';
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async function listedComments(provider: any, loaded: any, parent: ReturnType<typeof taskIssueIdentity>): Promise<any> {
|
|
496
|
+
return provider.comments?.list
|
|
497
|
+
? provider.comments.list({ context: providerOperationContext(loaded), parent }).then((response: any) => response.ok
|
|
498
|
+
? {
|
|
499
|
+
ok: true as const,
|
|
500
|
+
value: response.value.map((comment: any) => ({
|
|
501
|
+
id: providerCommentId(comment.id, provider),
|
|
502
|
+
body: comment.body,
|
|
503
|
+
user: comment.author?.name ? { login: comment.author.name } : undefined,
|
|
504
|
+
createdSequence: comment.createdSequence
|
|
505
|
+
}))
|
|
506
|
+
}
|
|
507
|
+
: response)
|
|
508
|
+
: unsupportedProviderOperation(provider, 'comments.list');
|
|
509
|
+
}
|
|
510
|
+
|
|
460
511
|
function validateRelatedMarkerSet(comments: RemoteComment[], prefix: string): { ok: boolean; code: string | null } {
|
|
461
512
|
const markers = comments.map((comment) => normalizeCommentContent(comment.body).split('\n', 1)[0] || '');
|
|
462
513
|
if (new Set(markers).size !== markers.length) return { ok: false, code: 'COMMENT_MARKER_CONFLICT' };
|
|
@@ -527,18 +578,7 @@ async function syncPlatformComment(taskRef: string, options: SyncOptions): Promi
|
|
|
527
578
|
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
528
579
|
if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
|
|
529
580
|
const issue = resourceIdentityNumber(issueIdentityFromTask);
|
|
530
|
-
const listed = loaded.value.provider.
|
|
531
|
-
? await loaded.value.provider.comments.list({
|
|
532
|
-
context: providerOperationContext(loaded.value),
|
|
533
|
-
parent: issueIdentityFromTask
|
|
534
|
-
}).then((response) => response.ok
|
|
535
|
-
? { ok: true as const, value: response.value.map((comment) => ({
|
|
536
|
-
id: providerCommentId(comment.id, loaded.value.provider),
|
|
537
|
-
body: comment.body,
|
|
538
|
-
user: comment.author?.name ? { login: comment.author.name } : undefined
|
|
539
|
-
})) }
|
|
540
|
-
: response)
|
|
541
|
-
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
581
|
+
const listed = await listedComments(loaded.value.provider, loaded.value, issueIdentityFromTask);
|
|
542
582
|
if (!listed.ok) {
|
|
543
583
|
return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
|
|
544
584
|
...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
|
|
@@ -569,6 +609,26 @@ async function syncPlatformComment(taskRef: string, options: SyncOptions): Promi
|
|
|
569
609
|
}
|
|
570
610
|
}
|
|
571
611
|
|
|
612
|
+
if (options.verifyOnly) {
|
|
613
|
+
if (options.kind !== 'summary' || !options.summaryAuthorization) {
|
|
614
|
+
return platformResult('failed', {
|
|
615
|
+
...contextFields(context), resource: { kind: 'issue', number: issue },
|
|
616
|
+
error: { code: 'SUMMARY_VERIFICATION_INVALID', message: 'summary verification requires durable authorization', retryable: false }
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
const position = summaryPosition(listed.value, resolved.taskId);
|
|
620
|
+
if (!position.ok || !position.summary || summaryDigest(position.summary) !== options.summaryAuthorization.sha256) {
|
|
621
|
+
return platformResult('failed', {
|
|
622
|
+
...contextFields(context), resource: { kind: 'issue', number: issue },
|
|
623
|
+
error: { code: 'SUMMARY_VERIFICATION_FAILED', message: 'summary marker, body digest, or managed comment order is invalid', retryable: true }
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
return platformResult('no-op', {
|
|
627
|
+
...contextFields(context), changed: false, resource: { kind: 'issue', number: issue },
|
|
628
|
+
comment: { kind: options.kind, marker: desired[0]!.marker, ids: [position.summary.id], parts: 1 }, error: null
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
|
|
572
632
|
|
|
573
633
|
// Backfill only supplies missing artifact comments; valid existing marker sets stay untouched.
|
|
574
634
|
if (options.kind === 'artifact' && options.backfill && existing.length > 0) {
|
|
@@ -646,6 +706,65 @@ async function syncPlatformComment(taskRef: string, options: SyncOptions): Promi
|
|
|
646
706
|
}
|
|
647
707
|
operations.push({ name: `comment:${comment.id}`, status: 'applied', reasonCode: 'STALE_CHUNK_DELETED' });
|
|
648
708
|
}
|
|
709
|
+
if (options.kind === 'summary') {
|
|
710
|
+
const refreshed = await listedComments(loaded.value.provider, loaded.value, issueIdentityFromTask);
|
|
711
|
+
if (!refreshed.ok) {
|
|
712
|
+
return platformResult(refreshed.error.retryable ? 'blocked' : 'failed', {
|
|
713
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error: refreshed.error
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
const position = summaryPosition(refreshed.value, resolved.taskId);
|
|
717
|
+
if (!position.ok && !position.known) {
|
|
718
|
+
return platformResult('blocked', {
|
|
719
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, operations,
|
|
720
|
+
error: { code: 'SUMMARY_POSITION_UNVERIFIED', message: 'Summary comment ordering cannot be proven from the provider snapshot', retryable: true }
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
if (!position.ok && position.summary) {
|
|
724
|
+
const owner = position.summary.user?.login;
|
|
725
|
+
if (!options.summaryAuthorization || options.summaryAuthorization.sha256 !== createHash('sha256').update(options.body ?? '').digest('hex')
|
|
726
|
+
|| !owner || owner !== context.platform.currentUser) {
|
|
727
|
+
return platformResult('failed', {
|
|
728
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, operations,
|
|
729
|
+
error: { code: 'SUMMARY_REPOSITION_UNAUTHORIZED', message: 'summary reposition requires current finalization authorization and an owned durable summary', retryable: false }
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
const deleted = loaded.value.provider.comments?.delete
|
|
733
|
+
? await loaded.value.provider.comments.delete({
|
|
734
|
+
context: providerOperationContext(loaded.value), parent: issueIdentityFromTask,
|
|
735
|
+
comment: { kind: 'id', value: String(position.summary.id) },
|
|
736
|
+
mutation: { idempotencyKey: `summary-reposition-delete:${resolved.taskId}:${position.summary.id}` }
|
|
737
|
+
})
|
|
738
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.delete');
|
|
739
|
+
if (!deleted.ok) {
|
|
740
|
+
return platformResult(deleted.error.retryable ? 'blocked' : 'failed', {
|
|
741
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error: deleted.error
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
const written = loaded.value.provider.comments?.write
|
|
745
|
+
? await loaded.value.provider.comments.write({
|
|
746
|
+
context: providerOperationContext(loaded.value), parent: issueIdentityFromTask, body: desired[0]!.body,
|
|
747
|
+
mutation: { idempotencyKey: `summary-reposition-write:${resolved.taskId}` }
|
|
748
|
+
})
|
|
749
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
750
|
+
if (!written.ok) {
|
|
751
|
+
return platformResult(written.error.retryable ? 'blocked' : 'failed', {
|
|
752
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error: written.error
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
ids.splice(0, ids.length, providerCommentId((written.value as { remoteId: string }).remoteId, loaded.value.provider));
|
|
756
|
+
operations.push({ name: `comment:${desired[0]!.marker}`, status: 'applied', reasonCode: 'SUMMARY_REPOSITIONED' });
|
|
757
|
+
const reconciled = await listedComments(loaded.value.provider, loaded.value, issueIdentityFromTask);
|
|
758
|
+
if (!reconciled.ok || !summaryPosition(reconciled.value, resolved.taskId).ok) {
|
|
759
|
+
const error = reconciled.ok
|
|
760
|
+
? { code: 'SUMMARY_POSITION_UNVERIFIED', message: 'Summary comment is not provably last among task-managed comments', retryable: true }
|
|
761
|
+
: reconciled.error;
|
|
762
|
+
return platformResult(error.retryable ? 'blocked' : 'failed', {
|
|
763
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, operations, error
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
649
768
|
const changed = operations.some((operation) => operation.status === 'applied');
|
|
650
769
|
const result = platformResult(changed ? 'applied' : 'no-op', {
|
|
651
770
|
...contextFields(context),
|
|
@@ -218,13 +218,17 @@ function issueType(value: unknown, label: string): NonNullable<IssueSnapshot['is
|
|
|
218
218
|
|
|
219
219
|
function validateComment(value: unknown): RemoteCommentSnapshot {
|
|
220
220
|
const item = record(value, 'comment');
|
|
221
|
-
exactKeys(item, ['id', 'author', 'body', 'createdAt', 'updatedAt'], 'comment');
|
|
221
|
+
exactKeys(item, ['id', 'author', 'body', 'createdAt', 'updatedAt', 'createdSequence'], 'comment');
|
|
222
222
|
return {
|
|
223
223
|
id: stringValue(item.id, 'comment.id'),
|
|
224
224
|
author: author(item.author, 'comment.author'),
|
|
225
225
|
body: stringValue(item.body, 'comment.body', true),
|
|
226
226
|
createdAt: utcTimestamp(item.createdAt, 'comment.createdAt'),
|
|
227
|
-
updatedAt: utcTimestamp(item.updatedAt, 'comment.updatedAt')
|
|
227
|
+
updatedAt: utcTimestamp(item.updatedAt, 'comment.updatedAt'),
|
|
228
|
+
createdSequence: item.createdSequence === null ? null : (() => {
|
|
229
|
+
if (!Number.isSafeInteger(item.createdSequence) || Number(item.createdSequence) < 1) throw new Error('comment.createdSequence must be a positive safe integer or null');
|
|
230
|
+
return Number(item.createdSequence);
|
|
231
|
+
})()
|
|
228
232
|
};
|
|
229
233
|
}
|
|
230
234
|
|
|
@@ -13,7 +13,7 @@ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.ts";
|
|
|
13
13
|
import { readPrDeliveryFact } from "../task/pr-delivery-fact.ts";
|
|
14
14
|
import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.ts";
|
|
15
15
|
import { taskIssueIdentity } from "./task-identities.ts";
|
|
16
|
-
import { renderTaskCommentResult } from "./issue-comments.ts";
|
|
16
|
+
import { MARKERS, renderTaskCommentResult } from "./issue-comments.ts";
|
|
17
17
|
import {
|
|
18
18
|
CONTROL_MARKER_PATTERN,
|
|
19
19
|
canonicalizeCommentBody,
|
|
@@ -279,7 +279,9 @@ async function fetchRemoteData(context: any, shared: VerificationShared): Promis
|
|
|
279
279
|
}
|
|
280
280
|
return {
|
|
281
281
|
issue,
|
|
282
|
-
comments: facts.value.comments.map((comment: { id: string; body: string }) => ({
|
|
282
|
+
comments: facts.value.comments.map((comment: { id: string; body: string; createdSequence: number | null }) => ({
|
|
283
|
+
id: comment.id, body: comment.body, createdSequence: comment.createdSequence
|
|
284
|
+
})),
|
|
283
285
|
prComments,
|
|
284
286
|
prLabels: changeRequest?.labels || null,
|
|
285
287
|
issueType: issueSnapshot
|
|
@@ -361,6 +363,21 @@ function checkCommentMarker(context: any, remoteData: any, shared: VerificationS
|
|
|
361
363
|
|
|
362
364
|
const comment = findCommentByMarker(remoteData.comments, context.marker);
|
|
363
365
|
if (comment) {
|
|
366
|
+
if (context.marker === MARKERS.summary(context.task.metadata.id)) {
|
|
367
|
+
const managed = remoteData.comments.filter((candidate: any) => {
|
|
368
|
+
const first = String(candidate.body || '').replace(/\r\n/g, '\n').split('\n', 1)[0] || '';
|
|
369
|
+
return first === MARKERS.task(context.task.metadata.id)
|
|
370
|
+
|| first === MARKERS.summary(context.task.metadata.id)
|
|
371
|
+
|| first.startsWith(`<!-- sync-issue:${context.task.metadata.id}:`);
|
|
372
|
+
});
|
|
373
|
+
if (managed.some((candidate: any) => !Number.isSafeInteger(candidate.createdSequence) || candidate.createdSequence < 1)) {
|
|
374
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} summary comment ordering is not provable`, "check_failed");
|
|
375
|
+
}
|
|
376
|
+
const summarySequence = comment.createdSequence;
|
|
377
|
+
if (!managed.every((candidate: any) => summarySequence >= candidate.createdSequence)) {
|
|
378
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} summary comment is not last among task-managed comments`, "check_failed");
|
|
379
|
+
}
|
|
380
|
+
}
|
|
364
381
|
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} has expected comment marker`);
|
|
365
382
|
}
|
|
366
383
|
|
|
@@ -441,49 +441,6 @@ async function executeRequestInner(
|
|
|
441
441
|
? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
|
|
442
442
|
: orchestrationFailure(code, message);
|
|
443
443
|
}
|
|
444
|
-
let controllerBinding: Readonly<{
|
|
445
|
-
instanceDigest: string;
|
|
446
|
-
controlGeneration: string;
|
|
447
|
-
}> | null = null;
|
|
448
|
-
if (request.family === 'task-orchestration') {
|
|
449
|
-
const codexPrepare = operation.family === 'task-orchestration'
|
|
450
|
-
&& operation.intent === 'prepare'
|
|
451
|
-
&& operation.input.client === 'codex';
|
|
452
|
-
if (codexPrepare) {
|
|
453
|
-
if (!request.controllerProof) {
|
|
454
|
-
const authority = options.lifecycleRecoveryAttestation;
|
|
455
|
-
if (!authority || authority.phase !== 'orchestration.prepare') {
|
|
456
|
-
return orchestrationFailure(
|
|
457
|
-
'CODEX_SANDBOX_CONTROLLER_PROOF_REQUIRED',
|
|
458
|
-
'Codex prepare requires a current controller lease proof'
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
controllerBinding = {
|
|
462
|
-
instanceDigest: authority.controllerInstanceDigest,
|
|
463
|
-
controlGeneration: authority.controlGeneration
|
|
464
|
-
};
|
|
465
|
-
} else {
|
|
466
|
-
try {
|
|
467
|
-
controllerBinding = (options.resolveControllerBinding ?? resolveCodexControllerBinding)({
|
|
468
|
-
manifest,
|
|
469
|
-
manifestPath,
|
|
470
|
-
proof: request.controllerProof,
|
|
471
|
-
buildIdentity: (options.buildIdentity ?? computeLifecycleBuildIdentity)(manifest.repoRoot)
|
|
472
|
-
});
|
|
473
|
-
} catch (error) {
|
|
474
|
-
const code = error instanceof CodexControllerRegistrationError
|
|
475
|
-
? error.code
|
|
476
|
-
: 'CODEX_SANDBOX_CONTROLLER_PROOF_INVALID';
|
|
477
|
-
return orchestrationFailure(code, `${code}: Codex controller proof was rejected`);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
} else if (request.controllerProof !== null) {
|
|
481
|
-
return orchestrationFailure(
|
|
482
|
-
'CODEX_SANDBOX_CONTROLLER_PROOF_INVALID',
|
|
483
|
-
'Controller proof is only accepted for canonical Codex prepare'
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
444
|
const diagnosticLog: OrchestrationDiagnosticLogger = (event, fields): void => {
|
|
488
445
|
appendExecutorAudit(manifest, event.startsWith('orchestration-') ? event : `orchestration-${event}`, {
|
|
489
446
|
requestId: request.id,
|
|
@@ -501,7 +458,6 @@ async function executeRequestInner(
|
|
|
501
458
|
manifestPath,
|
|
502
459
|
requestId: request.id,
|
|
503
460
|
diagnosticLog,
|
|
504
|
-
...(controllerBinding ? { controllerBinding } : {}),
|
|
505
461
|
lifecycleRecoveryAttestation: options.lifecycleRecoveryAttestation ?? null
|
|
506
462
|
});
|
|
507
463
|
const format = (value: unknown): SandboxControlExecutionResult => {
|