@fitlab-ai/agent-infra 0.9.8 → 0.9.10
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +11 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +58 -21
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +25 -11
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +173 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +127 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +63 -92
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
- package/dist/lib/agent-clients/adapters/index.js +3 -1
- package/dist/lib/agent-clients/adapters/traecli.js +66 -0
- package/dist/lib/agent-clients/tokens.js +2 -2
- package/dist/lib/agent-clients/types.js +2 -1
- package/dist/lib/defaults.json +5 -0
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/internal/codex-lifecycle.js +20 -6
- package/dist/lib/internal/codex-sandbox-controller.js +9 -15
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +2 -1
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-orchestration.js +156 -291
- package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +75 -42
- package/dist/lib/platform/pr-summary.js +48 -10
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/run/tui.js +5 -2
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/controller-registration.js +275 -0
- package/dist/lib/sandbox/control/executor.js +140 -5
- package/dist/lib/sandbox/control/lifecycle.js +3 -1
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/task/codex-orchestration.js +161 -39
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/delegation-receipts.js +248 -6
- package/dist/lib/task/finalization.js +593 -0
- package/dist/lib/task/index.js +0 -9
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +187 -761
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +110 -60
- package/dist/lib/task/verification.js +2 -1
- package/dist/lib/task/workflow-warning-intents.js +50 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +61 -19
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +37 -10
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +276 -12
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +163 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +91 -118
- package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
- package/lib/agent-clients/adapters/index.ts +3 -1
- package/lib/agent-clients/adapters/traecli.ts +67 -0
- package/lib/agent-clients/tokens.ts +2 -2
- package/lib/agent-clients/types.ts +2 -1
- package/lib/defaults.json +5 -0
- package/lib/git/workflow.ts +11 -2
- package/lib/internal/codex-lifecycle.ts +19 -6
- package/lib/internal/codex-sandbox-controller.ts +9 -14
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +2 -1
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-orchestration.ts +20 -103
- package/lib/{task/commands/validate.ts → internal/task-validate.ts} +75 -40
- package/lib/platform/pr-summary.ts +55 -11
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/run/tui.ts +5 -3
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/controller-registration.ts +352 -0
- package/lib/sandbox/control/executor.ts +169 -5
- package/lib/sandbox/control/lifecycle.ts +2 -1
- package/lib/sandbox/control/protocol.ts +80 -7
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +3 -3
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/task/codex-orchestration.ts +168 -40
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/delegation-receipts.ts +263 -4
- package/lib/task/finalization.ts +761 -0
- package/lib/task/index.ts +0 -8
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +218 -982
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +115 -63
- package/lib/task/verification.ts +3 -2
- package/lib/task/workflow-warning-intents.ts +57 -2
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +3 -4
- package/templates/.agents/QUICKSTART.zh-CN.md +3 -4
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -21
- package/templates/.agents/rules/README.en.md +1 -0
- package/templates/.agents/rules/README.zh-CN.md +1 -0
- package/templates/.agents/rules/compatibility-policy.en.md +39 -0
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +39 -0
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
- package/templates/.agents/rules/pr-sync.en.md +1 -1
- package/templates/.agents/rules/pr-sync.github.en.md +2 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +2 -0
- package/templates/.agents/rules/pr-sync.zh-CN.md +1 -0
- package/templates/.agents/rules/review-method.en.md +1 -0
- package/templates/.agents/rules/review-method.zh-CN.md +1 -0
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +17 -9
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +17 -9
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +41 -0
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +41 -0
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +2 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -1
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +8 -6
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +8 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.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
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +24 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/templates/.trae/skills/update-agent-infra.en.md +8 -0
- package/templates/.trae/skills/update-agent-infra.zh-CN.md +8 -0
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { commitExplicitPaths, inspectGitWorkflow, pushGitRefs } from '../git/workflow.js';
|
|
4
|
+
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
5
|
+
import { resolveTaskContext, resolveTaskRef } from './resolve-ref.js';
|
|
6
|
+
import { commitPushDecision } from './commit-policy.js';
|
|
7
|
+
import { mergeOperationWarnings } from './operation-outcome.js';
|
|
8
|
+
import { TaskExecutionLockError, withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.js';
|
|
9
|
+
import { appendActivityEntry, locateActivityLog } from './activity-log.js';
|
|
10
|
+
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
11
|
+
import { commitOrchestrationStageCompletion, planOrchestrationStageCompletion } from './orchestration.js';
|
|
12
|
+
const HEAD_REF = /^refs\/heads\/(.+)$/;
|
|
13
|
+
function isCommitExecutionMode(value) {
|
|
14
|
+
return value === 'direct' || value === 'orchestrated';
|
|
15
|
+
}
|
|
16
|
+
function gitText(repoRoot, args) {
|
|
17
|
+
return execFileSync('git', [...args], {
|
|
18
|
+
cwd: repoRoot,
|
|
19
|
+
encoding: 'utf8',
|
|
20
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
21
|
+
}).trim();
|
|
22
|
+
}
|
|
23
|
+
function canonicalRepositoryRoot(cwd) {
|
|
24
|
+
return gitText(cwd, ['rev-parse', '--show-toplevel']);
|
|
25
|
+
}
|
|
26
|
+
function failure(input, mode, taskId, code, message, status = 'failed', snapshot = null) {
|
|
27
|
+
return {
|
|
28
|
+
status,
|
|
29
|
+
changed: false,
|
|
30
|
+
result: status,
|
|
31
|
+
taskId,
|
|
32
|
+
mode,
|
|
33
|
+
warnings: [],
|
|
34
|
+
snapshot: snapshot ?? inspectGitWorkflow(input.cwd).snapshot,
|
|
35
|
+
operations: [],
|
|
36
|
+
error: { code, message }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function warning(code, message, retryable, step, target, severity) {
|
|
40
|
+
return { code, message, retryable, step, target, severity };
|
|
41
|
+
}
|
|
42
|
+
function resolveBoundTask(input) {
|
|
43
|
+
const mode = input.mode ?? 'direct';
|
|
44
|
+
if (input.taskRef !== undefined) {
|
|
45
|
+
const resolved = resolveTaskRef(input.taskRef, { repoRoot: input.cwd });
|
|
46
|
+
if (!resolved.ok)
|
|
47
|
+
return {
|
|
48
|
+
kind: 'error',
|
|
49
|
+
result: failure(input, mode, resolved.taskId, resolved.code, resolved.message)
|
|
50
|
+
};
|
|
51
|
+
return boundTask(input, resolved.taskId, resolved.taskDir, resolved.taskMdPath, resolved.repoRoot, resolved.state);
|
|
52
|
+
}
|
|
53
|
+
const context = resolveTaskContext(undefined, { repoRoot: input.cwd });
|
|
54
|
+
if (context.ok)
|
|
55
|
+
return boundTask(input, context.taskId, context.taskDir, context.taskMdPath, context.repoRoot, context.state);
|
|
56
|
+
if (context.code === 'TASK_CONTEXT_NOT_FOUND')
|
|
57
|
+
return { kind: 'none' };
|
|
58
|
+
return {
|
|
59
|
+
kind: 'error',
|
|
60
|
+
result: failure(input, mode, context.taskId, context.code, context.message)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function boundTask(input, taskId, taskDir, taskMdPath, repoRoot, state) {
|
|
64
|
+
let branch;
|
|
65
|
+
try {
|
|
66
|
+
const frontmatter = parseTaskFrontmatter(fs.readFileSync(taskMdPath, 'utf8'));
|
|
67
|
+
branch = frontmatter.branch ?? '';
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
kind: 'error',
|
|
72
|
+
result: failure(input, input.mode ?? 'direct', taskId, 'TASK_CONTEXT_UNREADABLE', error instanceof Error ? error.message : String(error))
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (!branch)
|
|
76
|
+
return {
|
|
77
|
+
kind: 'error',
|
|
78
|
+
result: failure(input, input.mode ?? 'direct', taskId, 'TASK_CONTEXT_UNREADABLE', 'task branch is missing')
|
|
79
|
+
};
|
|
80
|
+
return { kind: 'bound', task: { repoRoot, taskId, taskDir, taskMdPath, state, branch } };
|
|
81
|
+
}
|
|
82
|
+
function validateCommon(input, branch) {
|
|
83
|
+
if (!input.message.trim())
|
|
84
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit message is required' };
|
|
85
|
+
if (input.paths.length === 0 && !input.push)
|
|
86
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'At least one explicit path is required unless this is a push-only retry' };
|
|
87
|
+
if (!/^[a-f0-9]{40,64}$/.test(input.expectedHead))
|
|
88
|
+
return { code: 'GIT_HEAD_EXPECTATION_REQUIRED', message: 'A valid expected HEAD is required' };
|
|
89
|
+
if (!/^[a-f0-9]{40,64}$/.test(input.expectedTree))
|
|
90
|
+
return { code: 'GIT_TREE_EXPECTATION_REQUIRED', message: 'A valid expected tree is required' };
|
|
91
|
+
if (input.paths.some((candidate) => !candidate || candidate.startsWith('/') || candidate.split(/[\\/]/).includes('..'))) {
|
|
92
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit paths must stay inside the repository' };
|
|
93
|
+
}
|
|
94
|
+
if (input.paths.some((candidate) => /(^|\/)(?:\.env|credentials\.json|id_rsa)(?:$|\/)/i.test(candidate))) {
|
|
95
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Sensitive paths cannot be committed' };
|
|
96
|
+
}
|
|
97
|
+
if (branch !== null && branch === '')
|
|
98
|
+
return { code: 'GIT_BRANCH_INVALID', message: 'A named branch is required' };
|
|
99
|
+
if (input.push) {
|
|
100
|
+
if (!input.push.remote || input.push.refs.length !== 1)
|
|
101
|
+
return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
|
|
102
|
+
const ref = input.push.refs[0] ?? '';
|
|
103
|
+
if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
|
|
104
|
+
return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function validateOrchestrated(input, task) {
|
|
110
|
+
if (!input.agent)
|
|
111
|
+
return { error: { code: 'ORCHESTRATION_PROVENANCE_MISMATCH', message: 'orchestrated commit requires an agent' } };
|
|
112
|
+
const planned = planOrchestrationStageCompletion(task.taskId, {
|
|
113
|
+
stage: 'commit',
|
|
114
|
+
round: 1,
|
|
115
|
+
artifact: 'commit',
|
|
116
|
+
role: 'executor',
|
|
117
|
+
agent: input.agent
|
|
118
|
+
}, { repoRoot: task.repoRoot });
|
|
119
|
+
const run = planned.result.run;
|
|
120
|
+
if (!planned.plan
|
|
121
|
+
|| !run
|
|
122
|
+
|| run.status !== 'running'
|
|
123
|
+
|| !run.commitAuthorization.issuedAt
|
|
124
|
+
|| run.commitAuthorization.consumedAt)
|
|
125
|
+
return {
|
|
126
|
+
error: {
|
|
127
|
+
code: planned.result.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH',
|
|
128
|
+
message: planned.result.error?.message ?? 'orchestrated commit requires one matching activated commit delegation'
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
return { plan: planned.plan };
|
|
132
|
+
}
|
|
133
|
+
function syncTaskCommit(input, task, committedHead) {
|
|
134
|
+
let content;
|
|
135
|
+
try {
|
|
136
|
+
content = fs.readFileSync(task.taskMdPath, 'utf8');
|
|
137
|
+
const activity = locateActivityLog(content);
|
|
138
|
+
if (!activity)
|
|
139
|
+
return warning('TASK_STATUS_SYNC_FAILED', 'task has no unique Activity Log section', true, 'task', task.taskId, 'ACTION_REQUIRED');
|
|
140
|
+
const metadata = captureTaskWriteMetadata();
|
|
141
|
+
const commitNote = gitText(task.repoRoot, ['show', '-s', '--format=%h %s', committedHead]);
|
|
142
|
+
const alreadyLogged = activity.entries.some((entry) => entry.step === 'Commit' && entry.note === commitNote);
|
|
143
|
+
const mutations = [];
|
|
144
|
+
if (!alreadyLogged)
|
|
145
|
+
mutations.push({
|
|
146
|
+
kind: 'section',
|
|
147
|
+
aliases: ['活动日志', 'Activity Log'],
|
|
148
|
+
heading: activity.heading,
|
|
149
|
+
body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Commit', agent: input.agent, note: commitNote })
|
|
150
|
+
});
|
|
151
|
+
const frontmatter = { assigned_to: input.agent };
|
|
152
|
+
mutations.push({ kind: 'frontmatter', set: frontmatter });
|
|
153
|
+
if (mutations.length === 0)
|
|
154
|
+
return null;
|
|
155
|
+
const written = writeTask({
|
|
156
|
+
taskRef: task.taskId,
|
|
157
|
+
expectedState: 'active',
|
|
158
|
+
mutations
|
|
159
|
+
}, { repoRoot: task.repoRoot, metadataProvider: () => metadata });
|
|
160
|
+
if (written.status === 'failed')
|
|
161
|
+
return warning('TASK_STATUS_SYNC_FAILED', written.error.message, true, 'task', task.taskId, 'ACTION_REQUIRED');
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
return warning('TASK_STATUS_SYNC_FAILED', error instanceof Error ? error.message : String(error), true, 'task', task.taskId, 'ACTION_REQUIRED');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function executeUnlocked(input, task, mode) {
|
|
169
|
+
const inspected = inspectGitWorkflow(input.cwd);
|
|
170
|
+
if (!inspected.snapshot)
|
|
171
|
+
return failure(input, mode, task?.taskId ?? null, 'GIT_INSPECT_FAILED', 'Unable to inspect Git repository');
|
|
172
|
+
const branch = inspected.snapshot.branch;
|
|
173
|
+
const commonError = validateCommon(input, branch);
|
|
174
|
+
if (commonError)
|
|
175
|
+
return failure(input, mode, task?.taskId ?? null, commonError.code, commonError.message, 'blocked', inspected.snapshot);
|
|
176
|
+
if (task && task.state !== 'active')
|
|
177
|
+
return failure(input, mode, task.taskId, 'TASK_STATE_INVALID', 'task-bound commit requires an active task', 'blocked', inspected.snapshot);
|
|
178
|
+
if (task && task.branch !== branch)
|
|
179
|
+
return failure(input, mode, task.taskId, 'GIT_BRANCH_MISMATCH', `Task branch '${task.branch}' does not match current branch '${branch}'`, 'blocked', inspected.snapshot);
|
|
180
|
+
if (task && !input.agent)
|
|
181
|
+
return failure(input, mode, task.taskId, 'COMMIT_AGENT_REQUIRED', 'task-bound commit requires an agent', 'blocked', inspected.snapshot);
|
|
182
|
+
const orchestrationValidation = mode === 'orchestrated'
|
|
183
|
+
? (task
|
|
184
|
+
? validateOrchestrated(input, task)
|
|
185
|
+
: { error: { code: 'ORCHESTRATION_TASK_REQUIRED', message: 'orchestrated commit requires a task context' } })
|
|
186
|
+
: null;
|
|
187
|
+
if (orchestrationValidation && 'error' in orchestrationValidation) {
|
|
188
|
+
const error = orchestrationValidation.error;
|
|
189
|
+
return failure(input, mode, task?.taskId ?? null, error.code, error.message, 'blocked', inspected.snapshot);
|
|
190
|
+
}
|
|
191
|
+
const orchestrationCompletion = orchestrationValidation && 'plan' in orchestrationValidation
|
|
192
|
+
? orchestrationValidation.plan
|
|
193
|
+
: null;
|
|
194
|
+
const committed = input.paths.length === 0
|
|
195
|
+
? (() => {
|
|
196
|
+
if (inspected.snapshot.worktree.length > 0 || inspected.snapshot.staged.length > 0)
|
|
197
|
+
return {
|
|
198
|
+
status: 'failed',
|
|
199
|
+
changed: false,
|
|
200
|
+
snapshot: inspected.snapshot,
|
|
201
|
+
operations: [],
|
|
202
|
+
error: { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Push-only retry requires a clean working tree' }
|
|
203
|
+
};
|
|
204
|
+
if (inspected.snapshot.head !== input.expectedHead)
|
|
205
|
+
return {
|
|
206
|
+
status: 'failed',
|
|
207
|
+
changed: false,
|
|
208
|
+
snapshot: inspected.snapshot,
|
|
209
|
+
operations: [],
|
|
210
|
+
error: { code: 'GIT_HEAD_MISMATCH', message: `Expected HEAD ${input.expectedHead}, received ${inspected.snapshot.head}` }
|
|
211
|
+
};
|
|
212
|
+
const currentTree = gitText(input.cwd, ['rev-parse', 'HEAD^{tree}']);
|
|
213
|
+
if (currentTree !== input.expectedTree)
|
|
214
|
+
return {
|
|
215
|
+
status: 'failed',
|
|
216
|
+
changed: false,
|
|
217
|
+
snapshot: inspected.snapshot,
|
|
218
|
+
operations: [],
|
|
219
|
+
error: { code: 'GIT_TREE_MISMATCH', message: `Expected current tree ${input.expectedTree}, received ${currentTree}` }
|
|
220
|
+
};
|
|
221
|
+
return {
|
|
222
|
+
status: 'no-op',
|
|
223
|
+
changed: false,
|
|
224
|
+
snapshot: inspected.snapshot,
|
|
225
|
+
operations: [{ name: 'commit', status: 'no-op' }],
|
|
226
|
+
error: null
|
|
227
|
+
};
|
|
228
|
+
})()
|
|
229
|
+
: commitExplicitPaths({
|
|
230
|
+
cwd: input.cwd,
|
|
231
|
+
paths: input.paths,
|
|
232
|
+
message: input.message,
|
|
233
|
+
expectedHead: input.expectedHead,
|
|
234
|
+
expectedTree: input.expectedTree
|
|
235
|
+
});
|
|
236
|
+
if (committed.status === 'failed')
|
|
237
|
+
return {
|
|
238
|
+
...committed,
|
|
239
|
+
result: 'failed',
|
|
240
|
+
taskId: task?.taskId ?? null,
|
|
241
|
+
mode,
|
|
242
|
+
warnings: [],
|
|
243
|
+
snapshot: committed.snapshot
|
|
244
|
+
};
|
|
245
|
+
const warnings = [];
|
|
246
|
+
if (orchestrationCompletion) {
|
|
247
|
+
try {
|
|
248
|
+
commitOrchestrationStageCompletion(orchestrationCompletion);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
warnings.push(warning('ORCHESTRATION_COMPLETION_FAILED', error instanceof Error ? error.message : String(error), true, 'orchestration', task?.taskId ?? 'orchestration', 'ACTION_REQUIRED'));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
let status = committed.status;
|
|
255
|
+
let result = committed.status === 'applied' ? 'committed' : 'no_op';
|
|
256
|
+
if (input.push) {
|
|
257
|
+
const target = `${input.push.remote}:${input.push.refs[0]}`;
|
|
258
|
+
const decision = commitPushDecision({ branch, automatic: input.push.automatic }, target);
|
|
259
|
+
if (!decision.shouldPush) {
|
|
260
|
+
warnings.push(decision.warning);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
const pushed = pushGitRefs({
|
|
264
|
+
cwd: input.cwd,
|
|
265
|
+
remote: input.push.remote,
|
|
266
|
+
refs: input.push.refs,
|
|
267
|
+
expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
|
|
268
|
+
});
|
|
269
|
+
if (pushed.status !== 'applied')
|
|
270
|
+
warnings.push(warning('COMMIT_PUSH_FAILED', pushed.error?.message ?? 'Git push failed', true, 'push', target, 'ACTION_REQUIRED'));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (warnings.length > 0) {
|
|
274
|
+
status = 'applied';
|
|
275
|
+
result = 'committed_with_warnings';
|
|
276
|
+
}
|
|
277
|
+
if (task && (committed.status === 'applied' || committed.status === 'no-op')) {
|
|
278
|
+
const taskWarning = syncTaskCommit(input, task, committed.snapshot?.head ?? gitText(task.repoRoot, ['rev-parse', 'HEAD']));
|
|
279
|
+
if (taskWarning) {
|
|
280
|
+
warnings.push(taskWarning);
|
|
281
|
+
status = 'applied';
|
|
282
|
+
result = 'committed_with_warnings';
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
status,
|
|
287
|
+
changed: committed.changed,
|
|
288
|
+
result,
|
|
289
|
+
taskId: task?.taskId ?? null,
|
|
290
|
+
mode,
|
|
291
|
+
warnings: mergeOperationWarnings(warnings),
|
|
292
|
+
snapshot: committed.snapshot,
|
|
293
|
+
operations: committed.operations,
|
|
294
|
+
error: null
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function executeCommitOperation(input) {
|
|
298
|
+
if (input.mode !== undefined && !isCommitExecutionMode(input.mode))
|
|
299
|
+
return {
|
|
300
|
+
status: 'blocked',
|
|
301
|
+
changed: false,
|
|
302
|
+
result: 'blocked',
|
|
303
|
+
taskId: null,
|
|
304
|
+
mode: 'direct',
|
|
305
|
+
warnings: [],
|
|
306
|
+
snapshot: null,
|
|
307
|
+
operations: [],
|
|
308
|
+
error: { code: 'COMMIT_MODE_INVALID', message: 'commit mode must be direct or orchestrated' }
|
|
309
|
+
};
|
|
310
|
+
const mode = input.mode ?? 'direct';
|
|
311
|
+
let normalizedInput;
|
|
312
|
+
try {
|
|
313
|
+
normalizedInput = { ...input, cwd: canonicalRepositoryRoot(input.cwd) };
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
return failure(input, mode, null, 'GIT_INSPECT_FAILED', error instanceof Error ? error.message : 'Unable to resolve Git repository root', 'blocked');
|
|
317
|
+
}
|
|
318
|
+
const resolved = resolveBoundTask(normalizedInput);
|
|
319
|
+
if (resolved.kind === 'error')
|
|
320
|
+
return resolved.result;
|
|
321
|
+
const task = resolved.kind === 'bound' ? resolved.task : null;
|
|
322
|
+
if (mode === 'orchestrated' && !task)
|
|
323
|
+
return failure(normalizedInput, mode, null, 'ORCHESTRATION_TASK_REQUIRED', 'orchestrated commit requires a task context', 'blocked');
|
|
324
|
+
try {
|
|
325
|
+
return task
|
|
326
|
+
? withRepositoryMutationLock(normalizedInput.cwd, () => withTaskExecutionLock(normalizedInput.cwd, task.taskId, 'commit-operation', () => executeUnlocked(normalizedInput, task, mode)))
|
|
327
|
+
: withRepositoryMutationLock(normalizedInput.cwd, () => executeUnlocked(normalizedInput, null, mode));
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
const code = error instanceof TaskExecutionLockError ? error.code : 'COMMIT_OPERATION_FAILED';
|
|
331
|
+
return failure(normalizedInput, mode, task?.taskId ?? null, code, error instanceof Error ? error.message : String(error), 'blocked');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
export { executeCommitOperation };
|
|
335
|
+
//# sourceMappingURL=commit-operation.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function commitPushDecision(policy, target) {
|
|
2
|
+
const branch = policy.branch.trim();
|
|
3
|
+
if (policy.automatic && (branch === 'main' || branch === 'master'))
|
|
4
|
+
return {
|
|
5
|
+
shouldPush: false,
|
|
6
|
+
warning: {
|
|
7
|
+
code: 'COMMIT_AUTOPUSH_PROTECTED_BRANCH',
|
|
8
|
+
message: `Automatic commit push is disabled for protected branch '${branch}'`,
|
|
9
|
+
retryable: false,
|
|
10
|
+
step: 'push',
|
|
11
|
+
target,
|
|
12
|
+
severity: 'IMPORTANT'
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
return { shouldPush: true, warning: null };
|
|
16
|
+
}
|
|
17
|
+
export { commitPushDecision };
|
|
18
|
+
//# sourceMappingURL=commit-policy.js.map
|
|
@@ -1,5 +1,249 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { isAgentClientId } from '../agent-clients/types.js';
|
|
2
3
|
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function hasExactKeys(value, required, optional = []) {
|
|
8
|
+
if (!isRecord(value))
|
|
9
|
+
return false;
|
|
10
|
+
const keys = Object.keys(value);
|
|
11
|
+
const allowed = new Set([...required, ...optional]);
|
|
12
|
+
return required.every((key) => Object.prototype.hasOwnProperty.call(value, key))
|
|
13
|
+
&& keys.every((key) => allowed.has(key));
|
|
14
|
+
}
|
|
15
|
+
function exactText(value) {
|
|
16
|
+
return typeof value === 'string' && value.length > 0 && value.trim() === value;
|
|
17
|
+
}
|
|
18
|
+
function validCodexBuildMetadata(value) {
|
|
19
|
+
return Number.isSafeInteger(value.protocolVersion) && value.protocolVersion > 0
|
|
20
|
+
&& exactText(value.packageVersion)
|
|
21
|
+
&& exactText(value.internalExecutableBuildHash)
|
|
22
|
+
&& exactText(value.lifecycleContractHash);
|
|
23
|
+
}
|
|
24
|
+
function nullableText(value) {
|
|
25
|
+
return value === null || exactText(value);
|
|
26
|
+
}
|
|
27
|
+
function nullableSafeInteger(value) {
|
|
28
|
+
return value === null || (Number.isSafeInteger(value) && value >= 0);
|
|
29
|
+
}
|
|
30
|
+
const LIFECYCLE_PROVENANCE_KEYS = [
|
|
31
|
+
'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
|
|
32
|
+
'hookDefinitionHash', 'hookSource', 'hookSourcePathDigest', 'hookSourceHash',
|
|
33
|
+
'capabilitySessionId', 'capabilityTurnId', 'capabilityToolUseId',
|
|
34
|
+
'controllerInstanceDigest', 'controlGeneration'
|
|
35
|
+
];
|
|
36
|
+
function isDelegationLifecycleProvenance(value) {
|
|
37
|
+
if (!hasExactKeys(value, LIFECYCLE_PROVENANCE_KEYS))
|
|
38
|
+
return false;
|
|
39
|
+
return Number.isSafeInteger(value.protocolVersion)
|
|
40
|
+
&& value.protocolVersion > 0
|
|
41
|
+
&& exactText(value.packageVersion)
|
|
42
|
+
&& exactText(value.internalExecutableBuildHash)
|
|
43
|
+
&& exactText(value.lifecycleContractHash)
|
|
44
|
+
&& exactText(value.hookDefinitionHash)
|
|
45
|
+
&& ['project', 'managed', 'isolated-user'].includes(value.hookSource)
|
|
46
|
+
&& exactText(value.hookSourcePathDigest)
|
|
47
|
+
&& exactText(value.hookSourceHash)
|
|
48
|
+
&& exactText(value.capabilitySessionId)
|
|
49
|
+
&& exactText(value.capabilityTurnId)
|
|
50
|
+
&& exactText(value.capabilityToolUseId)
|
|
51
|
+
&& nullableText(value.controllerInstanceDigest)
|
|
52
|
+
&& nullableText(value.controlGeneration);
|
|
53
|
+
}
|
|
54
|
+
const HOST_EVIDENCE_REQUIRED_KEYS = [
|
|
55
|
+
'kind', 'hookDefinitionHash', 'startRevision', 'stopRevision', 'consumer', 'consumedAt'
|
|
56
|
+
];
|
|
57
|
+
const HOST_EVIDENCE_OPTIONAL_KEYS = [
|
|
58
|
+
'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
|
|
59
|
+
'hookSource', 'hookSourcePathDigest', 'hookSourceHash', 'capabilitySessionId',
|
|
60
|
+
'capabilityTurnId', 'capabilityToolUseId', 'spawnToolUseId', 'spawnObservedAt',
|
|
61
|
+
'controllerInstanceDigest', 'controlGeneration'
|
|
62
|
+
];
|
|
63
|
+
function isDelegationHostEvidence(value) {
|
|
64
|
+
if (!hasExactKeys(value, HOST_EVIDENCE_REQUIRED_KEYS, HOST_EVIDENCE_OPTIONAL_KEYS))
|
|
65
|
+
return false;
|
|
66
|
+
if (!['codex-lifecycle-v1', 'codex-lifecycle-v2'].includes(value.kind))
|
|
67
|
+
return false;
|
|
68
|
+
if (!exactText(value.hookDefinitionHash)
|
|
69
|
+
|| !Number.isSafeInteger(value.startRevision) || value.startRevision < 1
|
|
70
|
+
|| !nullableSafeInteger(value.stopRevision)
|
|
71
|
+
|| !nullableText(value.consumer)
|
|
72
|
+
|| !nullableText(value.consumedAt))
|
|
73
|
+
return false;
|
|
74
|
+
if (value.kind === 'codex-lifecycle-v1')
|
|
75
|
+
return true;
|
|
76
|
+
return Number.isSafeInteger(value.protocolVersion)
|
|
77
|
+
&& value.protocolVersion > 0
|
|
78
|
+
&& exactText(value.packageVersion)
|
|
79
|
+
&& exactText(value.internalExecutableBuildHash)
|
|
80
|
+
&& exactText(value.lifecycleContractHash)
|
|
81
|
+
&& ['project', 'managed', 'isolated-user'].includes(value.hookSource)
|
|
82
|
+
&& exactText(value.hookSourcePathDigest)
|
|
83
|
+
&& exactText(value.hookSourceHash)
|
|
84
|
+
&& exactText(value.capabilitySessionId)
|
|
85
|
+
&& exactText(value.capabilityTurnId)
|
|
86
|
+
&& exactText(value.spawnToolUseId)
|
|
87
|
+
&& exactText(value.spawnObservedAt)
|
|
88
|
+
&& nullableText(value.controllerInstanceDigest)
|
|
89
|
+
&& nullableText(value.controlGeneration);
|
|
90
|
+
}
|
|
91
|
+
function hasCurrentCodexEvidence(receipt) {
|
|
92
|
+
const provenance = receipt.lifecycleProvenance;
|
|
93
|
+
if (!provenance)
|
|
94
|
+
return false;
|
|
95
|
+
const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
96
|
+
if (!activated)
|
|
97
|
+
return receipt.hostEvidence === null;
|
|
98
|
+
const host = receipt.hostEvidence;
|
|
99
|
+
if (host?.kind !== 'codex-lifecycle-v2'
|
|
100
|
+
|| host.protocolVersion !== provenance.protocolVersion
|
|
101
|
+
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
102
|
+
|| host.hookSource !== provenance.hookSource
|
|
103
|
+
|| host.hookSourcePathDigest !== provenance.hookSourcePathDigest
|
|
104
|
+
|| host.hookSourceHash !== provenance.hookSourceHash
|
|
105
|
+
|| host.capabilitySessionId !== provenance.capabilitySessionId
|
|
106
|
+
|| host.capabilityTurnId !== provenance.capabilityTurnId
|
|
107
|
+
|| host.controllerInstanceDigest !== provenance.controllerInstanceDigest
|
|
108
|
+
|| host.controlGeneration !== provenance.controlGeneration
|
|
109
|
+
|| receipt.parentId !== provenance.capabilitySessionId
|
|
110
|
+
|| host.spawnToolUseId === provenance.capabilityToolUseId)
|
|
111
|
+
return false;
|
|
112
|
+
const spawnObservedAt = Date.parse(host.spawnObservedAt ?? '');
|
|
113
|
+
const spawnDispatchedAt = Date.parse(receipt.spawnDispatchedAt ?? '');
|
|
114
|
+
const activationDeadlineAt = Date.parse(receipt.activationDeadlineAt ?? '');
|
|
115
|
+
if (!Number.isFinite(spawnObservedAt)
|
|
116
|
+
|| !Number.isFinite(spawnDispatchedAt)
|
|
117
|
+
|| !Number.isFinite(activationDeadlineAt)
|
|
118
|
+
|| spawnObservedAt < spawnDispatchedAt
|
|
119
|
+
|| spawnObservedAt > activationDeadlineAt)
|
|
120
|
+
return false;
|
|
121
|
+
if (['sealed', 'consumed'].includes(receipt.status)) {
|
|
122
|
+
return Number.isSafeInteger(host.stopRevision)
|
|
123
|
+
&& host.stopRevision > host.startRevision
|
|
124
|
+
&& host.consumer === receipt.id
|
|
125
|
+
&& exactText(host.consumedAt);
|
|
126
|
+
}
|
|
127
|
+
return host.stopRevision === null && host.consumer === null && host.consumedAt === null;
|
|
128
|
+
}
|
|
129
|
+
const RECEIPT_KEYS = [
|
|
130
|
+
'id', 'taskId', 'runId', 'role', 'stage', 'round', 'artifact', 'client',
|
|
131
|
+
'requestedModel', 'requestedReasoningEffort', 'actualModel', 'actualReasoningEffort',
|
|
132
|
+
'modelFallbackReason', 'reasoningEffortFallbackReason', 'parentId', 'childId',
|
|
133
|
+
'spawnMode', 'agent', 'status', 'workspaceSnapshotScope', 'lifecycleProvenance',
|
|
134
|
+
'hostEvidence', 'beforeFingerprint', 'afterFingerprint', 'changedPaths', 'createdAt',
|
|
135
|
+
'preparedMonotonicMs', 'spawnDispatchMonotonicMs', 'activationDeadlineMonotonicMs',
|
|
136
|
+
'spawnDispatchedAt', 'activationDeadlineAt', 'startEvidenceMonotonicMs',
|
|
137
|
+
'activatedMonotonicMs', 'activatedAt', 'sealedAt', 'consumedAt'
|
|
138
|
+
];
|
|
139
|
+
function hasStatusBoundEvidence(receipt) {
|
|
140
|
+
const dispatchFields = [
|
|
141
|
+
receipt.spawnDispatchMonotonicMs,
|
|
142
|
+
receipt.activationDeadlineMonotonicMs,
|
|
143
|
+
receipt.spawnDispatchedAt,
|
|
144
|
+
receipt.activationDeadlineAt
|
|
145
|
+
];
|
|
146
|
+
const dispatchEmpty = dispatchFields.every((field) => field === null);
|
|
147
|
+
const dispatchComplete = dispatchFields.every((field) => field !== null);
|
|
148
|
+
if (!dispatchEmpty && !dispatchComplete)
|
|
149
|
+
return false;
|
|
150
|
+
const beforeActivation = ['prepared', 'aborted', 'expired'].includes(receipt.status);
|
|
151
|
+
if (beforeActivation) {
|
|
152
|
+
return receipt.parentId === null
|
|
153
|
+
&& receipt.childId === null
|
|
154
|
+
&& receipt.spawnMode === null
|
|
155
|
+
&& receipt.actualModel === null
|
|
156
|
+
&& receipt.actualReasoningEffort === null
|
|
157
|
+
&& receipt.modelFallbackReason === null
|
|
158
|
+
&& receipt.reasoningEffortFallbackReason === null
|
|
159
|
+
&& receipt.agent === null
|
|
160
|
+
&& receipt.hostEvidence === null
|
|
161
|
+
&& receipt.startEvidenceMonotonicMs === null
|
|
162
|
+
&& receipt.activatedMonotonicMs === null
|
|
163
|
+
&& receipt.activatedAt === null
|
|
164
|
+
&& receipt.afterFingerprint === null
|
|
165
|
+
&& receipt.changedPaths.length === 0
|
|
166
|
+
&& receipt.sealedAt === null
|
|
167
|
+
&& receipt.consumedAt === null;
|
|
168
|
+
}
|
|
169
|
+
if (!dispatchComplete
|
|
170
|
+
|| !exactText(receipt.parentId)
|
|
171
|
+
|| !exactText(receipt.childId)
|
|
172
|
+
|| receipt.parentId === receipt.childId
|
|
173
|
+
|| receipt.startEvidenceMonotonicMs === null
|
|
174
|
+
|| receipt.activatedMonotonicMs === null
|
|
175
|
+
|| !exactText(receipt.activatedAt))
|
|
176
|
+
return false;
|
|
177
|
+
if (receipt.status === 'activated') {
|
|
178
|
+
return receipt.agent === null
|
|
179
|
+
&& receipt.afterFingerprint === null
|
|
180
|
+
&& receipt.changedPaths.length === 0
|
|
181
|
+
&& receipt.sealedAt === null
|
|
182
|
+
&& receipt.consumedAt === null;
|
|
183
|
+
}
|
|
184
|
+
if (!exactText(receipt.agent)
|
|
185
|
+
|| normalizeAgentToken(receipt.agent) !== normalizeAgentToken(receipt.client))
|
|
186
|
+
return false;
|
|
187
|
+
if (receipt.status === 'stage-completed') {
|
|
188
|
+
return receipt.afterFingerprint === null
|
|
189
|
+
&& receipt.changedPaths.length === 0
|
|
190
|
+
&& receipt.sealedAt === null
|
|
191
|
+
&& receipt.consumedAt === null;
|
|
192
|
+
}
|
|
193
|
+
if (!exactText(receipt.afterFingerprint) || !exactText(receipt.sealedAt))
|
|
194
|
+
return false;
|
|
195
|
+
return receipt.status === 'sealed'
|
|
196
|
+
? receipt.consumedAt === null
|
|
197
|
+
: receipt.status === 'consumed' && exactText(receipt.consumedAt);
|
|
198
|
+
}
|
|
199
|
+
function isDelegationReceipt(value) {
|
|
200
|
+
if (!hasExactKeys(value, RECEIPT_KEYS))
|
|
201
|
+
return false;
|
|
202
|
+
const structurallyValid = exactText(value.id)
|
|
203
|
+
&& exactText(value.taskId)
|
|
204
|
+
&& exactText(value.runId)
|
|
205
|
+
&& ['executor', 'reviewer'].includes(value.role)
|
|
206
|
+
&& ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code', 'commit'].includes(value.stage)
|
|
207
|
+
&& Number.isSafeInteger(value.round)
|
|
208
|
+
&& value.round > 0
|
|
209
|
+
&& exactText(value.artifact)
|
|
210
|
+
&& isAgentClientId(value.client)
|
|
211
|
+
&& exactText(value.requestedModel)
|
|
212
|
+
&& exactText(value.requestedReasoningEffort)
|
|
213
|
+
&& nullableText(value.actualModel)
|
|
214
|
+
&& nullableText(value.actualReasoningEffort)
|
|
215
|
+
&& nullableText(value.modelFallbackReason)
|
|
216
|
+
&& nullableText(value.reasoningEffortFallbackReason)
|
|
217
|
+
&& nullableText(value.parentId)
|
|
218
|
+
&& nullableText(value.childId)
|
|
219
|
+
&& nullableText(value.spawnMode)
|
|
220
|
+
&& nullableText(value.agent)
|
|
221
|
+
&& ['prepared', 'activated', 'stage-completed', 'sealed', 'consumed', 'aborted', 'expired'].includes(value.status)
|
|
222
|
+
&& value.workspaceSnapshotScope === 'task'
|
|
223
|
+
&& (value.lifecycleProvenance === null || isDelegationLifecycleProvenance(value.lifecycleProvenance))
|
|
224
|
+
&& (value.hostEvidence === null || isDelegationHostEvidence(value.hostEvidence))
|
|
225
|
+
&& exactText(value.beforeFingerprint)
|
|
226
|
+
&& nullableText(value.afterFingerprint)
|
|
227
|
+
&& Array.isArray(value.changedPaths)
|
|
228
|
+
&& value.changedPaths.every(exactText)
|
|
229
|
+
&& exactText(value.createdAt)
|
|
230
|
+
&& nullableSafeInteger(value.preparedMonotonicMs)
|
|
231
|
+
&& value.preparedMonotonicMs !== null
|
|
232
|
+
&& nullableSafeInteger(value.spawnDispatchMonotonicMs)
|
|
233
|
+
&& nullableSafeInteger(value.activationDeadlineMonotonicMs)
|
|
234
|
+
&& nullableText(value.spawnDispatchedAt)
|
|
235
|
+
&& nullableText(value.activationDeadlineAt)
|
|
236
|
+
&& nullableSafeInteger(value.startEvidenceMonotonicMs)
|
|
237
|
+
&& nullableSafeInteger(value.activatedMonotonicMs)
|
|
238
|
+
&& nullableText(value.activatedAt)
|
|
239
|
+
&& nullableText(value.sealedAt)
|
|
240
|
+
&& nullableText(value.consumedAt);
|
|
241
|
+
if (!structurallyValid)
|
|
242
|
+
return false;
|
|
243
|
+
const receipt = value;
|
|
244
|
+
return hasStatusBoundEvidence(receipt)
|
|
245
|
+
&& (receipt.client !== 'codex' || hasCurrentCodexEvidence(receipt));
|
|
246
|
+
}
|
|
3
247
|
const MANAGED_AGENTS = {
|
|
4
248
|
'agent-infra-lifecycle-executor': 'executor',
|
|
5
249
|
'agent-infra-lifecycle-reviewer': 'reviewer'
|
|
@@ -53,7 +297,7 @@ function dispatchDelegation(receipt, options = {}) {
|
|
|
53
297
|
}
|
|
54
298
|
const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))();
|
|
55
299
|
const dispatchedAt = (options.now ?? (() => new Date().toISOString()))();
|
|
56
|
-
const window = options.activationWindowMs ??
|
|
300
|
+
const window = options.activationWindowMs ?? 60_000;
|
|
57
301
|
return { ok: true, receipt: Object.freeze({
|
|
58
302
|
...receipt,
|
|
59
303
|
spawnDispatchMonotonicMs: monotonic,
|
|
@@ -138,9 +382,6 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
138
382
|
const activationDeadlineAt = Date.parse(receipt.activationDeadlineAt);
|
|
139
383
|
if (!expected
|
|
140
384
|
|| event.hostEvidence.protocolVersion !== expected.protocolVersion
|
|
141
|
-
|| event.hostEvidence.packageVersion !== expected.packageVersion
|
|
142
|
-
|| event.hostEvidence.internalExecutableBuildHash !== expected.internalExecutableBuildHash
|
|
143
|
-
|| event.hostEvidence.lifecycleContractHash !== expected.lifecycleContractHash
|
|
144
385
|
|| event.hostEvidence.hookDefinitionHash !== expected.hookDefinitionHash
|
|
145
386
|
|| event.hostEvidence.hookSource !== expected.hookSource
|
|
146
387
|
|| event.hostEvidence.hookSourcePathDigest !== expected.hookSourcePathDigest
|
|
@@ -154,7 +395,8 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
154
395
|
|| spawnObservedAt > activationDeadlineAt
|
|
155
396
|
|| event.parentId !== expected.capabilitySessionId
|
|
156
397
|
|| (event.hostEvidence.controllerInstanceDigest ?? null) !== expected.controllerInstanceDigest
|
|
157
|
-
|| (event.hostEvidence.controlGeneration ?? null) !== expected.controlGeneration
|
|
398
|
+
|| (event.hostEvidence.controlGeneration ?? null) !== expected.controlGeneration
|
|
399
|
+
|| !validCodexBuildMetadata(event.hostEvidence))
|
|
158
400
|
return fail('DELEGATION_HOST_EVIDENCE_INVALID', 'Codex lifecycle provenance does not match the prepared receipt');
|
|
159
401
|
}
|
|
160
402
|
return { ok: true, receipt: Object.freeze({
|
|
@@ -252,5 +494,5 @@ function consumeDelegation(receipt, options = {}) {
|
|
|
252
494
|
consumedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
253
495
|
}) };
|
|
254
496
|
}
|
|
255
|
-
export { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, managedDelegationRole, prepareDelegation, sealDelegation };
|
|
497
|
+
export { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation };
|
|
256
498
|
//# sourceMappingURL=delegation-receipts.js.map
|