@fitlab-ai/agent-infra 0.9.8 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/bin/internal-cli.js +6 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +58 -21
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +173 -10
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +56 -67
  10. package/dist/lib/git/workflow.js +10 -1
  11. package/dist/lib/internal/codex-lifecycle.js +19 -5
  12. package/dist/lib/internal/codex-sandbox-controller.js +6 -13
  13. package/dist/lib/internal/git-workflow.js +5 -7
  14. package/dist/lib/internal/platform-pr.js +20 -7
  15. package/dist/lib/internal/release-workflow.js +2 -1
  16. package/dist/lib/internal/sandbox-control.js +79 -2
  17. package/dist/lib/internal/task-finalization.js +54 -0
  18. package/dist/lib/internal/task-orchestration.js +156 -291
  19. package/dist/lib/platform/pr-summary.js +48 -10
  20. package/dist/lib/platform/pull-requests.js +205 -41
  21. package/dist/lib/sandbox/commands/rm.js +316 -55
  22. package/dist/lib/sandbox/control/client.js +190 -6
  23. package/dist/lib/sandbox/control/controller-registration.js +273 -0
  24. package/dist/lib/sandbox/control/executor.js +137 -5
  25. package/dist/lib/sandbox/control/lifecycle.js +3 -1
  26. package/dist/lib/sandbox/control/protocol.js +67 -6
  27. package/dist/lib/sandbox/index.js +5 -3
  28. package/dist/lib/sandbox/managed-fs.js +5 -0
  29. package/dist/lib/sandbox/task-resolver.js +50 -6
  30. package/dist/lib/sandbox/workspace-identity.js +27 -0
  31. package/dist/lib/sandbox/worktree-safety.js +215 -2
  32. package/dist/lib/task/codex-orchestration.js +158 -33
  33. package/dist/lib/task/commands/status.js +59 -1
  34. package/dist/lib/task/commit-operation.js +335 -0
  35. package/dist/lib/task/commit-policy.js +18 -0
  36. package/dist/lib/task/delegation-receipts.js +243 -2
  37. package/dist/lib/task/finalization.js +567 -0
  38. package/dist/lib/task/operation-outcome.js +33 -0
  39. package/dist/lib/task/orchestration.js +187 -761
  40. package/dist/lib/task/short-id.js +59 -1
  41. package/dist/lib/task/task-execution-lock.js +6 -1
  42. package/dist/lib/task/verification-engine.js +113 -60
  43. package/dist/lib/task/verification.js +2 -1
  44. package/dist/lib/task/workflow-warning-intents.js +50 -1
  45. package/dist/lifecycle-build-manifest.json +3 -3
  46. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +61 -19
  47. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
  48. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +276 -12
  49. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
  50. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
  51. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
  52. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +83 -91
  53. package/lib/git/workflow.ts +11 -2
  54. package/lib/internal/codex-lifecycle.ts +18 -5
  55. package/lib/internal/codex-sandbox-controller.ts +6 -12
  56. package/lib/internal/git-workflow.ts +5 -6
  57. package/lib/internal/platform-pr.ts +14 -7
  58. package/lib/internal/release-workflow.ts +2 -1
  59. package/lib/internal/sandbox-control.ts +92 -2
  60. package/lib/internal/task-finalization.ts +68 -0
  61. package/lib/internal/task-orchestration.ts +20 -103
  62. package/lib/platform/pr-summary.ts +55 -11
  63. package/lib/platform/pull-requests.ts +211 -41
  64. package/lib/sandbox/commands/rm.ts +413 -61
  65. package/lib/sandbox/control/client.ts +255 -6
  66. package/lib/sandbox/control/controller-registration.ts +347 -0
  67. package/lib/sandbox/control/executor.ts +163 -5
  68. package/lib/sandbox/control/lifecycle.ts +2 -1
  69. package/lib/sandbox/control/protocol.ts +80 -7
  70. package/lib/sandbox/index.ts +5 -3
  71. package/lib/sandbox/managed-fs.ts +5 -0
  72. package/lib/sandbox/task-resolver.ts +58 -7
  73. package/lib/sandbox/workspace-identity.ts +50 -2
  74. package/lib/sandbox/workspace-view.ts +3 -3
  75. package/lib/sandbox/worktree-safety.ts +236 -2
  76. package/lib/task/codex-orchestration.ts +167 -33
  77. package/lib/task/commands/status.ts +78 -1
  78. package/lib/task/commit-operation.ts +430 -0
  79. package/lib/task/commit-policy.ts +30 -0
  80. package/lib/task/delegation-receipts.ts +258 -1
  81. package/lib/task/finalization.ts +719 -0
  82. package/lib/task/operation-outcome.ts +48 -0
  83. package/lib/task/orchestration.ts +217 -982
  84. package/lib/task/short-id.ts +65 -1
  85. package/lib/task/task-execution-lock.ts +10 -1
  86. package/lib/task/verification-engine.ts +118 -63
  87. package/lib/task/verification.ts +3 -2
  88. package/lib/task/workflow-warning-intents.ts +57 -2
  89. package/package.json +2 -2
  90. package/templates/.agents/QUICKSTART.en.md +2 -2
  91. package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
  92. package/templates/.agents/hooks/lifecycle-delegation.js +1 -21
  93. package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
  94. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
  95. package/templates/.agents/rules/task-management.en.md +2 -2
  96. package/templates/.agents/rules/task-management.zh-CN.md +2 -2
  97. package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
  98. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
  99. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  100. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  101. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
  102. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
  103. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
  104. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
  105. package/templates/.agents/skills/commit/SKILL.en.md +41 -125
  106. package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
  107. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  108. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  109. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
  110. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
  111. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  112. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  113. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
  114. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
  115. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
  116. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
  117. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  118. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  119. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  120. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  121. package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
  122. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
  123. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  124. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  125. package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
  126. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
  127. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
  128. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
  129. package/templates/.codex/hooks.json +4 -4
  130. package/dist/lib/task/commit-finalization.js +0 -305
  131. package/dist/lib/task/commit-intent.js +0 -165
  132. package/lib/task/commit-finalization.ts +0 -419
  133. 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,246 @@
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 nullableText(value) {
19
+ return value === null || exactText(value);
20
+ }
21
+ function nullableSafeInteger(value) {
22
+ return value === null || (Number.isSafeInteger(value) && value >= 0);
23
+ }
24
+ const LIFECYCLE_PROVENANCE_KEYS = [
25
+ 'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
26
+ 'hookDefinitionHash', 'hookSource', 'hookSourcePathDigest', 'hookSourceHash',
27
+ 'capabilitySessionId', 'capabilityTurnId', 'capabilityToolUseId',
28
+ 'controllerInstanceDigest', 'controlGeneration'
29
+ ];
30
+ function isDelegationLifecycleProvenance(value) {
31
+ if (!hasExactKeys(value, LIFECYCLE_PROVENANCE_KEYS))
32
+ return false;
33
+ return Number.isSafeInteger(value.protocolVersion)
34
+ && value.protocolVersion > 0
35
+ && exactText(value.packageVersion)
36
+ && exactText(value.internalExecutableBuildHash)
37
+ && exactText(value.lifecycleContractHash)
38
+ && exactText(value.hookDefinitionHash)
39
+ && ['project', 'managed', 'isolated-user'].includes(value.hookSource)
40
+ && exactText(value.hookSourcePathDigest)
41
+ && exactText(value.hookSourceHash)
42
+ && exactText(value.capabilitySessionId)
43
+ && exactText(value.capabilityTurnId)
44
+ && exactText(value.capabilityToolUseId)
45
+ && nullableText(value.controllerInstanceDigest)
46
+ && nullableText(value.controlGeneration);
47
+ }
48
+ const HOST_EVIDENCE_REQUIRED_KEYS = [
49
+ 'kind', 'hookDefinitionHash', 'startRevision', 'stopRevision', 'consumer', 'consumedAt'
50
+ ];
51
+ const HOST_EVIDENCE_OPTIONAL_KEYS = [
52
+ 'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
53
+ 'hookSource', 'hookSourcePathDigest', 'hookSourceHash', 'capabilitySessionId',
54
+ 'capabilityTurnId', 'capabilityToolUseId', 'spawnToolUseId', 'spawnObservedAt',
55
+ 'controllerInstanceDigest', 'controlGeneration'
56
+ ];
57
+ function isDelegationHostEvidence(value) {
58
+ if (!hasExactKeys(value, HOST_EVIDENCE_REQUIRED_KEYS, HOST_EVIDENCE_OPTIONAL_KEYS))
59
+ return false;
60
+ if (!['codex-lifecycle-v1', 'codex-lifecycle-v2'].includes(value.kind))
61
+ return false;
62
+ if (!exactText(value.hookDefinitionHash)
63
+ || !Number.isSafeInteger(value.startRevision) || value.startRevision < 1
64
+ || !nullableSafeInteger(value.stopRevision)
65
+ || !nullableText(value.consumer)
66
+ || !nullableText(value.consumedAt))
67
+ return false;
68
+ if (value.kind === 'codex-lifecycle-v1')
69
+ return true;
70
+ return Number.isSafeInteger(value.protocolVersion)
71
+ && value.protocolVersion > 0
72
+ && exactText(value.packageVersion)
73
+ && exactText(value.internalExecutableBuildHash)
74
+ && exactText(value.lifecycleContractHash)
75
+ && ['project', 'managed', 'isolated-user'].includes(value.hookSource)
76
+ && exactText(value.hookSourcePathDigest)
77
+ && exactText(value.hookSourceHash)
78
+ && exactText(value.capabilitySessionId)
79
+ && exactText(value.capabilityTurnId)
80
+ && exactText(value.spawnToolUseId)
81
+ && exactText(value.spawnObservedAt)
82
+ && nullableText(value.controllerInstanceDigest)
83
+ && nullableText(value.controlGeneration);
84
+ }
85
+ function hasCurrentCodexEvidence(receipt) {
86
+ const provenance = receipt.lifecycleProvenance;
87
+ if (!provenance)
88
+ return false;
89
+ const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
90
+ if (!activated)
91
+ return receipt.hostEvidence === null;
92
+ const host = receipt.hostEvidence;
93
+ if (host?.kind !== 'codex-lifecycle-v2'
94
+ || host.protocolVersion !== provenance.protocolVersion
95
+ || host.packageVersion !== provenance.packageVersion
96
+ || host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
97
+ || host.lifecycleContractHash !== provenance.lifecycleContractHash
98
+ || host.hookDefinitionHash !== provenance.hookDefinitionHash
99
+ || host.hookSource !== provenance.hookSource
100
+ || host.hookSourcePathDigest !== provenance.hookSourcePathDigest
101
+ || host.hookSourceHash !== provenance.hookSourceHash
102
+ || host.capabilitySessionId !== provenance.capabilitySessionId
103
+ || host.capabilityTurnId !== provenance.capabilityTurnId
104
+ || host.controllerInstanceDigest !== provenance.controllerInstanceDigest
105
+ || host.controlGeneration !== provenance.controlGeneration
106
+ || receipt.parentId !== provenance.capabilitySessionId
107
+ || host.spawnToolUseId === provenance.capabilityToolUseId)
108
+ return false;
109
+ const spawnObservedAt = Date.parse(host.spawnObservedAt ?? '');
110
+ const spawnDispatchedAt = Date.parse(receipt.spawnDispatchedAt ?? '');
111
+ const activationDeadlineAt = Date.parse(receipt.activationDeadlineAt ?? '');
112
+ if (!Number.isFinite(spawnObservedAt)
113
+ || !Number.isFinite(spawnDispatchedAt)
114
+ || !Number.isFinite(activationDeadlineAt)
115
+ || spawnObservedAt < spawnDispatchedAt
116
+ || spawnObservedAt > activationDeadlineAt)
117
+ return false;
118
+ if (['sealed', 'consumed'].includes(receipt.status)) {
119
+ return Number.isSafeInteger(host.stopRevision)
120
+ && host.stopRevision > host.startRevision
121
+ && host.consumer === receipt.id
122
+ && exactText(host.consumedAt);
123
+ }
124
+ return host.stopRevision === null && host.consumer === null && host.consumedAt === null;
125
+ }
126
+ const RECEIPT_KEYS = [
127
+ 'id', 'taskId', 'runId', 'role', 'stage', 'round', 'artifact', 'client',
128
+ 'requestedModel', 'requestedReasoningEffort', 'actualModel', 'actualReasoningEffort',
129
+ 'modelFallbackReason', 'reasoningEffortFallbackReason', 'parentId', 'childId',
130
+ 'spawnMode', 'agent', 'status', 'workspaceSnapshotScope', 'lifecycleProvenance',
131
+ 'hostEvidence', 'beforeFingerprint', 'afterFingerprint', 'changedPaths', 'createdAt',
132
+ 'preparedMonotonicMs', 'spawnDispatchMonotonicMs', 'activationDeadlineMonotonicMs',
133
+ 'spawnDispatchedAt', 'activationDeadlineAt', 'startEvidenceMonotonicMs',
134
+ 'activatedMonotonicMs', 'activatedAt', 'sealedAt', 'consumedAt'
135
+ ];
136
+ function hasStatusBoundEvidence(receipt) {
137
+ const dispatchFields = [
138
+ receipt.spawnDispatchMonotonicMs,
139
+ receipt.activationDeadlineMonotonicMs,
140
+ receipt.spawnDispatchedAt,
141
+ receipt.activationDeadlineAt
142
+ ];
143
+ const dispatchEmpty = dispatchFields.every((field) => field === null);
144
+ const dispatchComplete = dispatchFields.every((field) => field !== null);
145
+ if (!dispatchEmpty && !dispatchComplete)
146
+ return false;
147
+ const beforeActivation = ['prepared', 'aborted', 'expired'].includes(receipt.status);
148
+ if (beforeActivation) {
149
+ return receipt.parentId === null
150
+ && receipt.childId === null
151
+ && receipt.spawnMode === null
152
+ && receipt.actualModel === null
153
+ && receipt.actualReasoningEffort === null
154
+ && receipt.modelFallbackReason === null
155
+ && receipt.reasoningEffortFallbackReason === null
156
+ && receipt.agent === null
157
+ && receipt.hostEvidence === null
158
+ && receipt.startEvidenceMonotonicMs === null
159
+ && receipt.activatedMonotonicMs === null
160
+ && receipt.activatedAt === null
161
+ && receipt.afterFingerprint === null
162
+ && receipt.changedPaths.length === 0
163
+ && receipt.sealedAt === null
164
+ && receipt.consumedAt === null;
165
+ }
166
+ if (!dispatchComplete
167
+ || !exactText(receipt.parentId)
168
+ || !exactText(receipt.childId)
169
+ || receipt.parentId === receipt.childId
170
+ || receipt.startEvidenceMonotonicMs === null
171
+ || receipt.activatedMonotonicMs === null
172
+ || !exactText(receipt.activatedAt))
173
+ return false;
174
+ if (receipt.status === 'activated') {
175
+ return receipt.agent === null
176
+ && receipt.afterFingerprint === null
177
+ && receipt.changedPaths.length === 0
178
+ && receipt.sealedAt === null
179
+ && receipt.consumedAt === null;
180
+ }
181
+ if (!exactText(receipt.agent)
182
+ || normalizeAgentToken(receipt.agent) !== normalizeAgentToken(receipt.client))
183
+ return false;
184
+ if (receipt.status === 'stage-completed') {
185
+ return receipt.afterFingerprint === null
186
+ && receipt.changedPaths.length === 0
187
+ && receipt.sealedAt === null
188
+ && receipt.consumedAt === null;
189
+ }
190
+ if (!exactText(receipt.afterFingerprint) || !exactText(receipt.sealedAt))
191
+ return false;
192
+ return receipt.status === 'sealed'
193
+ ? receipt.consumedAt === null
194
+ : receipt.status === 'consumed' && exactText(receipt.consumedAt);
195
+ }
196
+ function isDelegationReceipt(value) {
197
+ if (!hasExactKeys(value, RECEIPT_KEYS))
198
+ return false;
199
+ const structurallyValid = exactText(value.id)
200
+ && exactText(value.taskId)
201
+ && exactText(value.runId)
202
+ && ['executor', 'reviewer'].includes(value.role)
203
+ && ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code', 'commit'].includes(value.stage)
204
+ && Number.isSafeInteger(value.round)
205
+ && value.round > 0
206
+ && exactText(value.artifact)
207
+ && isAgentClientId(value.client)
208
+ && exactText(value.requestedModel)
209
+ && exactText(value.requestedReasoningEffort)
210
+ && nullableText(value.actualModel)
211
+ && nullableText(value.actualReasoningEffort)
212
+ && nullableText(value.modelFallbackReason)
213
+ && nullableText(value.reasoningEffortFallbackReason)
214
+ && nullableText(value.parentId)
215
+ && nullableText(value.childId)
216
+ && nullableText(value.spawnMode)
217
+ && nullableText(value.agent)
218
+ && ['prepared', 'activated', 'stage-completed', 'sealed', 'consumed', 'aborted', 'expired'].includes(value.status)
219
+ && value.workspaceSnapshotScope === 'task'
220
+ && (value.lifecycleProvenance === null || isDelegationLifecycleProvenance(value.lifecycleProvenance))
221
+ && (value.hostEvidence === null || isDelegationHostEvidence(value.hostEvidence))
222
+ && exactText(value.beforeFingerprint)
223
+ && nullableText(value.afterFingerprint)
224
+ && Array.isArray(value.changedPaths)
225
+ && value.changedPaths.every(exactText)
226
+ && exactText(value.createdAt)
227
+ && nullableSafeInteger(value.preparedMonotonicMs)
228
+ && value.preparedMonotonicMs !== null
229
+ && nullableSafeInteger(value.spawnDispatchMonotonicMs)
230
+ && nullableSafeInteger(value.activationDeadlineMonotonicMs)
231
+ && nullableText(value.spawnDispatchedAt)
232
+ && nullableText(value.activationDeadlineAt)
233
+ && nullableSafeInteger(value.startEvidenceMonotonicMs)
234
+ && nullableSafeInteger(value.activatedMonotonicMs)
235
+ && nullableText(value.activatedAt)
236
+ && nullableText(value.sealedAt)
237
+ && nullableText(value.consumedAt);
238
+ if (!structurallyValid)
239
+ return false;
240
+ const receipt = value;
241
+ return hasStatusBoundEvidence(receipt)
242
+ && (receipt.client !== 'codex' || hasCurrentCodexEvidence(receipt));
243
+ }
3
244
  const MANAGED_AGENTS = {
4
245
  'agent-infra-lifecycle-executor': 'executor',
5
246
  'agent-infra-lifecycle-reviewer': 'reviewer'
@@ -53,7 +294,7 @@ function dispatchDelegation(receipt, options = {}) {
53
294
  }
54
295
  const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))();
55
296
  const dispatchedAt = (options.now ?? (() => new Date().toISOString()))();
56
- const window = options.activationWindowMs ?? 15_000;
297
+ const window = options.activationWindowMs ?? 60_000;
57
298
  return { ok: true, receipt: Object.freeze({
58
299
  ...receipt,
59
300
  spawnDispatchMonotonicMs: monotonic,
@@ -252,5 +493,5 @@ function consumeDelegation(receipt, options = {}) {
252
493
  consumedAt: (options.now ?? (() => new Date().toISOString()))()
253
494
  }) };
254
495
  }
255
- export { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, managedDelegationRole, prepareDelegation, sealDelegation };
496
+ export { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation };
256
497
  //# sourceMappingURL=delegation-receipts.js.map