@fitlab-ai/agent-infra 0.9.9 → 0.9.11

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 (309) hide show
  1. package/README.md +13 -3
  2. package/README.zh-CN.md +13 -3
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +55 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +17 -10
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +11 -31
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +15 -33
  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/opencode-sandbox.js +0 -4
  13. package/dist/lib/agent-clients/adapters/traecli.js +65 -0
  14. package/dist/lib/agent-clients/config.js +14 -99
  15. package/dist/lib/agent-clients/reconcile.js +1 -11
  16. package/dist/lib/agent-clients/schema.js +5 -4
  17. package/dist/lib/agent-clients/tokens.js +7 -17
  18. package/dist/lib/agent-clients/types.js +2 -1
  19. package/dist/lib/decide.js +14 -30
  20. package/dist/lib/defaults.json +9 -0
  21. package/dist/lib/init.js +2 -2
  22. package/dist/lib/internal/codex-lifecycle.js +2 -2
  23. package/dist/lib/internal/codex-sandbox-controller.js +4 -3
  24. package/dist/lib/internal/release-workflow.js +0 -1
  25. package/dist/lib/internal/sandbox-control.js +2 -2
  26. package/dist/lib/internal/task-finalization.js +19 -9
  27. package/dist/lib/internal/task-ledger.js +8 -2
  28. package/dist/lib/internal/task-lifecycle.js +17 -93
  29. package/dist/lib/internal/task-orchestration.js +30 -216
  30. package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +77 -43
  31. package/dist/lib/merge.js +205 -103
  32. package/dist/lib/process-data/normalize.js +2 -9
  33. package/dist/lib/run/host.js +46 -16
  34. package/dist/lib/run/tui.js +5 -2
  35. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  36. package/dist/lib/sandbox/commands/create.js +3 -3
  37. package/dist/lib/sandbox/commands/enter.js +18 -4
  38. package/dist/lib/sandbox/config.js +3 -12
  39. package/dist/lib/sandbox/control/controller-registration.js +4 -2
  40. package/dist/lib/sandbox/control/executor.js +135 -40
  41. package/dist/lib/sandbox/control/lifecycle.js +29 -20
  42. package/dist/lib/sandbox/control/server.js +18 -16
  43. package/dist/lib/sandbox/mounts.js +3 -2
  44. package/dist/lib/sandbox/recovery.js +123 -22
  45. package/dist/lib/sandbox/workspace-identity.js +14 -0
  46. package/dist/lib/sandbox/workspace-view.js +8 -9
  47. package/dist/lib/self-update.js +278 -0
  48. package/dist/lib/server/protocol.js +5 -5
  49. package/dist/lib/task/activity-log.js +4 -1
  50. package/dist/lib/task/artifact-lifecycle.js +4 -2
  51. package/dist/lib/task/codex-orchestration.js +32 -35
  52. package/dist/lib/task/command-options.js +1 -3
  53. package/dist/lib/task/commands/cat.js +0 -5
  54. package/dist/lib/task/commands/decisions.js +76 -92
  55. package/dist/lib/task/commands/files.js +6 -6
  56. package/dist/lib/task/commands/grep.js +25 -61
  57. package/dist/lib/task/commands/issue-body.js +6 -6
  58. package/dist/lib/task/commands/log.js +25 -10
  59. package/dist/lib/task/commands/show.js +5 -6
  60. package/dist/lib/task/commands/status.js +8 -8
  61. package/dist/lib/task/commit-operation.js +21 -25
  62. package/dist/lib/task/commit-policy.js +1 -1
  63. package/dist/lib/task/control-authority.js +431 -0
  64. package/dist/lib/task/create.js +6 -1
  65. package/dist/lib/task/current-contract.js +35 -0
  66. package/dist/lib/task/decision-details.js +263 -0
  67. package/dist/lib/task/decision-intents.js +5 -2
  68. package/dist/lib/task/delegation-receipts.js +8 -7
  69. package/dist/lib/task/events.js +11 -5
  70. package/dist/lib/task/finalization.js +72 -46
  71. package/dist/lib/task/index.js +19 -29
  72. package/dist/lib/task/ledger-intents.js +8 -9
  73. package/dist/lib/task/ledger.js +13 -6
  74. package/dist/lib/task/lifecycle.js +16 -0
  75. package/dist/lib/task/orchestration.js +51 -14
  76. package/dist/lib/task/review-artifacts.js +31 -12
  77. package/dist/lib/task/review-finalization.js +19 -6
  78. package/dist/lib/task/verification-engine.js +38 -18
  79. package/dist/lib/task/verification-review.js +1 -1
  80. package/dist/lib/task/workspace-snapshot.js +5 -11
  81. package/dist/lib/task/write.js +11 -1
  82. package/dist/lib/update.js +6 -37
  83. package/dist/lib/version.js +5 -1
  84. package/dist/lifecycle-build-manifest.json +3 -3
  85. package/lib/agent-client.ts +0 -1
  86. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +25 -10
  87. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +16 -33
  88. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +16 -35
  89. package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
  90. package/lib/agent-clients/adapters/index.ts +3 -1
  91. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  92. package/lib/agent-clients/adapters/traecli.ts +66 -0
  93. package/lib/agent-clients/config.ts +14 -144
  94. package/lib/agent-clients/reconcile.ts +1 -12
  95. package/lib/agent-clients/schema.ts +5 -4
  96. package/lib/agent-clients/tokens.ts +7 -20
  97. package/lib/agent-clients/types.ts +2 -1
  98. package/lib/decide.ts +12 -22
  99. package/lib/defaults.json +9 -0
  100. package/lib/init.ts +2 -2
  101. package/lib/internal/codex-lifecycle.ts +2 -2
  102. package/lib/internal/codex-sandbox-controller.ts +4 -3
  103. package/lib/internal/release-workflow.ts +0 -1
  104. package/lib/internal/sandbox-control.ts +2 -2
  105. package/lib/internal/task-finalization.ts +27 -13
  106. package/lib/internal/task-ledger.ts +8 -2
  107. package/lib/internal/task-lifecycle.ts +33 -76
  108. package/lib/internal/task-orchestration.ts +33 -190
  109. package/lib/{task/commands/validate.ts → internal/task-validate.ts} +77 -41
  110. package/lib/merge.ts +236 -120
  111. package/lib/process-data/normalize.ts +2 -8
  112. package/lib/run/host.ts +63 -14
  113. package/lib/run/tui.ts +5 -3
  114. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  115. package/lib/sandbox/commands/create.ts +4 -4
  116. package/lib/sandbox/commands/enter.ts +24 -4
  117. package/lib/sandbox/config.ts +2 -14
  118. package/lib/sandbox/control/container-identity.ts +2 -2
  119. package/lib/sandbox/control/controller-registration.ts +8 -3
  120. package/lib/sandbox/control/executor.ts +156 -53
  121. package/lib/sandbox/control/lifecycle.ts +34 -28
  122. package/lib/sandbox/control/protocol.ts +1 -3
  123. package/lib/sandbox/control/server.ts +15 -12
  124. package/lib/sandbox/control/state.ts +7 -7
  125. package/lib/sandbox/mounts.ts +4 -3
  126. package/lib/sandbox/recovery.ts +162 -34
  127. package/lib/sandbox/tools.ts +1 -1
  128. package/lib/sandbox/workspace-identity.ts +28 -0
  129. package/lib/sandbox/workspace-view.ts +14 -10
  130. package/lib/self-update.ts +341 -0
  131. package/lib/server/protocol.ts +5 -5
  132. package/lib/task/activity-log.ts +6 -1
  133. package/lib/task/artifact-lifecycle.ts +5 -3
  134. package/lib/task/codex-orchestration.ts +40 -45
  135. package/lib/task/command-options.ts +1 -2
  136. package/lib/task/commands/cat.ts +0 -4
  137. package/lib/task/commands/decisions.ts +80 -75
  138. package/lib/task/commands/files.ts +6 -6
  139. package/lib/task/commands/grep.ts +23 -51
  140. package/lib/task/commands/issue-body.ts +6 -6
  141. package/lib/task/commands/log.ts +22 -10
  142. package/lib/task/commands/show.ts +5 -6
  143. package/lib/task/commands/status.ts +8 -8
  144. package/lib/task/commit-operation.ts +25 -30
  145. package/lib/task/commit-policy.ts +1 -2
  146. package/lib/task/control-authority.ts +563 -0
  147. package/lib/task/create.ts +6 -1
  148. package/lib/task/current-contract.ts +49 -0
  149. package/lib/task/decision-details.ts +340 -0
  150. package/lib/task/decision-intents.ts +7 -2
  151. package/lib/task/delegation-receipts.ts +8 -6
  152. package/lib/task/events.ts +10 -5
  153. package/lib/task/finalization.ts +84 -42
  154. package/lib/task/index.ts +19 -28
  155. package/lib/task/ledger-intents.ts +6 -8
  156. package/lib/task/ledger.ts +17 -7
  157. package/lib/task/lifecycle.ts +20 -0
  158. package/lib/task/orchestration.ts +42 -16
  159. package/lib/task/review-artifacts.ts +53 -15
  160. package/lib/task/review-finalization.ts +33 -7
  161. package/lib/task/verification-engine.ts +33 -21
  162. package/lib/task/verification-review.ts +1 -1
  163. package/lib/task/workspace-snapshot.ts +4 -13
  164. package/lib/task/write.ts +20 -1
  165. package/lib/update.ts +6 -40
  166. package/lib/version.ts +6 -1
  167. package/package.json +1 -1
  168. package/templates/.agents/QUICKSTART.en.md +2 -3
  169. package/templates/.agents/QUICKSTART.zh-CN.md +2 -3
  170. package/templates/.agents/README.en.md +5 -3
  171. package/templates/.agents/README.zh-CN.md +5 -3
  172. package/templates/.agents/rules/README.en.md +1 -0
  173. package/templates/.agents/rules/README.zh-CN.md +1 -0
  174. package/templates/.agents/rules/compatibility-policy.en.md +40 -0
  175. package/templates/.agents/rules/compatibility-policy.zh-CN.md +40 -0
  176. package/templates/.agents/rules/create-issue.github.en.md +1 -1
  177. package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
  178. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  179. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  180. package/templates/.agents/rules/issue-fields.github.en.md +1 -1
  181. package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
  182. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  183. package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
  184. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
  185. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  186. package/templates/.agents/rules/issue-sync.github.en.md +1 -1
  187. package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
  188. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
  189. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
  190. package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
  191. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
  192. package/templates/.agents/rules/next-step-output.en.md +3 -3
  193. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  194. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  195. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  196. package/templates/.agents/rules/pr-sync.en.md +2 -2
  197. package/templates/.agents/rules/pr-sync.github.en.md +3 -2
  198. package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
  199. package/templates/.agents/rules/pr-sync.zh-CN.md +2 -1
  200. package/templates/.agents/rules/release-commands.github.en.md +6 -0
  201. package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
  202. package/templates/.agents/rules/review-handshake.en.md +1 -1
  203. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  204. package/templates/.agents/rules/review-method.en.md +1 -0
  205. package/templates/.agents/rules/review-method.zh-CN.md +1 -0
  206. package/templates/.agents/rules/task-management.en.md +1 -2
  207. package/templates/.agents/rules/task-management.zh-CN.md +1 -2
  208. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  209. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  210. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  213. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  214. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  215. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  216. package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
  217. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
  218. package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
  219. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/SKILL.en.md +4 -3
  221. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +5 -3
  222. package/templates/.agents/skills/commit/SKILL.en.md +27 -7
  223. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
  226. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  227. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  228. package/templates/.agents/skills/create-pr/SKILL.en.md +15 -11
  229. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +15 -11
  230. package/templates/.agents/skills/create-pr/reference/change-report.en.md +46 -0
  231. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +46 -0
  232. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  233. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  234. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  235. package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
  236. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
  237. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  238. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  239. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -4
  240. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -4
  241. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  242. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  243. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
  244. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
  245. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  246. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  247. package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
  248. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
  249. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  250. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  251. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +9 -6
  252. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +9 -6
  253. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
  254. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
  255. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  256. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  257. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +7 -3
  258. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +7 -3
  259. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
  260. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
  261. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
  262. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
  263. package/templates/.agents/skills/run-task/SKILL.en.md +6 -2
  264. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -2
  265. package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
  266. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
  267. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  268. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  269. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +159 -104
  270. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  271. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  272. package/templates/.agents/templates/task.en.md +1 -1
  273. package/templates/.agents/templates/task.zh-CN.md +1 -1
  274. package/templates/.claude/commands/analyze-task.en.md +1 -1
  275. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  276. package/templates/.claude/commands/block-task.en.md +1 -1
  277. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  278. package/templates/.claude/commands/cancel-task.en.md +1 -1
  279. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  280. package/templates/.claude/commands/check-task.en.md +1 -1
  281. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  282. package/templates/.claude/commands/code-task.en.md +1 -1
  283. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  284. package/templates/.claude/commands/commit.en.md +1 -1
  285. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  286. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  287. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  288. package/templates/.claude/commands/complete-task.en.md +1 -1
  289. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  290. package/templates/.claude/commands/create-pr.en.md +1 -1
  291. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  292. package/templates/.claude/commands/plan-task.en.md +1 -1
  293. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  294. package/templates/.claude/commands/review-analysis.en.md +1 -1
  295. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  296. package/templates/.claude/commands/review-code.en.md +1 -1
  297. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  298. package/templates/.claude/commands/review-plan.en.md +1 -1
  299. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  300. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  301. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  302. package/templates/.claude/commands/run-task.en.md +1 -1
  303. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  304. package/templates/.claude/commands/watch-pr.en.md +1 -1
  305. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  306. package/templates/.traecli/commands/update-agent-infra.en.md +7 -0
  307. package/templates/.traecli/commands/update-agent-infra.zh-CN.md +7 -0
  308. package/dist/lib/builtin-tuis.js +0 -40
  309. package/lib/builtin-tuis.ts +0 -56
@@ -10,20 +10,13 @@ import type {
10
10
  OrchestrationModelPolicy
11
11
  } from './types.ts';
12
12
 
13
- type AgentClientConfigInput = Readonly<{
14
- agentClients?: unknown;
15
- tuis?: unknown;
16
- sandbox?: unknown;
17
- customTUIs?: unknown;
18
- }>;
13
+ type AgentClientConfigInput = Readonly<Record<string, unknown>>;
19
14
 
20
15
  type AgentClientDiagnosticCode =
21
16
  | 'INVALID_AGENT_CLIENTS'
22
17
  | 'DUPLICATE_AGENT_CLIENT'
23
18
  | 'MISSING_AGENT_CLIENT'
24
- | 'UNKNOWN_AGENT_CLIENT'
25
- | 'LEGACY_CONFLICT'
26
- | 'LEGACY_VALUE_IGNORED';
19
+ | 'UNKNOWN_AGENT_CLIENT';
27
20
 
28
21
  type AgentClientDiagnostic = Readonly<{
29
22
  code: AgentClientDiagnosticCode;
@@ -31,13 +24,8 @@ type AgentClientDiagnostic = Readonly<{
31
24
  }>;
32
25
 
33
26
  type NormalizeAgentClientsResult = Readonly<{
34
- source: 'canonical' | 'legacy';
35
27
  state: AgentClientState;
36
28
  canonical: AgentClientsConfig;
37
- remainingSandboxTools: readonly string[] | undefined;
38
- removeLegacyTuis: boolean;
39
- changed: boolean;
40
- diagnostics: readonly AgentClientDiagnostic[];
41
29
  }>;
42
30
 
43
31
  class AgentClientConfigError extends Error {
@@ -166,147 +154,29 @@ function serializeAgentClients(state: AgentClientState): AgentClientsConfig {
166
154
  }));
167
155
  }
168
156
 
169
- function projectLegacyTuis(
170
- value: unknown,
171
- diagnostics: AgentClientDiagnostic[]
172
- ): Readonly<Record<AgentClientId, boolean>> {
173
- if (!Array.isArray(value)) {
174
- return Object.fromEntries(
175
- AGENT_CLIENT_IDS.map((id) => [id, true])
176
- ) as Readonly<Record<AgentClientId, boolean>>;
177
- }
178
-
179
- const enabled = new Set<AgentClientId>();
180
- for (const [index, candidate] of value.entries()) {
181
- if (isAgentClientId(candidate)) {
182
- enabled.add(candidate);
183
- } else {
184
- diagnostics.push({
185
- code: 'LEGACY_VALUE_IGNORED',
186
- path: `tuis[${index}]`
187
- });
188
- }
189
- }
190
- return Object.fromEntries(
191
- AGENT_CLIENT_IDS.map((id) => [id, enabled.has(id)])
192
- ) as Readonly<Record<AgentClientId, boolean>>;
193
- }
194
-
195
- type SandboxProjection = Readonly<{
196
- installed: Readonly<Record<AgentClientId, boolean>>;
197
- clientSignals: ReadonlySet<AgentClientId>;
198
- remainingTools: readonly string[] | undefined;
199
- }>;
200
-
201
- function projectLegacySandbox(
202
- value: unknown,
203
- diagnostics: AgentClientDiagnostic[]
204
- ): SandboxProjection {
205
- if (!Array.isArray(value) || value.length === 0) {
206
- return {
207
- installed: Object.fromEntries(
208
- AGENT_CLIENT_IDS.map((id) => [id, true])
209
- ) as Readonly<Record<AgentClientId, boolean>>,
210
- clientSignals: new Set<AgentClientId>(),
211
- remainingTools: Array.isArray(value) ? [] : undefined
212
- };
213
- }
214
-
215
- const installed = new Set<AgentClientId>();
216
- const remainingTools: string[] = [];
217
- for (const [index, candidate] of value.entries()) {
218
- if (isAgentClientId(candidate)) {
219
- installed.add(candidate);
220
- } else if (typeof candidate === 'string') {
221
- remainingTools.push(candidate);
222
- } else {
223
- diagnostics.push({
224
- code: 'LEGACY_VALUE_IGNORED',
225
- path: `sandbox.tools[${index}]`
226
- });
227
- }
228
- }
229
- return {
230
- installed: Object.fromEntries(
231
- AGENT_CLIENT_IDS.map((id) => [id, installed.has(id)])
232
- ) as Readonly<Record<AgentClientId, boolean>>,
233
- clientSignals: installed,
234
- remainingTools
235
- };
236
- }
237
-
238
- function stateFromLegacy(
239
- enabled: Readonly<Record<AgentClientId, boolean>>,
240
- installed: Readonly<Record<AgentClientId, boolean>>
241
- ): AgentClientState {
242
- return Object.fromEntries(
243
- AGENT_CLIENT_IDS.map((id) => [
244
- id,
245
- {
246
- enabled: enabled[id],
247
- installInSandbox: installed[id]
248
- }
249
- ])
250
- ) as AgentClientState;
251
- }
252
-
253
- function sameCanonicalOrder(value: unknown): boolean {
254
- return Array.isArray(value)
255
- && value.every((entry, index) => isRecord(entry) && entry.id === AGENT_CLIENT_IDS[index]);
256
- }
257
-
258
157
  function normalizeAgentClients(
259
158
  input: AgentClientConfigInput
260
159
  ): NormalizeAgentClientsResult {
261
- const diagnostics: AgentClientDiagnostic[] = [];
262
- const hasCanonical = hasOwn(input, 'agentClients');
263
- const hasLegacyTuis = hasOwn(input, 'tuis');
264
- const sandbox = isRecord(input.sandbox) ? input.sandbox : undefined;
265
- const hasLegacySandboxTools = sandbox !== undefined && hasOwn(sandbox, 'tools');
266
- const tuiProjection = projectLegacyTuis(input.tuis, diagnostics);
267
- const sandboxProjection = projectLegacySandbox(sandbox?.tools, diagnostics);
268
-
269
- if (!hasCanonical) {
270
- const state = stateFromLegacy(tuiProjection, sandboxProjection.installed);
271
- return {
272
- source: 'legacy',
273
- state,
274
- canonical: serializeAgentClients(state),
275
- remainingSandboxTools: sandboxProjection.remainingTools,
276
- removeLegacyTuis: hasLegacyTuis,
277
- changed: true,
278
- diagnostics
279
- };
280
- }
281
-
160
+ if (!hasOwn(input, 'agentClients')) fail('MISSING_AGENT_CLIENT', 'agentClients');
282
161
  const state = parseCanonical(input.agentClients);
283
- if (hasLegacyTuis) {
284
- for (const id of AGENT_CLIENT_IDS) {
285
- if (state[id].enabled !== tuiProjection[id]) {
286
- fail('LEGACY_CONFLICT', 'tuis');
287
- }
162
+ for (const [index, id] of AGENT_CLIENT_IDS.entries()) {
163
+ const entry = Array.isArray(input.agentClients) ? input.agentClients[index] : undefined;
164
+ if (!isRecord(entry) || entry.id !== id) {
165
+ fail('INVALID_AGENT_CLIENTS', `agentClients[${index}].id`);
288
166
  }
289
167
  }
290
- if (hasLegacySandboxTools) {
291
- for (const id of sandboxProjection.clientSignals) {
292
- if (!state[id].installInSandbox) {
293
- fail('LEGACY_CONFLICT', 'sandbox.tools');
168
+ if (hasOwn(input, 'tuis')) fail('INVALID_AGENT_CLIENTS', 'tuis');
169
+ const sandbox = isRecord(input.sandbox) ? input.sandbox : undefined;
170
+ if (Array.isArray(sandbox?.tools)) {
171
+ for (const [index, tool] of sandbox.tools.entries()) {
172
+ if (isAgentClientId(tool)) {
173
+ fail('INVALID_AGENT_CLIENTS', `sandbox.tools[${index}]`);
294
174
  }
295
175
  }
296
176
  }
297
-
298
177
  return {
299
- source: 'canonical',
300
178
  state,
301
- canonical: serializeAgentClients(state),
302
- remainingSandboxTools: hasLegacySandboxTools
303
- ? sandboxProjection.remainingTools
304
- : undefined,
305
- removeLegacyTuis: hasLegacyTuis,
306
- changed: hasLegacyTuis
307
- || sandboxProjection.clientSignals.size > 0
308
- || !sameCanonicalOrder(input.agentClients),
309
- diagnostics
179
+ canonical: serializeAgentClients(state)
310
180
  };
311
181
  }
312
182
 
@@ -32,7 +32,6 @@ type AgentClientSeedOperation = Readonly<{
32
32
  type WorkflowDiagnostic = AgentClientDiagnostic | Readonly<{ code: string; path: string }>;
33
33
 
34
34
  type AgentClientWorkflowPlan = Readonly<{
35
- source: 'canonical' | 'legacy';
36
35
  before: AgentClientState;
37
36
  desired: AgentClientState;
38
37
  nextConfig: Record<string, unknown>;
@@ -149,15 +148,6 @@ function planAgentClientReconciliation(input: Readonly<{
149
148
 
150
149
  const nextConfig = structuredClone(input.config);
151
150
  nextConfig.agentClients = serializeAgentClients(desired);
152
- delete nextConfig.tuis;
153
- if (normalized.remainingSandboxTools !== undefined) {
154
- const sandbox = isRecord(nextConfig.sandbox) ? { ...nextConfig.sandbox } : {};
155
- sandbox.tools = [
156
- 'agent-infra',
157
- ...normalized.remainingSandboxTools.filter((tool) => tool !== 'agent-infra')
158
- ];
159
- nextConfig.sandbox = sandbox;
160
- }
161
151
  const existingFiles = isRecord(nextConfig.files) ? nextConfig.files : {};
162
152
  const existingBaselines = isRecord(existingFiles.managedBaselines)
163
153
  ? existingFiles.managedBaselines
@@ -240,14 +230,13 @@ function planAgentClientReconciliation(input: Readonly<{
240
230
  || actionableSeed;
241
231
 
242
232
  return Object.freeze({
243
- source: normalized.source,
244
233
  before: normalized.state,
245
234
  desired,
246
235
  nextConfig,
247
236
  projectAssets,
248
237
  seedOperations: Object.freeze(seedOperations),
249
238
  nextSteps,
250
- diagnostics: Object.freeze([...normalized.diagnostics, ...custom.diagnostics]),
239
+ diagnostics: Object.freeze([...custom.diagnostics]),
251
240
  changed,
252
241
  projectRoot: input.projectRoot,
253
242
  configPath: path.join(input.projectRoot, '.agents', '.airc.json')
@@ -15,14 +15,15 @@ const AGENT_CLIENTS_SCHEMA = {
15
15
  maxItems: AGENT_CLIENT_IDS.length,
16
16
  uniqueItems: true,
17
17
  description: 'Canonical Agent Client configuration. Object ID completeness and uniqueness are enforced by the runtime normalizer.',
18
- items: {
18
+ additionalItems: false,
19
+ items: AGENT_CLIENT_IDS.map((id) => ({
19
20
  type: 'object',
20
21
  additionalProperties: false,
21
22
  required: ['id', 'enabled', 'installInSandbox'],
22
23
  properties: {
23
24
  id: {
24
- type: 'string',
25
- enum: [...AGENT_CLIENT_IDS]
25
+ const: id,
26
+ type: 'string'
26
27
  },
27
28
  enabled: {
28
29
  type: 'boolean'
@@ -40,7 +41,7 @@ const AGENT_CLIENTS_SCHEMA = {
40
41
  }
41
42
  }
42
43
  }
43
- }
44
+ }))
44
45
  },
45
46
  customTUIs: {
46
47
  type: 'array',
@@ -1,33 +1,20 @@
1
1
  // Active AI collaborator short tokens — the write-side whitelist for the
2
2
  // activity log. Migrated from lib/task/commands/log.ts so that the write-side
3
- // internal commands and the rendering side share one definition. Historical
4
- // Gemini tokens remain read-only and are handled by classifyAgent below.
3
+ // internal commands and the rendering side share one definition.
5
4
  export const KNOWN_AI_AGENTS: ReadonlySet<string> =
6
- new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor']);
5
+ new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor', 'traecli']);
7
6
 
8
- // Long name -> short name mapping (HD-4, decision A): `claude-code` reuses the
9
- // existing orchestration precedent, and `antigravity-cli` is the active
10
- // client. `gemini-cli` is intentionally not writable.
7
+ // Long name -> short name mapping: `claude-code` reuses the existing
8
+ // orchestration precedent, and `antigravity-cli` is the active client.
11
9
  export const AGENT_LONG_NAMES: Readonly<Record<string, string>> = {
12
10
  'claude-code': 'claude',
13
11
  'antigravity-cli': 'antigravity'
14
12
  };
15
13
 
16
14
  export const AGENT_USAGE_HINT =
17
- "agent must be a short token (claude/codex/antigravity/opencode/cursor), " +
15
+ "agent must be a short token (claude/codex/antigravity/opencode/cursor/traecli), " +
18
16
  "a long name (claude-code -> claude, antigravity-cli -> antigravity), or 'human'";
19
17
 
20
- // Read-side historical compatibility for activity-log rendering: `gemini` and
21
- // `gemini-cli` stay recognizable when displaying old logs, but are never
22
- // accepted by normalizeAgentToken (write side).
23
- const READ_KNOWN_AI_AGENTS: ReadonlySet<string> =
24
- new Set([...KNOWN_AI_AGENTS, 'gemini']);
25
-
26
- const READ_AGENT_LONG_NAMES: Readonly<Record<string, string>> = {
27
- ...AGENT_LONG_NAMES,
28
- 'gemini-cli': 'gemini'
29
- };
30
-
31
18
  // Strict write-side validation: the value must be exactly a short token, a
32
19
  // long name, or 'human'; returns the normalized short token, or null when
33
20
  // the value is not a recognized agent.
@@ -47,7 +34,7 @@ export function classifyAgent(raw: string):
47
34
  const token = raw.trim().split(/\s+/)[0]?.replace(/\(.*$/, '') ?? '';
48
35
  if (token === '') return { status: 'unknown', display: 'human' };
49
36
  if (token === 'human') return { status: 'human', display: 'human' };
50
- if (READ_KNOWN_AI_AGENTS.has(token)) return { status: 'ai', display: token };
51
- const short = READ_AGENT_LONG_NAMES[token];
37
+ if (KNOWN_AI_AGENTS.has(token)) return { status: 'ai', display: token };
38
+ const short = AGENT_LONG_NAMES[token];
52
39
  return short ? { status: 'ai', display: short } : { status: 'unknown', display: 'human' };
53
40
  }
@@ -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
  ] as const;
7
8
 
8
9
  type AgentClientId = (typeof AGENT_CLIENT_IDS)[number];
package/lib/decide.ts CHANGED
@@ -3,7 +3,7 @@ import { parseTaskScope } from './task/command-options.ts';
3
3
  import { applyHumanDecision } from './task/decision-intents.ts';
4
4
  import { canonicalTimestamp } from './task/write.ts';
5
5
  import { consumeHumanOverride, failureId } from './task/human-override.ts';
6
- import { resolveTaskRef } from './task/resolve-ref.ts';
6
+ import { resolveTaskContext } from './task/resolve-ref.ts';
7
7
  import { TaskExecutionLockError, withTaskExecutionLock } from './task/task-execution-lock.ts';
8
8
 
9
9
  type DecideOptions = {
@@ -55,44 +55,36 @@ export async function decide(args: string[], options: DecideOptions = {}): Promi
55
55
  item = scope.positionals[++index];
56
56
  if (!item) { process.stderr.write(`Error: ${arg} requires a value\n`); return 1; }
57
57
  } else if (arg.startsWith('--item=')) {
58
- if (item !== undefined) { process.stderr.write("Error: duplicate option '--item'\n"); return 1; }
59
- item = arg.slice('--item='.length);
60
- if (item === '') { process.stderr.write('Error: --item requires a value\n'); return 1; }
58
+ process.stderr.write('Error: --item=... is not supported; use --item <selector> or -i <selector>\n'); return 1;
61
59
  } else operands.push(arg);
62
60
  }
63
- let taskRef = scope.taskRef;
64
- let selector = item;
65
- let decisionParts: string[];
66
- if (item !== undefined) {
67
- decisionParts = operands;
68
- } else if (!scope.explicit) {
69
- [taskRef, selector, ...decisionParts] = operands;
70
- } else {
71
- decisionParts = [];
72
- }
61
+ const taskRef = scope.taskRef;
62
+ const selector = item;
63
+ const decisionParts = operands;
73
64
  if (!selector || decisionParts.length === 0) {
74
- process.stderr.write('Usage: ai decide [--task <ref>] --item <ordinal|ledger-id> [--needs-implementation true|false] <decision>\n ai decide <task-ref> <ordinal|ledger-id> [--needs-implementation true|false] <decision>\n');
65
+ process.stderr.write('Usage: ai decide [--task <ref> | -t <ref>] (--item <ordinal|ledger-id> | -i <ordinal|ledger-id>) [--needs-implementation true|false] <decision>\n');
75
66
  return 1;
76
67
  }
77
68
  try {
78
69
  const parsedDecision = parseDecisionParts(decisionParts);
79
70
  const now = (options.now ?? canonicalTimestamp)();
71
+ const resolved = resolveTaskContext(taskRef, { repoRoot: options.repoRoot });
72
+ if (!resolved.ok) throw new Error(resolved.message);
80
73
  const request = {
81
- taskRef, selector, decision: parsedDecision.decision,
74
+ taskRef: resolved.taskId, selector, decision: parsedDecision.decision,
82
75
  needsImplementation: parsedDecision.needsImplementation
83
76
  };
84
77
  const writeOptions = {
85
- repoRoot: options.repoRoot,
78
+ repoRoot: resolved.repoRoot,
86
79
  metadataProvider: () => ({ timestamp: now, agentInfraVersion: options.version ?? VERSION })
87
80
  };
88
- const resolved = taskRef ? resolveTaskRef(taskRef, { repoRoot: options.repoRoot }) : null;
89
81
  let result;
90
82
  const execute = () => {
91
83
  let current = applyHumanDecision(request, writeOptions);
92
84
  if (current.status !== 'failed' || !parsedDecision.overrideTicket) return current;
93
85
  if (!parsedDecision.overrideTarget || !parsedDecision.overrideScope) throw new Error('override ticket requires --override-target and --override-scope');
94
86
  const consumed = consumeHumanOverride({
95
- taskRef: resolved?.ok ? resolved.taskId : String(taskRef ?? ''),
87
+ taskRef: resolved.taskId,
96
88
  ticketId: parsedDecision.overrideTicket,
97
89
  failureId: failureId('decision-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
98
90
  target: parsedDecision.overrideTarget,
@@ -110,9 +102,7 @@ export async function decide(args: string[], options: DecideOptions = {}): Promi
110
102
  if (consumed.status === 'failed') throw new Error(consumed.error.message);
111
103
  return current;
112
104
  };
113
- result = resolved?.ok
114
- ? withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-decision', execute)
115
- : execute();
105
+ result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-decision', execute);
116
106
  if (result.error) throw new Error(result.error.message);
117
107
  return 0;
118
108
  } catch (error) {
package/lib/defaults.json CHANGED
@@ -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": {
@@ -85,6 +90,10 @@
85
90
  ],
86
91
  "ejected": [],
87
92
  "retiredManaged": [
93
+ {
94
+ "path": ".trae/skills/",
95
+ "templateHashes": []
96
+ },
88
97
  {
89
98
  "path": ".agents/workspace/README.md",
90
99
  "templateHashes": [
package/lib/init.ts CHANGED
@@ -117,7 +117,7 @@ async function cmdInit(): Promise<void> {
117
117
  // check existing config
118
118
  if (fs.existsSync(configPath)) {
119
119
  err('This project already has agent-infra configuration.');
120
- err('Use /update-agent-infra in your AI TUI to update.');
120
+ err('Use /update-agent-infra in your AI TUI to sync project files.');
121
121
  process.exitCode = 1;
122
122
  return;
123
123
  }
@@ -294,7 +294,7 @@ async function cmdInit(): Promise<void> {
294
294
  console.log('');
295
295
  ok('Project initialized successfully!');
296
296
  console.log('');
297
- console.log(' If this init used npx, install agent-infra persistently before update or validation:');
297
+ console.log(' If this init used npx, install agent-infra persistently before ai update or validation:');
298
298
  console.log(' npm install -g @fitlab-ai/agent-infra');
299
299
  console.log('');
300
300
  if (workflowPlan.nextSteps.length === 0) {
@@ -11,7 +11,7 @@ import {
11
11
  import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.ts';
12
12
  import { createCodexCapabilityStore } from '../agent-clients/adapters/codex-lifecycle/capability-store.ts';
13
13
  import { computeLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.ts';
14
- import { verifyCodexSandboxControllerContext } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
14
+ import { verifyCodexSandboxControllerContextWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
15
15
  import type { CodexLifecycleEvent } from '../agent-clients/adapters/codex-lifecycle/evidence.ts';
16
16
  import { resolveTaskRef } from '../task/resolve-ref.ts';
17
17
  import { hasSealableOrchestrationDelegation } from '../task/orchestration.ts';
@@ -101,7 +101,7 @@ function hookDefinitionHash(): string {
101
101
  function controllerBinding(taskId: string) {
102
102
  const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
103
103
  if (!contextPath) return undefined;
104
- const context = verifyCodexSandboxControllerContext(contextPath, { repoRoot: process.cwd() });
104
+ const context = verifyCodexSandboxControllerContextWithWarnings(contextPath, { repoRoot: process.cwd() }).context;
105
105
  return {
106
106
  instanceDigest: context.controllerInstanceDigest,
107
107
  controlGeneration: context.controlGeneration
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runCodexSandboxController,
3
- verifyCodexSandboxControllerContext
3
+ verifyCodexSandboxControllerContextWithWarnings
4
4
  } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
5
5
 
6
6
  const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
@@ -58,7 +58,8 @@ async function codexSandboxController(args: string[] = []): Promise<void> {
58
58
  const contextPath = parsed.values['--context']
59
59
  ?? process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
60
60
  if (!contextPath) throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_MISSING');
61
- const context = verifyCodexSandboxControllerContext(contextPath);
61
+ const verified = verifyCodexSandboxControllerContextWithWarnings(contextPath);
62
+ const context = verified.context;
62
63
  process.stdout.write(`${JSON.stringify({
63
64
  status: 'ready',
64
65
  changed: false,
@@ -69,8 +70,8 @@ async function codexSandboxController(args: string[] = []): Promise<void> {
69
70
  expiresAt: context.expiresAt,
70
71
  buildIdentity: context.buildIdentity,
71
72
  hookDefinitionHash: context.hookDefinitionHash,
72
- lifecycleProfilesHash: context.lifecycleProfilesHash
73
73
  },
74
+ ...(verified.warnings.length > 0 ? { warnings: verified.warnings } : {}),
74
75
  error: null
75
76
  })}\n`);
76
77
  return;
@@ -36,7 +36,6 @@ const DEMO_INPUT_PATHS = [
36
36
  'lib/prompt.ts',
37
37
  'lib/paths.ts',
38
38
  'lib/render.ts',
39
- 'lib/builtin-tuis.ts',
40
39
  'lib/sandbox/engines/'
41
40
  ] as const;
42
41
  const DEMO_DIGEST_PATH = 'assets/demo-init.inputs.sha256';
@@ -9,7 +9,7 @@ import { serveSandboxControl } from '../sandbox/control/server.ts';
9
9
  import { runSandboxControlExecutor } from '../sandbox/control/executor.ts';
10
10
  import {
11
11
  controllerProofFromContext,
12
- verifyCodexSandboxControllerContext
12
+ verifyCodexSandboxControllerContextWithWarnings
13
13
  } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
14
14
 
15
15
  function isCanonicalCodexPrepare(args: readonly string[]): boolean {
@@ -118,7 +118,7 @@ async function sandboxControl(args: string[]): Promise<void> {
118
118
  if (family === 'task-orchestration' && isCanonicalCodexPrepare(commandArgs)) {
119
119
  const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
120
120
  const proof = contextPath
121
- ? controllerProofFromContext(verifyCodexSandboxControllerContext(contextPath))
121
+ ? controllerProofFromContext(verifyCodexSandboxControllerContextWithWarnings(contextPath).context)
122
122
  : null;
123
123
  response = requestSandboxTaskControl({ family, args: commandArgs, controllerProof: proof });
124
124
  } else {
@@ -1,7 +1,10 @@
1
- import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
1
+ import {
2
+ createDirectHostExecutionContext,
3
+ dispatchTaskControlOperation,
4
+ parseTaskControlOperation
5
+ } from '../task/control-authority.ts';
2
6
  import { applyTaskFinalization } from '../task/finalization.ts';
3
7
  import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.ts';
4
- import { verifyTaskEvent } from '../task/verification.ts';
5
8
 
6
9
  const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
7
10
 
@@ -28,17 +31,26 @@ function fail(message: string): void {
28
31
  process.exitCode = 1;
29
32
  }
30
33
 
34
+ function parseFailure(error: unknown): string {
35
+ const message = error instanceof Error ? error.message : String(error);
36
+ return message.replace(/^TASK_CONTROL_OPERATION_INVALID: /u, '');
37
+ }
38
+
31
39
  function taskFinalization(args: string[] = []): void {
32
40
  if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
33
- if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
34
- fail('task ref, complete intent, and --agent are required');
41
+
42
+ let operation;
43
+ try {
44
+ operation = parseTaskControlOperation('task-finalization', args);
45
+ } catch (error) {
46
+ fail(parseFailure(error));
35
47
  return;
36
48
  }
37
- const agent = normalizeAgentToken(args[3]);
38
- if (!agent) {
39
- fail(`invalid --agent: ${AGENT_USAGE_HINT}`);
49
+ if (operation.family !== 'task-finalization') {
50
+ fail('finalization operation is invalid');
40
51
  return;
41
52
  }
53
+
42
54
  let repoRoot: string;
43
55
  try {
44
56
  repoRoot = detectRepoRoot();
@@ -48,18 +60,20 @@ function taskFinalization(args: string[] = []): void {
48
60
  process.exitCode = 1;
49
61
  return;
50
62
  }
51
- const resolved = resolveTaskRef(args[0], { repoRoot });
63
+ const resolved = resolveTaskRef(operation.request.taskRef, { repoRoot });
52
64
  if (!resolved.ok) {
53
65
  const detail = { code: resolved.code, message: resolved.message, retryable: false };
54
66
  process.stdout.write(envelope('failed', false, true, null, detail));
55
67
  process.exitCode = 1;
56
68
  return;
57
69
  }
58
- const result = applyTaskFinalization(
59
- { taskRef: resolved.taskId, intent: 'complete', agent },
60
- { repoRoot, preflight: (request, options) => verifyTaskEvent(
61
- { ...request, event: 'complete-task.hard-preflight' }, options
62
- ) }
70
+ const boundOperation = {
71
+ ...operation,
72
+ request: { ...operation.request, taskRef: resolved.taskId }
73
+ };
74
+ const result = dispatchTaskControlOperation(
75
+ createDirectHostExecutionContext({ repoRoot }),
76
+ boundOperation
63
77
  );
64
78
  process.stdout.write(envelope(result.status, result.changed, true, result, result.error));
65
79
  process.exitCode = exitCode(result.status);
@@ -3,7 +3,7 @@ import fs from 'node:fs';
3
3
  import { applyLedgerIntent } from '../task/ledger-intents.ts';
4
4
  import type { LedgerIntent } from '../task/ledger-intents.ts';
5
5
  import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.ts';
6
- import { isReviewStage, parseLedger, summarizeLedgerStage, validateLedgerRows } from '../task/ledger.ts';
6
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, isReviewStage, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from '../task/ledger.ts';
7
7
  import { resolveTaskRef } from '../task/resolve-ref.ts';
8
8
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
9
9
 
@@ -81,7 +81,13 @@ function taskLedger(args: string[] = []): void {
81
81
  return;
82
82
  }
83
83
  try {
84
- const rows = parseLedger(fs.readFileSync(resolved.taskMdPath, 'utf8'));
84
+ const ledger = parseLedgerDocument(fs.readFileSync(resolved.taskMdPath, 'utf8'));
85
+ if (!ledger.present) {
86
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, taskId: resolved.taskId, error: { code: LEDGER_SECTION_MISSING_CODE, message: LEDGER_SECTION_MISSING_MESSAGE } })}\n`);
87
+ process.exitCode = 1;
88
+ return;
89
+ }
90
+ const rows = ledger.rows;
85
91
  const invalid = validateLedgerRows(rows);
86
92
  if (invalid) {
87
93
  process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, taskId: resolved.taskId, error: invalid })}\n`);