@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.
Files changed (182) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/bin/internal-cli.js +11 -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 +25 -11
  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 +127 -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 +63 -92
  10. package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
  11. package/dist/lib/agent-clients/adapters/index.js +3 -1
  12. package/dist/lib/agent-clients/adapters/traecli.js +66 -0
  13. package/dist/lib/agent-clients/tokens.js +2 -2
  14. package/dist/lib/agent-clients/types.js +2 -1
  15. package/dist/lib/defaults.json +5 -0
  16. package/dist/lib/git/workflow.js +10 -1
  17. package/dist/lib/internal/codex-lifecycle.js +20 -6
  18. package/dist/lib/internal/codex-sandbox-controller.js +9 -15
  19. package/dist/lib/internal/git-workflow.js +5 -7
  20. package/dist/lib/internal/platform-pr.js +20 -7
  21. package/dist/lib/internal/release-workflow.js +2 -1
  22. package/dist/lib/internal/sandbox-control.js +79 -2
  23. package/dist/lib/internal/task-finalization.js +54 -0
  24. package/dist/lib/internal/task-orchestration.js +156 -291
  25. package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +75 -42
  26. package/dist/lib/platform/pr-summary.js +48 -10
  27. package/dist/lib/platform/pull-requests.js +205 -41
  28. package/dist/lib/run/tui.js +5 -2
  29. package/dist/lib/sandbox/commands/rm.js +316 -55
  30. package/dist/lib/sandbox/control/client.js +190 -6
  31. package/dist/lib/sandbox/control/controller-registration.js +275 -0
  32. package/dist/lib/sandbox/control/executor.js +140 -5
  33. package/dist/lib/sandbox/control/lifecycle.js +3 -1
  34. package/dist/lib/sandbox/control/protocol.js +67 -6
  35. package/dist/lib/sandbox/index.js +5 -3
  36. package/dist/lib/sandbox/managed-fs.js +5 -0
  37. package/dist/lib/sandbox/task-resolver.js +50 -6
  38. package/dist/lib/sandbox/workspace-identity.js +27 -0
  39. package/dist/lib/sandbox/worktree-safety.js +215 -2
  40. package/dist/lib/task/codex-orchestration.js +161 -39
  41. package/dist/lib/task/commands/status.js +59 -1
  42. package/dist/lib/task/commit-operation.js +335 -0
  43. package/dist/lib/task/commit-policy.js +18 -0
  44. package/dist/lib/task/delegation-receipts.js +248 -6
  45. package/dist/lib/task/finalization.js +593 -0
  46. package/dist/lib/task/index.js +0 -9
  47. package/dist/lib/task/operation-outcome.js +33 -0
  48. package/dist/lib/task/orchestration.js +187 -761
  49. package/dist/lib/task/short-id.js +59 -1
  50. package/dist/lib/task/task-execution-lock.js +6 -1
  51. package/dist/lib/task/verification-engine.js +110 -60
  52. package/dist/lib/task/verification.js +2 -1
  53. package/dist/lib/task/workflow-warning-intents.js +50 -1
  54. package/dist/lifecycle-build-manifest.json +3 -3
  55. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +61 -19
  56. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +37 -10
  57. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +276 -12
  58. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +163 -0
  59. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
  60. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
  61. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +91 -118
  62. package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
  63. package/lib/agent-clients/adapters/index.ts +3 -1
  64. package/lib/agent-clients/adapters/traecli.ts +67 -0
  65. package/lib/agent-clients/tokens.ts +2 -2
  66. package/lib/agent-clients/types.ts +2 -1
  67. package/lib/defaults.json +5 -0
  68. package/lib/git/workflow.ts +11 -2
  69. package/lib/internal/codex-lifecycle.ts +19 -6
  70. package/lib/internal/codex-sandbox-controller.ts +9 -14
  71. package/lib/internal/git-workflow.ts +5 -6
  72. package/lib/internal/platform-pr.ts +14 -7
  73. package/lib/internal/release-workflow.ts +2 -1
  74. package/lib/internal/sandbox-control.ts +92 -2
  75. package/lib/internal/task-finalization.ts +68 -0
  76. package/lib/internal/task-orchestration.ts +20 -103
  77. package/lib/{task/commands/validate.ts → internal/task-validate.ts} +75 -40
  78. package/lib/platform/pr-summary.ts +55 -11
  79. package/lib/platform/pull-requests.ts +211 -41
  80. package/lib/run/tui.ts +5 -3
  81. package/lib/sandbox/commands/rm.ts +413 -61
  82. package/lib/sandbox/control/client.ts +255 -6
  83. package/lib/sandbox/control/controller-registration.ts +352 -0
  84. package/lib/sandbox/control/executor.ts +169 -5
  85. package/lib/sandbox/control/lifecycle.ts +2 -1
  86. package/lib/sandbox/control/protocol.ts +80 -7
  87. package/lib/sandbox/index.ts +5 -3
  88. package/lib/sandbox/managed-fs.ts +5 -0
  89. package/lib/sandbox/task-resolver.ts +58 -7
  90. package/lib/sandbox/workspace-identity.ts +50 -2
  91. package/lib/sandbox/workspace-view.ts +3 -3
  92. package/lib/sandbox/worktree-safety.ts +236 -2
  93. package/lib/task/codex-orchestration.ts +168 -40
  94. package/lib/task/commands/status.ts +78 -1
  95. package/lib/task/commit-operation.ts +430 -0
  96. package/lib/task/commit-policy.ts +30 -0
  97. package/lib/task/delegation-receipts.ts +263 -4
  98. package/lib/task/finalization.ts +761 -0
  99. package/lib/task/index.ts +0 -8
  100. package/lib/task/operation-outcome.ts +48 -0
  101. package/lib/task/orchestration.ts +218 -982
  102. package/lib/task/short-id.ts +65 -1
  103. package/lib/task/task-execution-lock.ts +10 -1
  104. package/lib/task/verification-engine.ts +115 -63
  105. package/lib/task/verification.ts +3 -2
  106. package/lib/task/workflow-warning-intents.ts +57 -2
  107. package/package.json +2 -2
  108. package/templates/.agents/QUICKSTART.en.md +3 -4
  109. package/templates/.agents/QUICKSTART.zh-CN.md +3 -4
  110. package/templates/.agents/hooks/lifecycle-delegation.js +1 -21
  111. package/templates/.agents/rules/README.en.md +1 -0
  112. package/templates/.agents/rules/README.zh-CN.md +1 -0
  113. package/templates/.agents/rules/compatibility-policy.en.md +39 -0
  114. package/templates/.agents/rules/compatibility-policy.zh-CN.md +39 -0
  115. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
  116. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
  117. package/templates/.agents/rules/pr-sync.en.md +1 -1
  118. package/templates/.agents/rules/pr-sync.github.en.md +2 -1
  119. package/templates/.agents/rules/pr-sync.github.zh-CN.md +2 -0
  120. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -0
  121. package/templates/.agents/rules/review-method.en.md +1 -0
  122. package/templates/.agents/rules/review-method.zh-CN.md +1 -0
  123. package/templates/.agents/rules/task-management.en.md +2 -2
  124. package/templates/.agents/rules/task-management.zh-CN.md +2 -2
  125. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -0
  126. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -0
  127. package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
  128. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
  129. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  130. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  131. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
  132. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
  133. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
  134. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
  135. package/templates/.agents/skills/code-task/SKILL.en.md +1 -0
  136. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -0
  137. package/templates/.agents/skills/commit/SKILL.en.md +41 -125
  138. package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
  139. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  140. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  141. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
  142. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
  143. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  144. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  145. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
  146. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
  147. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
  148. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
  149. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  150. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  151. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  152. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  153. package/templates/.agents/skills/create-pr/SKILL.en.md +17 -9
  154. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +17 -9
  155. package/templates/.agents/skills/create-pr/reference/change-report.en.md +41 -0
  156. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +41 -0
  157. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  158. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  159. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -1
  160. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -1
  161. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +8 -6
  162. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +8 -6
  163. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +3 -3
  164. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +3 -3
  165. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
  166. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
  167. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
  168. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
  169. package/templates/.agents/skills/run-task/SKILL.en.md +6 -6
  170. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -6
  171. package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
  172. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
  173. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +24 -0
  174. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
  175. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
  176. package/templates/.codex/hooks.json +4 -4
  177. package/templates/.trae/skills/update-agent-infra.en.md +8 -0
  178. package/templates/.trae/skills/update-agent-infra.zh-CN.md +8 -0
  179. package/dist/lib/task/commit-finalization.js +0 -305
  180. package/dist/lib/task/commit-intent.js +0 -165
  181. package/lib/task/commit-finalization.ts +0 -419
  182. package/lib/task/commit-intent.ts +0 -230
@@ -5,25 +5,11 @@ import os from 'node:os';
5
5
  import path from 'node:path';
6
6
  import semver from 'semver';
7
7
  import * as toml from 'smol-toml';
8
- import { requestSandboxControl } from '../../../sandbox/control/client.js';
8
+ import { requestCodexControllerClose, requestCodexControllerOpen, requestCodexControllerVerify, requestSandboxControl } from '../../../sandbox/control/client.js';
9
9
  import { readSandboxControlStatus } from '../../../sandbox/control/state.js';
10
- import { getProcessStartTime, isProcessAlive } from '../../../server/process-state.js';
11
- import { computeLifecycleBuildIdentity } from './build-identity.js';
12
- const CONTEXT_TTL_MS = 4 * 60 * 60 * 1_000;
13
- function digestFiles(files) {
14
- const hash = crypto.createHash('sha256');
15
- for (const file of [...files].sort()) {
16
- const stat = fs.lstatSync(file);
17
- if (!stat.isFile() || stat.isSymbolicLink()) {
18
- throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
19
- }
20
- hash.update(path.basename(file));
21
- hash.update('\0');
22
- hash.update(fs.readFileSync(file));
23
- hash.update('\0');
24
- }
25
- return hash.digest('hex');
26
- }
10
+ import { getProcessStartTime } from '../../../server/process-state.js';
11
+ import { LIFECYCLE_PROTOCOL_VERSION } from './build-identity.js';
12
+ import { contextFromControllerLease, controllerProofFromContext, verifyCodexSandboxControllerContextWithWarnings as verifyContextFileWithWarnings, writeCodexSandboxControllerContext } from './controller-context.js';
27
13
  function copyRegular(source, destination, mode) {
28
14
  const stat = fs.lstatSync(source);
29
15
  if (!stat.isFile() || stat.isSymbolicLink()) {
@@ -108,6 +94,30 @@ function verifyControl(taskId, control) {
108
94
  if (payload.taskId !== taskId)
109
95
  throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_BINDING_INVALID');
110
96
  }
97
+ function verifyCodexSandboxControllerContextWithWarnings(contextPath, options = {}) {
98
+ const control = options.control ?? controlFromEnvironment();
99
+ const fileVerification = verifyContextFileWithWarnings(contextPath, {
100
+ repoRoot: options.repoRoot,
101
+ now: options.now,
102
+ generation: control.generation,
103
+ probeProcess: options.probeProcess
104
+ });
105
+ const context = fileVerification.context;
106
+ const verified = (options.requestControllerVerify ?? requestCodexControllerVerify)({
107
+ controllerProof: controllerProofFromContext(context),
108
+ ...control,
109
+ timeoutMs: 30_000
110
+ });
111
+ if (verified.binding.taskId !== context.taskId
112
+ || verified.binding.controlGeneration !== context.controlGeneration
113
+ || verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
114
+ throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
115
+ }
116
+ return Object.freeze({
117
+ context,
118
+ warnings: fileVerification.warnings
119
+ });
120
+ }
111
121
  function detectedCodexVersion() {
112
122
  const result = spawnSync('codex', ['--version'], { encoding: 'utf8' });
113
123
  const version = /codex-cli\s+(\d+\.\d+\.\d+)/u.exec(result.stdout ?? '')?.[1];
@@ -191,7 +201,6 @@ function prepareCodexSandboxController(input, options = {}) {
191
201
  const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
192
202
  const sourceHome = path.resolve(options.codexHome ?? process.env.CODEX_HOME ?? path.join(os.homedir(), '.codex'));
193
203
  const control = options.control ?? controlFromEnvironment();
194
- (options.verifyTaskBinding ?? verifyControl)(input.taskId, control);
195
204
  const version = (options.codexVersion ?? detectedCodexVersion)();
196
205
  if (!semver.gte(version, '0.147.0'))
197
206
  throw new Error('CODEX_SANDBOX_CONTROLLER_CODEX_UNSUPPORTED');
@@ -200,48 +209,36 @@ function prepareCodexSandboxController(input, options = {}) {
200
209
  fs.mkdirSync(runtimeRoot, { recursive: true, mode: 0o700 });
201
210
  fs.chmodSync(runtimeRoot, 0o700);
202
211
  const key = crypto.createHash('sha256')
203
- .update(`${input.taskId}\0${control.generation}`)
212
+ .update(`${process.pid}\0${control.generation}`)
204
213
  .digest('hex');
205
- const leasePath = path.join(runtimeRoot, `${key}.lease.json`);
206
- if (fs.existsSync(leasePath)) {
207
- const lease = JSON.parse(fs.readFileSync(leasePath, 'utf8'));
208
- if (Number.isSafeInteger(lease.pid) && typeof lease.startTime === 'number'
209
- && getProcessStartTime(lease.pid) === lease.startTime) {
210
- throw new Error('CODEX_SANDBOX_CONTROLLER_BUSY');
211
- }
212
- fs.unlinkSync(leasePath);
213
- }
214
214
  const parentStartTime = getProcessStartTime(process.pid);
215
215
  if (!parentStartTime)
216
216
  throw new Error('CODEX_SANDBOX_CONTROLLER_PROCESS_IDENTITY_INVALID');
217
- const instanceDigest = crypto.randomBytes(32).toString('hex');
218
- fs.writeFileSync(leasePath, `${JSON.stringify({
219
- version: 1,
220
- pid: process.pid,
221
- startTime: parentStartTime,
222
- taskId: input.taskId,
223
- generation: control.generation,
224
- instanceDigest
225
- })}\n`, { mode: 0o600, flag: 'wx' });
226
217
  const home = fs.mkdtempSync(path.join(runtimeRoot, `${key}-`));
227
218
  fs.chmodSync(home, 0o700);
228
219
  let cleaned = false;
220
+ let context = null;
229
221
  const cleanup = () => {
230
222
  if (cleaned)
231
223
  return;
232
224
  cleaned = true;
225
+ if (context) {
226
+ try {
227
+ (options.closeController ?? requestCodexControllerClose)({
228
+ controllerProcess: context.controllerProcess,
229
+ controllerProof: controllerProofFromContext(context),
230
+ ...control,
231
+ timeoutMs: 30_000
232
+ });
233
+ }
234
+ catch {
235
+ // The wrapper is exiting; dead-process recovery safely handles an unknown close result.
236
+ }
237
+ }
233
238
  const relative = path.relative(runtimeRoot, home);
234
239
  if (!relative.startsWith('..') && !path.isAbsolute(relative)) {
235
240
  fs.rmSync(home, { recursive: true, force: true });
236
241
  }
237
- try {
238
- const lease = JSON.parse(fs.readFileSync(leasePath, 'utf8'));
239
- if (lease.instanceDigest === instanceDigest)
240
- fs.unlinkSync(leasePath);
241
- }
242
- catch {
243
- // A missing or replaced lease is owned by another cleanup path.
244
- }
245
242
  };
246
243
  try {
247
244
  const auth = path.join(sourceHome, 'auth.json');
@@ -254,31 +251,26 @@ function prepareCodexSandboxController(input, options = {}) {
254
251
  copyRegular(hooks, path.join(home, 'hooks.json'), 0o600);
255
252
  copyRegular(executor, path.join(home, 'agents', path.basename(executor)), 0o600);
256
253
  copyRegular(reviewer, path.join(home, 'agents', path.basename(reviewer)), 0o600);
257
- if (crypto.createHash('sha256').update(fs.readFileSync(path.join(home, 'hooks.json'))).digest('hex')
258
- !== crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
259
- || digestFiles([
260
- path.join(home, 'agents', path.basename(executor)),
261
- path.join(home, 'agents', path.basename(reviewer))
262
- ]) !== digestFiles([executor, reviewer])) {
263
- throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
254
+ const opened = (options.openController ?? requestCodexControllerOpen)({
255
+ controllerProcess: { pid: process.pid, startTime: parentStartTime },
256
+ ...control,
257
+ timeoutMs: 30_000
258
+ });
259
+ const taskId = opened.lease.taskId;
260
+ if (opened.lease.buildIdentity.protocolVersion !== LIFECYCLE_PROTOCOL_VERSION) {
261
+ throw new Error('CODEX_LIFECYCLE_PROTOCOL_MISMATCH: Codex lifecycle protocol version does not match');
264
262
  }
265
- const buildIdentity = computeLifecycleBuildIdentity(repoRoot);
266
- const now = (options.now ?? Date.now)();
267
- const context = Object.freeze({
268
- version: 1,
269
- taskId: input.taskId,
270
- controlGeneration: control.generation,
271
- controllerInstanceDigest: instanceDigest,
272
- parentPid: process.pid,
273
- parentStartTime,
274
- issuedAt: now,
275
- expiresAt: now + CONTEXT_TTL_MS,
276
- buildIdentity,
277
- hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex'),
278
- lifecycleProfilesHash: digestFiles([executor, reviewer])
263
+ context = contextFromControllerLease(opened.lease, {
264
+ hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
279
265
  });
266
+ if (opened.lease.controlGeneration !== control.generation
267
+ || opened.lease.controllerProcess.pid !== process.pid
268
+ || opened.lease.controllerProcess.startTime !== parentStartTime) {
269
+ throw new Error('SANDBOX_CONTROL_RESULT_INVALID');
270
+ }
271
+ (options.verifyTaskBinding ?? verifyControl)(taskId, control);
280
272
  const contextPath = path.join(home, 'controller-context.json');
281
- fs.writeFileSync(contextPath, `${JSON.stringify(context, null, 2)}\n`, { mode: 0o600 });
273
+ writeCodexSandboxControllerContext(contextPath, context);
282
274
  const shimDir = path.join(home, 'bin');
283
275
  fs.mkdirSync(shimDir, { mode: 0o700 });
284
276
  const internalCli = path.resolve(process.argv[1] ?? path.join(repoRoot, 'bin', 'internal-cli.ts'));
@@ -292,7 +284,7 @@ function prepareCodexSandboxController(input, options = {}) {
292
284
  input.reviewerModel ? `--reviewer-model ${input.reviewerModel}` : '',
293
285
  input.reviewerReasoningEffort ? `--reviewer-reasoning-effort ${input.reviewerReasoningEffort}` : ''
294
286
  ].filter(Boolean).join(' ');
295
- const prompt = `$run-task ${input.taskRef}${policy ? ` ${policy}` : ''}`;
287
+ const prompt = `$run-task ${taskId}${policy ? ` ${policy}` : ''}`;
296
288
  const args = Object.freeze([
297
289
  'exec',
298
290
  '--enable', 'hooks',
@@ -303,7 +295,7 @@ function prepareCodexSandboxController(input, options = {}) {
303
295
  '-C', repoRoot,
304
296
  prompt
305
297
  ]);
306
- const env = isolatedEnvironment(home, shimDir, contextPath, input.taskId, control, providerEnvironment, options.environment ?? process.env);
298
+ const env = isolatedEnvironment(home, shimDir, contextPath, taskId, control, providerEnvironment, options.environment ?? process.env);
307
299
  return Object.freeze({
308
300
  command: 'codex',
309
301
  args,
@@ -319,27 +311,6 @@ function prepareCodexSandboxController(input, options = {}) {
319
311
  throw error;
320
312
  }
321
313
  }
322
- function verifyCodexSandboxControllerContext(contextPath, taskId, options = {}) {
323
- const stat = fs.lstatSync(contextPath);
324
- if (!stat.isFile() || stat.isSymbolicLink() || (stat.mode & 0o077) !== 0) {
325
- throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
326
- }
327
- const context = JSON.parse(fs.readFileSync(contextPath, 'utf8'));
328
- const generation = options.generation ?? process.env.AGENT_INFRA_CONTROL_GENERATION;
329
- if (context.version !== 1
330
- || context.taskId !== taskId
331
- || context.controlGeneration !== generation
332
- || context.expiresAt < (options.now ?? Date.now())
333
- || getProcessStartTime(context.parentPid) !== context.parentStartTime
334
- || !isProcessAlive(context.parentPid)) {
335
- throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
336
- }
337
- const current = computeLifecycleBuildIdentity(options.repoRoot ?? process.cwd());
338
- if (JSON.stringify(current) !== JSON.stringify(context.buildIdentity)) {
339
- throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
340
- }
341
- return Object.freeze(context);
342
- }
343
314
  async function runCodexSandboxController(input, options = {}) {
344
315
  const prepared = prepareCodexSandboxController(input, options);
345
316
  try {
@@ -394,5 +365,5 @@ async function runCodexSandboxController(input, options = {}) {
394
365
  prepared.cleanup();
395
366
  }
396
367
  }
397
- export { prepareCodexSandboxController, runCodexSandboxController, verifyCodexSandboxControllerContext };
368
+ export { prepareCodexSandboxController, runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings, controllerProofFromContext };
398
369
  //# sourceMappingURL=sandbox-controller.js.map
@@ -30,6 +30,8 @@ function resolveHostCatalogPath(value, hostHomeDir) {
30
30
  }
31
31
  }
32
32
  const CODEX_DISABLED_FEATURE_FLAGS = ['apps', 'enable_mcp_apps'];
33
+ const SANDBOX_CODEX_HOOKS_PATH = '/workspace/.codex/hooks.json';
34
+ const CODEX_HOOK_TRUST_HASH = /^sha256:[a-f0-9]{64}$/;
33
35
  function removeCodexMcpServers(sandboxParsed) {
34
36
  if (!Object.hasOwn(sandboxParsed, 'mcp_servers')) {
35
37
  return false;
@@ -61,19 +63,58 @@ function inheritDisabledCodexFeatureFlags(sandboxParsed, hostParsed) {
61
63
  }
62
64
  return changed;
63
65
  }
64
- function ensureCodexModelInheritance(toolDir, hostHomeDir, containerCodexDir = '/home/devuser/.codex') {
65
- if (!hostHomeDir) {
66
- return;
66
+ function inheritCodexHookTrustState(sandboxParsed, hostParsed, hostProjectDir) {
67
+ if (!hostProjectDir) {
68
+ return false;
67
69
  }
68
- const hostConfigPath = path.join(hostHomeDir, '.codex', 'config.toml');
69
- if (!fs.existsSync(hostConfigPath)) {
70
- return;
70
+ if (Object.hasOwn(sandboxParsed, 'hooks') && !isJsonObjectRecord(sandboxParsed.hooks)) {
71
+ return false;
71
72
  }
72
- let hostParsed;
73
- try {
74
- hostParsed = toml.parse(fs.readFileSync(hostConfigPath, 'utf8'));
73
+ const sandboxHooks = sandboxParsed.hooks ?? {};
74
+ if (Object.hasOwn(sandboxHooks, 'state') && !isJsonObjectRecord(sandboxHooks.state)) {
75
+ return false;
75
76
  }
76
- catch {
77
+ const sandboxState = sandboxHooks.state ?? {};
78
+ const sandboxPrefix = `${SANDBOX_CODEX_HOOKS_PATH}:`;
79
+ const nextState = Object.fromEntries(Object.entries(sandboxState).filter(([key]) => !key.startsWith(sandboxPrefix)));
80
+ const hostState = isJsonObjectRecord(hostParsed.hooks)
81
+ && isJsonObjectRecord(hostParsed.hooks.state)
82
+ ? hostParsed.hooks.state
83
+ : {};
84
+ const hostPrefix = `${path.join(hostProjectDir, '.codex', 'hooks.json')}:`;
85
+ for (const [key, value] of Object.entries(hostState)) {
86
+ if (!key.startsWith(hostPrefix) || !isJsonObjectRecord(value)) {
87
+ continue;
88
+ }
89
+ const trustedHash = value.trusted_hash;
90
+ if (typeof trustedHash !== 'string' || !CODEX_HOOK_TRUST_HASH.test(trustedHash)) {
91
+ continue;
92
+ }
93
+ nextState[`${sandboxPrefix}${key.slice(hostPrefix.length)}`] = {
94
+ trusted_hash: trustedHash,
95
+ ...(typeof value.enabled === 'boolean' ? { enabled: value.enabled } : {})
96
+ };
97
+ }
98
+ if (JSON.stringify(sandboxState) === JSON.stringify(nextState)) {
99
+ return false;
100
+ }
101
+ if (Object.keys(nextState).length > 0) {
102
+ sandboxHooks.state = nextState;
103
+ sandboxParsed.hooks = sandboxHooks;
104
+ }
105
+ else {
106
+ delete sandboxHooks.state;
107
+ if (Object.keys(sandboxHooks).length > 0) {
108
+ sandboxParsed.hooks = sandboxHooks;
109
+ }
110
+ else {
111
+ delete sandboxParsed.hooks;
112
+ }
113
+ }
114
+ return true;
115
+ }
116
+ function ensureCodexModelInheritance(toolDir, hostHomeDir, containerCodexDir = '/home/devuser/.codex', hostProjectDir) {
117
+ if (!hostHomeDir) {
77
118
  return;
78
119
  }
79
120
  const sandboxConfigPath = path.join(toolDir, 'config.toml');
@@ -87,8 +128,25 @@ function ensureCodexModelInheritance(toolDir, hostHomeDir, containerCodexDir = '
87
128
  return;
88
129
  }
89
130
  }
131
+ const hostConfigPath = path.join(hostHomeDir, '.codex', 'config.toml');
132
+ let hostParsed = null;
133
+ if (fs.existsSync(hostConfigPath)) {
134
+ try {
135
+ hostParsed = toml.parse(fs.readFileSync(hostConfigPath, 'utf8'));
136
+ }
137
+ catch {
138
+ // An unavailable host trust source must not leave previously projected trust active.
139
+ }
140
+ }
141
+ if (!hostParsed) {
142
+ if (inheritCodexHookTrustState(sandboxParsed, {}, hostProjectDir)) {
143
+ fs.writeFileSync(sandboxConfigPath, `${toml.stringify(sandboxParsed)}\n`, 'utf8');
144
+ }
145
+ return;
146
+ }
90
147
  let changed = removeCodexMcpServers(sandboxParsed);
91
148
  changed = inheritDisabledCodexFeatureFlags(sandboxParsed, hostParsed) || changed;
149
+ changed = inheritCodexHookTrustState(sandboxParsed, hostParsed, hostProjectDir) || changed;
92
150
  const inheritSpecs = [
93
151
  ['model', 'string'],
94
152
  ['model_reasoning_effort', 'string'],
@@ -151,7 +209,10 @@ const codexBeforeContainerCreateHook = {
151
209
  message: 'Codex sandbox hook requires its resolved tool state.'
152
210
  };
153
211
  }
154
- ensureCodexModelInheritance(entry.dir, create.hostHome, entry.tool.containerMount);
212
+ const hostProjectDir = typeof context.config?.repoRoot === 'string'
213
+ ? context.config.repoRoot
214
+ : undefined;
215
+ ensureCodexModelInheritance(entry.dir, create.hostHome, entry.tool.containerMount, hostProjectDir);
155
216
  ensureCodexWorkspaceTrust(entry.dir);
156
217
  return { status: 'ready' };
157
218
  }
@@ -3,11 +3,13 @@ import { claudeCodeAdapter } from './claude-code.js';
3
3
  import { codexAdapter } from './codex.js';
4
4
  import { antigravityCliAdapter } from './antigravity-cli.js';
5
5
  import { opencodeAdapter } from './opencode.js';
6
+ import { traecliAdapter } from './traecli.js';
6
7
  const adaptersById = new Map([
7
8
  claudeCodeAdapter,
8
9
  codexAdapter,
9
10
  antigravityCliAdapter,
10
- opencodeAdapter
11
+ opencodeAdapter,
12
+ traecliAdapter
11
13
  ].map((adapter) => [adapter.id, adapter]));
12
14
  const BUILTIN_AGENT_CLIENT_ADAPTERS = Object.freeze(AGENT_CLIENT_IDS.map((id) => {
13
15
  const adapter = adaptersById.get(id);
@@ -0,0 +1,66 @@
1
+ import { defineAgentClientAdapter } from '../adapter.js';
2
+ import { hostJoin } from '../../sandbox/engines/wsl2-paths.js';
3
+ const traecliAdapter = defineAgentClientAdapter({
4
+ id: 'traecli',
5
+ displayName: 'TraeCode CLI',
6
+ invocation: '/${skillName}',
7
+ capabilities: {
8
+ instructions: { level: 'compatible' },
9
+ skills: { level: 'compatible' },
10
+ commands: { level: 'integrated' },
11
+ hooks: { level: 'compatible' },
12
+ subagents: { level: 'experimental' },
13
+ orchestration: { level: 'experimental' },
14
+ sandbox: { level: 'integrated' },
15
+ verification: { level: 'compatible' }
16
+ },
17
+ modelSelection: {
18
+ kind: 'interactive-only',
19
+ command: '/model',
20
+ guidance: 'Use the host model picker for the complete model and reasoning-effort catalog.'
21
+ },
22
+ delegationEvidence: {
23
+ actualModel: 'unavailable',
24
+ actualReasoningEffort: 'unavailable'
25
+ },
26
+ project: {
27
+ ownedPathPrefixes: ['.trae/'],
28
+ managed: ['.trae/skills/'],
29
+ merged: [],
30
+ ejected: [],
31
+ seedCommands: [{
32
+ templates: {
33
+ en: '.trae/skills/update-agent-infra.en.md',
34
+ 'zh-CN': '.trae/skills/update-agent-infra.zh-CN.md'
35
+ },
36
+ target: '.trae/skills/update-agent-infra.md'
37
+ }],
38
+ customCommand: {
39
+ target: '.trae/skills/${skillName}.md',
40
+ frontmatter: {},
41
+ includeUsage: true,
42
+ inheritDisableModelInvocation: true
43
+ }
44
+ },
45
+ sandbox: {
46
+ createTool: ({ home }) => ({
47
+ id: 'traecli',
48
+ name: 'TraeCode CLI',
49
+ install: {
50
+ type: 'shell',
51
+ cmd: 'curl -fsSL https://trae.cn/trae-cli/install_v2.sh | bash'
52
+ },
53
+ sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'traecli'),
54
+ containerMount: '/home/devuser/.trae',
55
+ versionCmd: 'traecli --version',
56
+ setupHint: 'Run traecli login inside the container to authenticate.'
57
+ }),
58
+ aliases: [
59
+ { name: 'traecli-yolo', command: 'traecli --dangerously-bypass-approvals-and-sandbox; tput ed' },
60
+ { name: 'ty', command: 'traecli --dangerously-bypass-approvals-and-sandbox; tput ed' }
61
+ ],
62
+ hooks: []
63
+ }
64
+ });
65
+ export { traecliAdapter };
66
+ //# sourceMappingURL=traecli.js.map
@@ -2,7 +2,7 @@
2
2
  // activity log. Migrated from lib/task/commands/log.ts so that the write-side
3
3
  // internal commands and the rendering side share one definition. Historical
4
4
  // Gemini tokens remain read-only and are handled by classifyAgent below.
5
- export const KNOWN_AI_AGENTS = new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor']);
5
+ export const KNOWN_AI_AGENTS = new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor', 'traecli']);
6
6
  // Long name -> short name mapping (HD-4, decision A): `claude-code` reuses the
7
7
  // existing orchestration precedent, and `antigravity-cli` is the active
8
8
  // client. `gemini-cli` is intentionally not writable.
@@ -10,7 +10,7 @@ export const AGENT_LONG_NAMES = {
10
10
  'claude-code': 'claude',
11
11
  'antigravity-cli': 'antigravity'
12
12
  };
13
- export const AGENT_USAGE_HINT = "agent must be a short token (claude/codex/antigravity/opencode/cursor), " +
13
+ export const AGENT_USAGE_HINT = "agent must be a short token (claude/codex/antigravity/opencode/cursor/traecli), " +
14
14
  "a long name (claude-code -> claude, antigravity-cli -> antigravity), or 'human'";
15
15
  // Read-side historical compatibility for activity-log rendering: `gemini` and
16
16
  // `gemini-cli` stay recognizable when displaying old logs, but are never
@@ -2,7 +2,8 @@ const AGENT_CLIENT_IDS = [
2
2
  'claude-code',
3
3
  'codex',
4
4
  'antigravity-cli',
5
- 'opencode'
5
+ 'opencode',
6
+ 'traecli'
6
7
  ];
7
8
  const AGENT_CLIENT_CAPABILITY_IDS = [
8
9
  'instructions',
@@ -22,6 +22,11 @@
22
22
  "id": "opencode",
23
23
  "enabled": true,
24
24
  "installInSandbox": true
25
+ },
26
+ {
27
+ "id": "traecli",
28
+ "enabled": true,
29
+ "installInSandbox": true
25
30
  }
26
31
  ],
27
32
  "sandbox": {
@@ -124,7 +124,16 @@ function pushGitRefs(input, runner = defaultRunner) {
124
124
  }
125
125
  const canonical = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`;
126
126
  const remoteFact = run(runner, input.cwd, ['ls-remote', '--exit-code', input.remote, canonical]);
127
- operations.push({ name: 'push', ref, status: remoteFact.status === 0 ? 'applied' : 'failed', message: remoteFact.status === 0 ? undefined : 'Remote ref verification failed' });
127
+ const remoteSha = remoteFact.status === 0 ? remoteFact.stdout.trim().split(/\s+/)[0] ?? null : null;
128
+ const verified = remoteFact.status === 0 && (!input.expectedSha || remoteSha === input.expectedSha);
129
+ operations.push({
130
+ name: 'push',
131
+ ref,
132
+ status: verified ? 'applied' : 'failed',
133
+ message: verified ? undefined : input.expectedSha
134
+ ? `Remote ref verification failed: expected ${input.expectedSha}, received ${remoteSha ?? 'unavailable'}`
135
+ : 'Remote ref verification failed'
136
+ });
128
137
  }
129
138
  const failures = operations.filter((item) => item.status === 'failed').length;
130
139
  return { status: failures === 0 ? 'applied' : failures === operations.length ? 'failed' : 'degraded', changed: failures < operations.length, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations, error: failures ? { code: 'GIT_PUSH_PARTIAL', message: `${failures} ref push operation(s) failed` } : null };
@@ -6,8 +6,9 @@ import { preflightCodexLifecycleEvidence, resolveCodexTerminal, resolveCodexThre
6
6
  import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.js';
7
7
  import { createCodexCapabilityStore } from '../agent-clients/adapters/codex-lifecycle/capability-store.js';
8
8
  import { computeLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.js';
9
- import { verifyCodexSandboxControllerContext } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
9
+ import { verifyCodexSandboxControllerContextWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
10
10
  import { resolveTaskRef } from '../task/resolve-ref.js';
11
+ import { hasSealableOrchestrationDelegation } from '../task/orchestration.js';
11
12
  import { activateCodexOrchestrationDelegation, activateCodexSpawnDelegation, reconcileCodexOrchestrationDelegation, sealCodexOrchestrationDelegation, sealCodexParentDelegation } from '../task/codex-orchestration.js';
12
13
  const USAGE = 'Usage: agent-infra-internal codex-lifecycle <capability-arm|hook-event|resolve-start|resolve-stop|preflight|consume> [options]\n';
13
14
  const MANAGED_AGENT = /^agent-infra-lifecycle-(executor|reviewer)$/;
@@ -80,7 +81,7 @@ function controllerBinding(taskId) {
80
81
  const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
81
82
  if (!contextPath)
82
83
  return undefined;
83
- const context = verifyCodexSandboxControllerContext(contextPath, taskId, { repoRoot: process.cwd() });
84
+ const context = verifyCodexSandboxControllerContextWithWarnings(contextPath, { repoRoot: process.cwd() }).context;
84
85
  return {
85
86
  instanceDigest: context.controllerInstanceDigest,
86
87
  controlGeneration: context.controlGeneration
@@ -121,7 +122,8 @@ function recordFromPayload(phase, payload) {
121
122
  sessionId: text('sessionId'),
122
123
  turnId: text('turnId'),
123
124
  childThreadId: text('childThreadId'),
124
- nativeAgent: text('nativeAgent')
125
+ nativeAgent: text('nativeAgent'),
126
+ source: 'hook'
125
127
  };
126
128
  if (phase === 'subagent-stop')
127
129
  return {
@@ -314,8 +316,17 @@ async function codexLifecycle(args = []) {
314
316
  }
315
317
  const result = store.apply(event);
316
318
  if (parsed.values['--bridge'] === 'true' && event.type === 'hook-stop') {
317
- const bridged = await sealCodexOrchestrationDelegation(event.childThreadId, { store });
318
- outputBridgeResult(bridged);
319
+ if (!hasSealableOrchestrationDelegation('codex', event.childThreadId, { repoRoot: process.cwd() })) {
320
+ output({ status: 'ignored', changed: false, evidence: null, diagnostics: [], error: null });
321
+ return;
322
+ }
323
+ output({
324
+ status: result.state.status,
325
+ changed: true,
326
+ evidence: result.state,
327
+ diagnostics: [],
328
+ error: result.state.error
329
+ });
319
330
  return;
320
331
  }
321
332
  output({ status: result.state.status, changed: true, evidence: result.state, diagnostics: [], error: result.state.error });
@@ -341,7 +352,10 @@ async function codexLifecycle(args = []) {
341
352
  return;
342
353
  }
343
354
  if (parsed.operation === 'resolve-stop') {
344
- const terminal = await resolveCodexTerminal(childThreadId);
355
+ const stopTurnId = store.read(childThreadId).state.stop?.turnId;
356
+ if (!stopTurnId)
357
+ throw new Error('Codex lifecycle stop hook is not available');
358
+ const terminal = await resolveCodexTerminal(childThreadId, stopTurnId);
345
359
  const latest = store.apply(terminal);
346
360
  if (latest.state.status !== 'stop-ready') {
347
361
  throw new Error(`Codex lifecycle stop evidence is not ready (status=${latest.state.status})`);
@@ -1,4 +1,4 @@
1
- import { runCodexSandboxController, verifyCodexSandboxControllerContext } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
1
+ import { runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
2
2
  const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
3
3
  function fail(code, message, exitCode = 1) {
4
4
  process.stdout.write(`${JSON.stringify({
@@ -20,8 +20,6 @@ function parse(args) {
20
20
  return null;
21
21
  }
22
22
  const allowed = new Set([
23
- '--task-id',
24
- '--task-ref',
25
23
  '--context',
26
24
  '--executor-model',
27
25
  '--executor-reasoning-effort',
@@ -31,6 +29,11 @@ function parse(args) {
31
29
  const values = {};
32
30
  for (let index = 1; index < args.length; index += 1) {
33
31
  const flag = args[index];
32
+ if (flag === '--task-id' || flag.startsWith('--task-id=')
33
+ || flag === '--task-ref' || flag.startsWith('--task-ref=')) {
34
+ fail('CODEX_SANDBOX_CONTROLLER_IDENTITY_OPTION_UNSUPPORTED', 'controller task identity options are not supported', 2);
35
+ return null;
36
+ }
34
37
  const value = args[++index];
35
38
  if (!allowed.has(flag) || Object.hasOwn(values, flag) || !value || value.startsWith('--')) {
36
39
  fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', `invalid option '${flag}'`, 2);
@@ -44,18 +47,14 @@ async function codexSandboxController(args = []) {
44
47
  const parsed = parse(args);
45
48
  if (!parsed || process.exitCode)
46
49
  return;
47
- const taskId = parsed.values['--task-id'];
48
- if (!taskId) {
49
- fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', '--task-id is required', 2);
50
- return;
51
- }
52
50
  try {
53
51
  if (parsed.operation === 'verify-context') {
54
52
  const contextPath = parsed.values['--context']
55
53
  ?? process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
56
54
  if (!contextPath)
57
55
  throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_MISSING');
58
- const context = verifyCodexSandboxControllerContext(contextPath, taskId);
56
+ const verified = verifyCodexSandboxControllerContextWithWarnings(contextPath);
57
+ const context = verified.context;
59
58
  process.stdout.write(`${JSON.stringify({
60
59
  status: 'ready',
61
60
  changed: false,
@@ -66,15 +65,12 @@ async function codexSandboxController(args = []) {
66
65
  expiresAt: context.expiresAt,
67
66
  buildIdentity: context.buildIdentity,
68
67
  hookDefinitionHash: context.hookDefinitionHash,
69
- lifecycleProfilesHash: context.lifecycleProfilesHash
70
68
  },
69
+ ...(verified.warnings.length > 0 ? { warnings: verified.warnings } : {}),
71
70
  error: null
72
71
  })}\n`);
73
72
  return;
74
73
  }
75
- const taskRef = parsed.values['--task-ref'];
76
- if (!taskRef)
77
- throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_REF_REQUIRED');
78
74
  const policy = [
79
75
  '--executor-model',
80
76
  '--executor-reasoning-effort',
@@ -86,8 +82,6 @@ async function codexSandboxController(args = []) {
86
82
  throw new Error('CODEX_SANDBOX_CONTROLLER_MODEL_POLICY_INVALID');
87
83
  }
88
84
  const exitCode = await runCodexSandboxController({
89
- taskId,
90
- taskRef,
91
85
  executorModel: parsed.values['--executor-model'],
92
86
  executorReasoningEffort: parsed.values['--executor-reasoning-effort'],
93
87
  reviewerModel: parsed.values['--reviewer-model'],
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
- import { commitExplicitPaths, inspectGitWorkflow, pushGitRefs, pushRebasedBranch } from '../git/workflow.js';
2
+ import { inspectGitWorkflow, pushRebasedBranch } from '../git/workflow.js';
3
3
  import { compareReviewTrees, snapshotReview } from '../git/review-snapshot.js';
4
4
  import { resolvePostReviewGlobs } from '../task/review-fingerprint.js';
5
+ import { executeCommitOperation } from '../task/commit-operation.js';
5
6
  function output(value) { process.stdout.write(`${JSON.stringify(value)}\n`); }
6
7
  function gitWorkflow(args = []) {
7
8
  const [action, ...rest] = args;
@@ -16,12 +17,9 @@ function gitWorkflow(args = []) {
16
17
  else if (action === 'commit') {
17
18
  const inputIndex = rest.indexOf('--input');
18
19
  const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1], 'utf8')) : null;
19
- result = input ? commitExplicitPaths({ cwd: cwd ?? process.cwd(), ...input }) : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
20
- }
21
- else if (action === 'push') {
22
- const inputIndex = rest.indexOf('--input');
23
- const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1], 'utf8')) : null;
24
- result = input ? pushGitRefs({ cwd: cwd ?? process.cwd(), ...input }) : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
20
+ result = input
21
+ ? executeCommitOperation({ cwd: cwd ?? process.cwd(), ...input })
22
+ : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
25
23
  }
26
24
  else if (action === 'push-rebased') {
27
25
  const inputIndex = rest.indexOf('--input');