@fitlab-ai/agent-infra 0.9.10 → 0.9.12

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 (375) hide show
  1. package/README.md +19 -9
  2. package/README.zh-CN.md +19 -9
  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 +71 -10
  8. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  9. package/dist/lib/agent-clients/adapters/traecli.js +7 -8
  10. package/dist/lib/agent-clients/config.js +14 -99
  11. package/dist/lib/agent-clients/reconcile.js +1 -11
  12. package/dist/lib/agent-clients/schema.js +5 -4
  13. package/dist/lib/agent-clients/tokens.js +5 -15
  14. package/dist/lib/decide.js +14 -30
  15. package/dist/lib/defaults.json +8 -0
  16. package/dist/lib/git/workflow.js +43 -1
  17. package/dist/lib/init.js +3 -2
  18. package/dist/lib/internal/git-workflow.js +8 -1
  19. package/dist/lib/internal/platform-pr.js +8 -2
  20. package/dist/lib/internal/release-workflow.js +0 -1
  21. package/dist/lib/internal/task-delivery.js +59 -0
  22. package/dist/lib/internal/task-finalization.js +19 -9
  23. package/dist/lib/internal/task-ledger.js +8 -2
  24. package/dist/lib/internal/task-lifecycle.js +17 -93
  25. package/dist/lib/internal/task-orchestration.js +30 -216
  26. package/dist/lib/internal/task-validate.js +2 -1
  27. package/dist/lib/merge.js +205 -103
  28. package/dist/lib/platform/github-client.js +7 -1
  29. package/dist/lib/platform/pr-checks.js +8 -5
  30. package/dist/lib/platform/pr-summary.js +12 -8
  31. package/dist/lib/platform/pull-requests.js +393 -91
  32. package/dist/lib/platform/verification-required.js +13 -2
  33. package/dist/lib/platform/verification-sync.js +9 -4
  34. package/dist/lib/pr-review/evidence-grading.js +12 -4
  35. package/dist/lib/process-data/normalize.js +2 -9
  36. package/dist/lib/run/host.js +46 -16
  37. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  38. package/dist/lib/sandbox/commands/create.js +3 -3
  39. package/dist/lib/sandbox/commands/enter.js +18 -4
  40. package/dist/lib/sandbox/config.js +8 -12
  41. package/dist/lib/sandbox/control/executor.js +131 -39
  42. package/dist/lib/sandbox/control/lifecycle.js +55 -24
  43. package/dist/lib/sandbox/control/server.js +18 -16
  44. package/dist/lib/sandbox/mounts.js +3 -2
  45. package/dist/lib/sandbox/recovery.js +123 -22
  46. package/dist/lib/sandbox/workspace-identity.js +14 -0
  47. package/dist/lib/sandbox/workspace-view.js +8 -9
  48. package/dist/lib/self-update.js +278 -0
  49. package/dist/lib/server/process-state.js +2 -2
  50. package/dist/lib/server/protocol.js +5 -5
  51. package/dist/lib/task/activity-log.js +4 -1
  52. package/dist/lib/task/artifact-lifecycle.js +6 -4
  53. package/dist/lib/task/codex-orchestration.js +1 -1
  54. package/dist/lib/task/command-options.js +1 -3
  55. package/dist/lib/task/commands/cat.js +0 -5
  56. package/dist/lib/task/commands/decisions.js +76 -92
  57. package/dist/lib/task/commands/files.js +6 -6
  58. package/dist/lib/task/commands/grep.js +25 -61
  59. package/dist/lib/task/commands/issue-body.js +6 -6
  60. package/dist/lib/task/commands/log.js +25 -10
  61. package/dist/lib/task/commands/show.js +5 -6
  62. package/dist/lib/task/commands/status.js +8 -8
  63. package/dist/lib/task/commit-intent.js +100 -0
  64. package/dist/lib/task/commit-operation.js +181 -91
  65. package/dist/lib/task/commit-policy.js +1 -1
  66. package/dist/lib/task/control-authority.js +431 -0
  67. package/dist/lib/task/create.js +39 -8
  68. package/dist/lib/task/current-contract.js +35 -0
  69. package/dist/lib/task/decision-details.js +218 -0
  70. package/dist/lib/task/decision-intents.js +5 -2
  71. package/dist/lib/task/delivery-target.js +97 -0
  72. package/dist/lib/task/delivery.js +153 -0
  73. package/dist/lib/task/events.js +11 -5
  74. package/dist/lib/task/human-override.js +5 -2
  75. package/dist/lib/task/index.js +19 -20
  76. package/dist/lib/task/ledger-intents.js +8 -9
  77. package/dist/lib/task/ledger.js +13 -6
  78. package/dist/lib/task/lifecycle.js +122 -9
  79. package/dist/lib/task/orchestration.js +105 -75
  80. package/dist/lib/task/pr-delivery-fact.js +182 -0
  81. package/dist/lib/task/review-artifacts.js +31 -12
  82. package/dist/lib/task/review-finalization.js +13 -4
  83. package/dist/lib/task/review-fingerprint.js +8 -0
  84. package/dist/lib/task/verification-engine.js +86 -39
  85. package/dist/lib/task/verification-review.js +2 -2
  86. package/dist/lib/task/workspace-snapshot.js +5 -11
  87. package/dist/lib/task/write.js +11 -1
  88. package/dist/lib/update.js +13 -35
  89. package/dist/lib/version.js +5 -1
  90. package/dist/lifecycle-build-manifest.json +3 -3
  91. package/lib/agent-client.ts +0 -1
  92. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  93. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  94. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  95. package/lib/agent-clients/adapters/traecli.ts +7 -8
  96. package/lib/agent-clients/config.ts +14 -144
  97. package/lib/agent-clients/reconcile.ts +1 -12
  98. package/lib/agent-clients/schema.ts +5 -4
  99. package/lib/agent-clients/tokens.ts +5 -18
  100. package/lib/decide.ts +12 -22
  101. package/lib/defaults.json +8 -0
  102. package/lib/git/workflow.ts +38 -1
  103. package/lib/init.ts +5 -2
  104. package/lib/internal/git-workflow.ts +7 -1
  105. package/lib/internal/platform-pr.ts +8 -1
  106. package/lib/internal/release-workflow.ts +0 -1
  107. package/lib/internal/task-delivery.ts +53 -0
  108. package/lib/internal/task-finalization.ts +27 -13
  109. package/lib/internal/task-ledger.ts +8 -2
  110. package/lib/internal/task-lifecycle.ts +33 -76
  111. package/lib/internal/task-orchestration.ts +33 -190
  112. package/lib/internal/task-validate.ts +3 -2
  113. package/lib/merge.ts +236 -120
  114. package/lib/platform/github-client.ts +8 -1
  115. package/lib/platform/pr-checks.ts +7 -5
  116. package/lib/platform/pr-summary.ts +10 -7
  117. package/lib/platform/pull-requests.ts +425 -87
  118. package/lib/platform/verification-required.ts +12 -2
  119. package/lib/platform/verification-sync.ts +9 -4
  120. package/lib/pr-review/evidence-grading.ts +7 -4
  121. package/lib/process-data/normalize.ts +2 -8
  122. package/lib/run/host.ts +63 -14
  123. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  124. package/lib/sandbox/commands/create.ts +4 -4
  125. package/lib/sandbox/commands/enter.ts +24 -4
  126. package/lib/sandbox/config.ts +9 -14
  127. package/lib/sandbox/control/container-identity.ts +2 -2
  128. package/lib/sandbox/control/executor.ts +148 -51
  129. package/lib/sandbox/control/lifecycle.ts +59 -31
  130. package/lib/sandbox/control/protocol.ts +1 -3
  131. package/lib/sandbox/control/server.ts +15 -12
  132. package/lib/sandbox/control/state.ts +7 -7
  133. package/lib/sandbox/mounts.ts +4 -3
  134. package/lib/sandbox/recovery.ts +162 -34
  135. package/lib/sandbox/tools.ts +1 -1
  136. package/lib/sandbox/workspace-identity.ts +28 -0
  137. package/lib/sandbox/workspace-view.ts +14 -10
  138. package/lib/self-update.ts +341 -0
  139. package/lib/server/process-state.ts +2 -2
  140. package/lib/server/protocol.ts +5 -5
  141. package/lib/task/activity-log.ts +6 -1
  142. package/lib/task/artifact-lifecycle.ts +7 -5
  143. package/lib/task/codex-orchestration.ts +2 -1
  144. package/lib/task/command-options.ts +1 -2
  145. package/lib/task/commands/cat.ts +0 -4
  146. package/lib/task/commands/decisions.ts +80 -75
  147. package/lib/task/commands/files.ts +6 -6
  148. package/lib/task/commands/grep.ts +23 -51
  149. package/lib/task/commands/issue-body.ts +6 -6
  150. package/lib/task/commands/log.ts +22 -10
  151. package/lib/task/commands/show.ts +5 -6
  152. package/lib/task/commands/status.ts +8 -8
  153. package/lib/task/commit-intent.ts +137 -0
  154. package/lib/task/commit-operation.ts +161 -79
  155. package/lib/task/commit-policy.ts +1 -2
  156. package/lib/task/control-authority.ts +563 -0
  157. package/lib/task/create.ts +40 -10
  158. package/lib/task/current-contract.ts +49 -0
  159. package/lib/task/decision-details.ts +280 -0
  160. package/lib/task/decision-intents.ts +7 -2
  161. package/lib/task/delivery-target.ts +130 -0
  162. package/lib/task/delivery.ts +160 -0
  163. package/lib/task/events.ts +10 -5
  164. package/lib/task/human-override.ts +5 -2
  165. package/lib/task/index.ts +19 -20
  166. package/lib/task/ledger-intents.ts +6 -8
  167. package/lib/task/ledger.ts +17 -7
  168. package/lib/task/lifecycle.ts +113 -4
  169. package/lib/task/orchestration.ts +102 -80
  170. package/lib/task/pr-delivery-fact.ts +254 -0
  171. package/lib/task/review-artifacts.ts +53 -15
  172. package/lib/task/review-finalization.ts +21 -5
  173. package/lib/task/review-fingerprint.ts +10 -0
  174. package/lib/task/verification-engine.ts +78 -46
  175. package/lib/task/verification-review.ts +3 -1
  176. package/lib/task/workspace-snapshot.ts +4 -13
  177. package/lib/task/write.ts +20 -1
  178. package/lib/update.ts +14 -38
  179. package/lib/version.ts +6 -1
  180. package/package.json +1 -1
  181. package/templates/.agents/README.en.md +5 -3
  182. package/templates/.agents/README.zh-CN.md +5 -3
  183. package/templates/.agents/rules/README.en.md +4 -2
  184. package/templates/.agents/rules/README.zh-CN.md +4 -2
  185. package/templates/.agents/rules/compatibility-policy.en.md +5 -4
  186. package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
  187. package/templates/.agents/rules/create-issue.en.md +11 -2
  188. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  189. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  191. package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
  192. package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
  193. package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
  194. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  195. package/templates/.agents/rules/issue-sync.en.md +26 -5
  196. package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
  197. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  198. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  199. package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
  200. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
  201. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  202. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  203. package/templates/.agents/rules/next-step-output.en.md +3 -3
  204. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  205. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  206. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  207. package/templates/.agents/rules/pr-sync.en.md +23 -14
  208. package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
  209. package/templates/.agents/rules/release-commands.en.md +42 -3
  210. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  211. package/templates/.agents/rules/review-handshake.en.md +1 -1
  212. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  213. package/templates/.agents/rules/security-alerts.en.md +17 -2
  214. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  215. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  216. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  217. package/templates/.agents/rules/task-management.en.md +2 -3
  218. package/templates/.agents/rules/task-management.zh-CN.md +2 -3
  219. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  220. package/templates/.agents/scripts/security-alerts.sh +31 -0
  221. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  223. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  224. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  226. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  227. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
  230. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
  231. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
  232. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
  233. package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
  234. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
  235. package/templates/.agents/skills/commit/SKILL.en.md +30 -10
  236. package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
  237. package/templates/.agents/skills/commit/config/verify.json +1 -1
  238. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  239. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  240. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  241. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  242. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  243. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  244. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
  245. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
  246. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  247. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  248. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  249. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  250. package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
  251. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
  252. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  253. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  254. package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
  255. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
  256. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
  257. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
  258. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  259. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  260. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  261. package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
  262. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
  263. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  264. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  265. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  266. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  267. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  268. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  269. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  270. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  271. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  272. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  273. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  274. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  275. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
  276. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
  277. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  278. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  279. package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
  280. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
  281. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  282. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  283. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  284. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  285. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  286. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  287. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  288. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  289. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
  290. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
  291. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  292. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  293. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  294. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  295. package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
  296. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
  297. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  298. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  299. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  300. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  301. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  302. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  303. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  304. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  305. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
  306. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
  307. package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
  308. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
  309. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
  312. package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
  313. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
  314. package/templates/.agents/templates/task.en.md +6 -2
  315. package/templates/.agents/templates/task.zh-CN.md +6 -2
  316. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  317. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  318. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  319. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  320. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  321. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  322. package/templates/.claude/commands/analyze-task.en.md +1 -1
  323. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  324. package/templates/.claude/commands/block-task.en.md +1 -1
  325. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  326. package/templates/.claude/commands/cancel-task.en.md +1 -1
  327. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  328. package/templates/.claude/commands/check-task.en.md +1 -1
  329. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  330. package/templates/.claude/commands/code-task.en.md +1 -1
  331. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  332. package/templates/.claude/commands/commit.en.md +1 -1
  333. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  334. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  335. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  336. package/templates/.claude/commands/complete-task.en.md +1 -1
  337. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  338. package/templates/.claude/commands/create-pr.en.md +1 -1
  339. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  340. package/templates/.claude/commands/plan-task.en.md +1 -1
  341. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  342. package/templates/.claude/commands/review-analysis.en.md +1 -1
  343. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  344. package/templates/.claude/commands/review-code.en.md +1 -1
  345. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  346. package/templates/.claude/commands/review-plan.en.md +1 -1
  347. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  348. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  349. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  350. package/templates/.claude/commands/run-task.en.md +1 -1
  351. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/watch-pr.en.md +1 -1
  353. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  354. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
  355. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
  356. package/dist/lib/builtin-tuis.js +0 -40
  357. package/lib/builtin-tuis.ts +0 -56
  358. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  359. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  360. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  361. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  362. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  363. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  364. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  365. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  366. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  367. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  368. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  369. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  370. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  371. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  372. package/templates/.agents/rules/release-commands.github.en.md +0 -38
  373. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
  374. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  375. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
@@ -1,6 +1,6 @@
1
- import { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, awaitOrchestrationDelegationActivation, beginOrResumeOrchestration, dispatchOrchestrationDelegation, OrchestrationStateError, pauseOrchestration, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration } from '../task/orchestration.js';
2
- import { prepareCodexOrchestrationDelegation } from '../task/codex-orchestration.js';
3
- import { isAgentClientId } from '../agent-clients/types.js';
1
+ import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../task/control-authority.js';
2
+ import { OrchestrationStateError } from '../task/orchestration.js';
3
+ import { detectRepoRoot } from '../task/resolve-ref.js';
4
4
  const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|dispatch|await-activation|recover-prepared|hook-start|hook-stop|advance|pause|status> [options]\n';
5
5
  function usageFailure(message) {
6
6
  process.stdout.write(`${JSON.stringify({
@@ -10,236 +10,50 @@ function usageFailure(message) {
10
10
  process.stderr.write(USAGE);
11
11
  process.exitCode = 2;
12
12
  }
13
+ function parseFailure(error) {
14
+ const message = error instanceof Error ? error.message : String(error);
15
+ return message.replace(/^TASK_CONTROL_OPERATION_INVALID: /u, '');
16
+ }
13
17
  async function taskOrchestration(args = []) {
14
18
  if (args[0] === '--help' || args[0] === '-h') {
15
19
  process.stdout.write(USAGE);
16
20
  return;
17
21
  }
18
- if (args.length < 2) {
19
- usageFailure('task ref and intent are required');
20
- return;
22
+ let operation;
23
+ try {
24
+ operation = parseTaskControlOperation('task-orchestration', args);
21
25
  }
22
- const [taskRef, intent] = args;
23
- if (![
24
- 'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
25
- 'hook-start', 'hook-stop', 'advance', 'pause', 'status'
26
- ].includes(intent)) {
27
- usageFailure(`unknown intent '${intent}'`);
26
+ catch (error) {
27
+ usageFailure(parseFailure(error));
28
28
  return;
29
29
  }
30
- const values = {};
31
- const seen = new Set();
32
- for (let index = 2; index < args.length; index += 1) {
33
- const flag = args[index];
34
- if (![
35
- '--max-steps', '--executor-model', '--executor-reasoning-effort',
36
- '--reviewer-model', '--reviewer-reasoning-effort',
37
- '--client', '--requested-model', '--requested-reasoning-effort', '--capability-token',
38
- '--parent-id', '--before-fingerprint',
39
- '--stage', '--round', '--artifact', '--role',
40
- '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
41
- '--model-fallback-reason', '--reasoning-effort-fallback-reason',
42
- '--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent',
43
- '--git-worktree-root'
44
- ].includes(flag)) {
45
- usageFailure(`unknown option '${flag}'`);
46
- return;
47
- }
48
- if (seen.has(flag)) {
49
- usageFailure(`duplicate option '${flag}'`);
50
- return;
51
- }
52
- seen.add(flag);
53
- const value = args[++index];
54
- if (!value || value.startsWith('--')) {
55
- usageFailure(`option '${flag}' requires a value`);
56
- return;
57
- }
58
- values[flag] = value;
59
- }
60
- const requireValues = (flags) => flags.find((flag) => values[flag] === undefined);
61
- const coreOptions = values['--git-worktree-root'] === undefined
62
- ? {}
63
- : { gitWorktreeRoot: values['--git-worktree-root'] };
64
- if (values['--client'] !== undefined && !isAgentClientId(values['--client'])) {
65
- usageFailure(`unknown client '${values['--client']}'`);
30
+ if (operation.family !== 'task-orchestration') {
31
+ usageFailure('orchestration operation is invalid');
66
32
  return;
67
33
  }
68
- let result;
34
+ let repoRoot = process.cwd();
69
35
  try {
70
- if (intent === 'begin-or-resume') {
71
- const missing = requireValues(['--client']);
72
- if (missing) {
73
- usageFailure(`intent 'begin-or-resume' requires '${missing}'`);
74
- return;
75
- }
76
- const maxSteps = values['--max-steps'] === undefined ? undefined : Number(values['--max-steps']);
77
- if (maxSteps !== undefined && (!Number.isInteger(maxSteps) || maxSteps < 1)) {
78
- usageFailure('--max-steps must be a positive integer');
79
- return;
80
- }
81
- const policyFlags = [
82
- '--executor-model', '--executor-reasoning-effort',
83
- '--reviewer-model', '--reviewer-reasoning-effort'
84
- ];
85
- const hasAnyPolicy = policyFlags.some((flag) => values[flag] !== undefined);
86
- if (hasAnyPolicy && policyFlags.some((flag) => values[flag] === undefined)) {
87
- usageFailure('explicit model policy requires executor/reviewer model and reasoning effort');
88
- return;
89
- }
90
- result = beginOrResumeOrchestration(taskRef, {
91
- ...coreOptions,
92
- maxSteps,
93
- client: values['--client'],
94
- modelPolicy: hasAnyPolicy ? {
95
- executor: {
96
- model: values['--executor-model'],
97
- reasoningEffort: values['--executor-reasoning-effort']
98
- },
99
- reviewer: {
100
- model: values['--reviewer-model'],
101
- reasoningEffort: values['--reviewer-reasoning-effort']
102
- }
103
- } : undefined
104
- });
105
- }
106
- else if (intent === 'route') {
107
- result = routeOrchestration(taskRef, coreOptions);
108
- }
109
- else if (intent === 'status') {
110
- result = statusOrchestration(taskRef, coreOptions);
111
- }
112
- else if (intent === 'prepare') {
113
- const missing = requireValues(['--client']);
114
- if (missing) {
115
- usageFailure(`intent 'prepare' requires '${missing}'`);
116
- return;
117
- }
118
- result = await prepareCodexOrchestrationDelegation(taskRef, {
119
- client: values['--client'],
120
- requestedModel: values['--requested-model'],
121
- requestedReasoningEffort: values['--requested-reasoning-effort'],
122
- capabilityToken: values['--capability-token']
123
- }, { orchestrationOptions: coreOptions });
124
- }
125
- else if (intent === 'dispatch') {
126
- result = dispatchOrchestrationDelegation(taskRef, coreOptions);
127
- }
128
- else if (intent === 'await-activation') {
129
- const missing = requireValues(['--stage', '--round', '--artifact', '--role']);
130
- if (missing) {
131
- usageFailure(`intent 'await-activation' requires '${missing}'`);
132
- return;
133
- }
134
- const round = Number(values['--round']);
135
- if (!Number.isSafeInteger(round) || round < 1) {
136
- usageFailure('--round must be a positive integer');
137
- return;
138
- }
139
- if (!['executor', 'reviewer'].includes(values['--role'])) {
140
- usageFailure('--role must be executor or reviewer');
141
- return;
142
- }
143
- result = await awaitOrchestrationDelegationActivation(taskRef, {
144
- stage: values['--stage'],
145
- round,
146
- artifact: values['--artifact'],
147
- role: values['--role']
148
- }, coreOptions);
149
- }
150
- else if (intent === 'recover-prepared') {
151
- result = recoverPreparedOrchestrationDelegation(taskRef, coreOptions);
152
- }
153
- else if (intent === 'hook-start') {
154
- const missing = requireValues([
155
- ...(taskRef === 'auto' ? ['--client'] : []),
156
- '--native-agent', '--child-id', '--parent-id'
157
- ]);
158
- if (missing) {
159
- usageFailure(`intent 'hook-start' requires '${missing}'`);
160
- return;
161
- }
162
- const event = {
163
- nativeAgent: values['--native-agent'], childId: values['--child-id'],
164
- parentId: values['--parent-id'], spawnMode: values['--spawn-mode'],
165
- actualModel: values['--actual-model'],
166
- actualReasoningEffort: values['--actual-reasoning-effort'],
167
- modelFallbackReason: values['--model-fallback-reason'],
168
- reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
169
- };
170
- result = taskRef === 'auto'
171
- ? activateMatchingOrchestrationDelegation(values['--client'], event, coreOptions)
172
- : activateOrchestrationDelegation(taskRef, event, coreOptions);
173
- }
174
- else if (intent === 'hook-stop') {
175
- if (taskRef === 'auto') {
176
- const missing = requireValues(['--client', '--native-agent', '--child-id']);
177
- if (missing) {
178
- usageFailure(`intent 'hook-stop' requires '${missing}'`);
179
- return;
180
- }
181
- result = sealMatchingOrchestrationDelegation(values['--client'], {
182
- nativeAgent: values['--native-agent'], childId: values['--child-id'],
183
- actualModel: values['--actual-model'],
184
- actualReasoningEffort: values['--actual-reasoning-effort'],
185
- modelFallbackReason: values['--model-fallback-reason'],
186
- reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
187
- }, coreOptions);
188
- }
189
- else {
190
- const missing = requireValues(['--child-id', '--exit-code', '--after-fingerprint']);
191
- if (missing) {
192
- usageFailure(`intent 'hook-stop' requires '${missing}'`);
193
- return;
194
- }
195
- const exitCode = Number(values['--exit-code']);
196
- if (!Number.isInteger(exitCode)) {
197
- usageFailure('--exit-code must be an integer');
198
- return;
199
- }
200
- result = sealOrchestrationDelegation(taskRef, {
201
- childId: values['--child-id'], exitCode,
202
- afterFingerprint: values['--after-fingerprint'],
203
- changedPaths: values['--changed-paths'] ? values['--changed-paths'].split(',').filter(Boolean) : [],
204
- actualModel: values['--actual-model'],
205
- actualReasoningEffort: values['--actual-reasoning-effort'],
206
- modelFallbackReason: values['--model-fallback-reason'],
207
- reasoningEffortFallbackReason: values['--reasoning-effort-fallback-reason']
208
- }, coreOptions);
209
- }
210
- }
211
- else if (intent === 'advance') {
212
- result = advanceOrchestration(taskRef, coreOptions);
213
- }
214
- else {
215
- const missing = requireValues(['--code', '--message', '--recoverable']);
216
- if (missing) {
217
- usageFailure(`intent 'pause' requires '${missing}'`);
218
- return;
219
- }
220
- if (!['true', 'false'].includes(values['--recoverable'])) {
221
- usageFailure('--recoverable must be true or false');
222
- return;
223
- }
224
- result = pauseOrchestration(taskRef, values['--code'], values['--message'], values['--recoverable'] === 'true', coreOptions);
225
- }
36
+ repoRoot = detectRepoRoot();
37
+ }
38
+ catch { /* domain resolution reports the repository error */ }
39
+ try {
40
+ const result = await dispatchTaskControlOperation(createDirectHostExecutionContext({
41
+ repoRoot,
42
+ ...(process.env.AGENT_INFRA_RUNTIME_DIR ? { runtimeDir: process.env.AGENT_INFRA_RUNTIME_DIR } : {})
43
+ }), operation);
44
+ process.stdout.write(`${JSON.stringify(result)}\n`);
45
+ if (result.status === 'failed')
46
+ process.exitCode = 1;
226
47
  }
227
48
  catch (error) {
228
49
  if (!(error instanceof OrchestrationStateError))
229
50
  throw error;
230
- result = {
231
- status: 'failed',
232
- changed: false,
233
- taskId: error.taskId,
234
- run: null,
235
- next: null,
51
+ process.stdout.write(`${JSON.stringify({
52
+ status: 'failed', changed: false, taskId: error.taskId, run: null, next: null,
236
53
  error: { code: error.code, message: error.message }
237
- };
238
- }
239
- const output = result;
240
- process.stdout.write(`${JSON.stringify(output)}\n`);
241
- if (result.status === 'failed')
54
+ })}\n`);
242
55
  process.exitCode = 1;
56
+ }
243
57
  }
244
58
  export { taskOrchestration };
245
59
  //# sourceMappingURL=task-orchestration.js.map
@@ -12,6 +12,7 @@ import { containerNameCandidates } from '../sandbox/constants.js';
12
12
  import { resolveSandboxTarget } from '../sandbox/workspace-identity.js';
13
13
  import { sandboxControlPaths } from '../sandbox/workspace-view.js';
14
14
  import { appendSandboxControlAudit, readActiveLease, readSandboxControlStatus } from '../sandbox/control/state.js';
15
+ import { readSandboxControlManifest } from '../sandbox/control/lifecycle.js';
15
16
  import { SANDBOX_CONTROL_FUTURE_SKEW_MS, SANDBOX_CONTROL_STATUS_STALE_MS } from '../sandbox/control/protocol.js';
16
17
  import { getProcessStartTime } from '../server/process-state.js';
17
18
  import { assertGitWorktreeBinding } from '../git/worktree-identity.js';
@@ -150,7 +151,7 @@ function inplaceValidation(config, target, options) {
150
151
  base: config.controlBase, project: config.project,
151
152
  container: row.name, identity: target.workspace
152
153
  });
153
- const manifest = JSON.parse(fs.readFileSync(control.manifestPath, 'utf8'));
154
+ const manifest = readSandboxControlManifest(control.manifestPath);
154
155
  const initial = readSandboxControlStatus(control.statusDir);
155
156
  const now = Date.now();
156
157
  if (initial.generation !== manifest.generation
package/dist/lib/merge.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { info, ok } from './log.js';
4
3
  import { removeDirRecursive } from './remove-dir.js';
4
+ import { validateCurrentTaskContract } from './task/current-contract.js';
5
5
  const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
6
6
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
7
7
  const TITLE_RE = /^# (.+)$/m;
8
- const DATE_FROM_PATH_RE = /(?:^|[/\\])(\d{4})[/\\](\d{2})[/\\](\d{2})(?:[/\\]|$)/;
9
8
  const MUTABLE_SECTIONS = ['active', 'blocked', 'completed'];
10
9
  const ALL_SECTIONS = [...MUTABLE_SECTIONS, 'archive'];
11
10
  const SECTION_LABELS = {
@@ -64,36 +63,6 @@ function normalizeTaskRecord(taskDir, taskFile, dateParts) {
64
63
  completedAt: taskDate
65
64
  };
66
65
  }
67
- function fallbackDateParts(taskDir, content) {
68
- const pathMatch = taskDir.match(DATE_FROM_PATH_RE);
69
- if (pathMatch) {
70
- const [, year, month, day] = pathMatch;
71
- if (!year || !month || !day) {
72
- return null;
73
- }
74
- return {
75
- year,
76
- month,
77
- day
78
- };
79
- }
80
- const completedAt = extractField(content, 'completed_at');
81
- const updatedAt = extractField(content, 'updated_at');
82
- const source = completedAt || updatedAt;
83
- const dateMatch = source?.match(/^(\d{4})-(\d{2})-(\d{2})/);
84
- if (dateMatch) {
85
- const [, year, month, day] = dateMatch;
86
- if (!year || !month || !day) {
87
- return null;
88
- }
89
- return {
90
- year,
91
- month,
92
- day
93
- };
94
- }
95
- return null;
96
- }
97
66
  function scanSourceTasks(sourceDir) {
98
67
  const tasks = [];
99
68
  const years = fs.existsSync(sourceDir) ? fs.readdirSync(sourceDir, { withFileTypes: true }) : [];
@@ -130,37 +99,6 @@ function scanSourceTasks(sourceDir) {
130
99
  }
131
100
  }
132
101
  }
133
- if (tasks.length > 0) {
134
- return tasks;
135
- }
136
- // Fall back to a deeper scan if the source layout is unusual but still contains archived tasks.
137
- const stack = [sourceDir];
138
- while (stack.length > 0) {
139
- const currentDir = stack.pop();
140
- if (!currentDir || !fs.existsSync(currentDir)) {
141
- continue;
142
- }
143
- for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
144
- const entryPath = path.join(currentDir, entry.name);
145
- if (!entry.isDirectory()) {
146
- continue;
147
- }
148
- if (TASK_ID_RE.test(entry.name)) {
149
- const taskFile = path.join(entryPath, 'task.md');
150
- if (!fs.existsSync(taskFile)) {
151
- continue;
152
- }
153
- const content = fs.readFileSync(taskFile, 'utf8');
154
- const dateParts = fallbackDateParts(entryPath, content);
155
- if (!dateParts) {
156
- continue;
157
- }
158
- tasks.push(normalizeTaskRecord(entryPath, taskFile, dateParts));
159
- continue;
160
- }
161
- stack.push(entryPath);
162
- }
163
- }
164
102
  return tasks.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
165
103
  }
166
104
  function findTaskDirById(rootDir, taskId) {
@@ -470,14 +408,200 @@ function copyTaskToSection(sourceTask, workspaceDir) {
470
408
  fs.cpSync(sourceTask.taskDir, destinationDir, { recursive: true });
471
409
  return destinationDir;
472
410
  }
411
+ function invalidSource(message) {
412
+ throw new Error(`Invalid merge source: ${message}`);
413
+ }
414
+ function sourceRelativePath(sourceRoot, targetPath) {
415
+ return path.relative(sourceRoot, targetPath).split(path.sep).join('/') || '.';
416
+ }
417
+ function isMissingPathError(error) {
418
+ return Boolean(error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT');
419
+ }
420
+ function assertSourcePathContained(sourceRoot, targetPath) {
421
+ let sourceRealPath;
422
+ let targetRealPath;
423
+ try {
424
+ sourceRealPath = fs.realpathSync(sourceRoot);
425
+ targetRealPath = fs.realpathSync(targetPath);
426
+ }
427
+ catch (error) {
428
+ invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} cannot be resolved: ${error instanceof Error ? error.message : String(error)}`);
429
+ }
430
+ const relativePath = path.relative(sourceRealPath, targetRealPath);
431
+ if (relativePath === '..' || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) {
432
+ invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} resolves outside the source workspace`);
433
+ }
434
+ }
435
+ function sourcePathStat(sourceRoot, targetPath) {
436
+ let stat;
437
+ try {
438
+ stat = fs.lstatSync(targetPath);
439
+ }
440
+ catch (error) {
441
+ if (isMissingPathError(error)) {
442
+ return null;
443
+ }
444
+ throw error;
445
+ }
446
+ if (stat.isSymbolicLink()) {
447
+ invalidSource(`${sourceRelativePath(sourceRoot, targetPath)} must not be a symbolic link`);
448
+ }
449
+ assertSourcePathContained(sourceRoot, targetPath);
450
+ return stat;
451
+ }
452
+ function validateTaskTree(sourceRoot, taskDir) {
453
+ for (const entry of fs.readdirSync(taskDir, { withFileTypes: true })) {
454
+ const entryPath = path.join(taskDir, entry.name);
455
+ const entryStat = sourcePathStat(sourceRoot, entryPath);
456
+ if (!entryStat) {
457
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} disappeared during validation`);
458
+ }
459
+ if (entryStat.isDirectory()) {
460
+ validateTaskTree(sourceRoot, entryPath);
461
+ continue;
462
+ }
463
+ if (!entryStat.isFile()) {
464
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} must be a regular file or directory`);
465
+ }
466
+ }
467
+ }
468
+ function validateSourceRoot(sourceRoot) {
469
+ const stat = sourcePathStat(sourceRoot, sourceRoot);
470
+ if (!stat || !stat.isDirectory()) {
471
+ invalidSource('source path must be a real directory');
472
+ }
473
+ }
474
+ function validateTaskDirectory(sourceRoot, taskDir, seenTaskIds) {
475
+ const taskId = path.basename(taskDir);
476
+ const taskFile = path.join(taskDir, 'task.md');
477
+ const taskDirStat = sourcePathStat(sourceRoot, taskDir);
478
+ if (!taskDirStat || !taskDirStat.isDirectory()) {
479
+ invalidSource(`${sourceRelativePath(sourceRoot, taskDir)} must be a directory`);
480
+ }
481
+ const taskFileStat = sourcePathStat(sourceRoot, taskFile);
482
+ if (!taskFileStat || !taskFileStat.isFile()) {
483
+ invalidSource(`${sourceRelativePath(sourceRoot, taskDir)} requires a regular task.md file`);
484
+ }
485
+ const content = fs.readFileSync(taskFile, 'utf8');
486
+ const contract = validateCurrentTaskContract(content);
487
+ if (!contract.ok) {
488
+ invalidSource(`${sourceRelativePath(sourceRoot, taskFile)} is not a valid current task: ${contract.message}`);
489
+ }
490
+ if (contract.metadata.id !== taskId) {
491
+ invalidSource(`${sourceRelativePath(sourceRoot, taskFile)} id '${String(contract.metadata.id ?? 'missing')}' does not match directory '${taskId}'`);
492
+ }
493
+ validateTaskTree(sourceRoot, taskDir);
494
+ const previousPath = seenTaskIds.get(taskId);
495
+ if (previousPath) {
496
+ invalidSource(`duplicate task ID ${taskId} at ${previousPath} and ${sourceRelativePath(sourceRoot, taskDir)}`);
497
+ }
498
+ seenTaskIds.set(taskId, sourceRelativePath(sourceRoot, taskDir));
499
+ }
500
+ function validateMutableSection(sourceRoot, section, seenTaskIds) {
501
+ const sectionDir = path.join(sourceRoot, section);
502
+ const sectionStat = sourcePathStat(sourceRoot, sectionDir);
503
+ if (!sectionStat) {
504
+ return;
505
+ }
506
+ if (!sectionStat.isDirectory()) {
507
+ invalidSource(`${section} must be a directory`);
508
+ }
509
+ for (const entry of fs.readdirSync(sectionDir, { withFileTypes: true })) {
510
+ const entryPath = path.join(sectionDir, entry.name);
511
+ const entryStat = sourcePathStat(sourceRoot, entryPath);
512
+ if (!entryStat) {
513
+ continue;
514
+ }
515
+ if (!entryStat.isDirectory()) {
516
+ if (TASK_ID_RE.test(entry.name)) {
517
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} is not a directory`);
518
+ }
519
+ continue;
520
+ }
521
+ if (!TASK_ID_RE.test(entry.name)) {
522
+ invalidSource(`${sourceRelativePath(sourceRoot, entryPath)} is not a direct TASK-YYYYMMDD-HHMMSS directory`);
523
+ }
524
+ validateTaskDirectory(sourceRoot, entryPath, seenTaskIds);
525
+ }
526
+ }
527
+ function validateArchiveSection(sourceRoot, seenTaskIds) {
528
+ const archiveDir = path.join(sourceRoot, 'archive');
529
+ const archiveStat = sourcePathStat(sourceRoot, archiveDir);
530
+ if (!archiveStat) {
531
+ return;
532
+ }
533
+ if (!archiveStat.isDirectory()) {
534
+ invalidSource('archive must be a directory');
535
+ }
536
+ const allowManifest = (directory, entry) => {
537
+ const entryPath = path.join(directory, entry.name);
538
+ const entryStat = sourcePathStat(sourceRoot, entryPath);
539
+ return Boolean(entryStat?.isFile() && entry.name === 'manifest.md');
540
+ };
541
+ const years = fs.readdirSync(archiveDir, { withFileTypes: true });
542
+ for (const yearEntry of years) {
543
+ const yearPath = path.join(archiveDir, yearEntry.name);
544
+ if (allowManifest(archiveDir, yearEntry)) {
545
+ continue;
546
+ }
547
+ const yearStat = sourcePathStat(sourceRoot, yearPath);
548
+ if (!yearStat?.isDirectory() || !/^\d{4}$/.test(yearEntry.name)) {
549
+ invalidSource(`${sourceRelativePath(sourceRoot, yearPath)} is not a YYYY directory or manifest.md`);
550
+ }
551
+ const yearDir = yearPath;
552
+ for (const monthEntry of fs.readdirSync(yearDir, { withFileTypes: true })) {
553
+ const monthPath = path.join(yearDir, monthEntry.name);
554
+ if (allowManifest(yearDir, monthEntry)) {
555
+ continue;
556
+ }
557
+ const monthStat = sourcePathStat(sourceRoot, monthPath);
558
+ if (!monthStat?.isDirectory() || !/^\d{2}$/.test(monthEntry.name)) {
559
+ invalidSource(`${sourceRelativePath(sourceRoot, monthPath)} is not a MM directory or manifest.md`);
560
+ }
561
+ const monthDir = monthPath;
562
+ for (const dayEntry of fs.readdirSync(monthDir, { withFileTypes: true })) {
563
+ const dayPath = path.join(monthDir, dayEntry.name);
564
+ if (allowManifest(monthDir, dayEntry)) {
565
+ continue;
566
+ }
567
+ const dayStat = sourcePathStat(sourceRoot, dayPath);
568
+ if (!dayStat?.isDirectory() || !/^\d{2}$/.test(dayEntry.name)) {
569
+ invalidSource(`${sourceRelativePath(sourceRoot, dayPath)} is not a DD directory or manifest.md`);
570
+ }
571
+ const dayDir = dayPath;
572
+ for (const taskEntry of fs.readdirSync(dayDir, { withFileTypes: true })) {
573
+ const taskPath = path.join(dayDir, taskEntry.name);
574
+ const taskStat = sourcePathStat(sourceRoot, taskPath);
575
+ if (!taskStat?.isDirectory() || !TASK_ID_RE.test(taskEntry.name)) {
576
+ invalidSource(`${sourceRelativePath(sourceRoot, taskPath)} is not a TASK-YYYYMMDD-HHMMSS directory`);
577
+ }
578
+ validateTaskDirectory(sourceRoot, taskPath, seenTaskIds);
579
+ }
580
+ }
581
+ }
582
+ }
583
+ }
584
+ function validateSourceWorkspace(sourcePath) {
585
+ validateSourceRoot(sourcePath);
586
+ const seenTaskIds = new Map();
587
+ for (const section of MUTABLE_SECTIONS) {
588
+ validateMutableSection(sourcePath, section, seenTaskIds);
589
+ }
590
+ validateArchiveSection(sourcePath, seenTaskIds);
591
+ }
473
592
  function detectSourceMode(sourcePath) {
593
+ validateSourceRoot(sourcePath);
474
594
  for (const section of ALL_SECTIONS) {
475
595
  const sectionDir = path.join(sourcePath, section);
476
- if (fs.existsSync(sectionDir) && fs.statSync(sectionDir).isDirectory()) {
596
+ const sectionStat = sourcePathStat(sourcePath, sectionDir);
597
+ if (sectionStat?.isDirectory()) {
477
598
  return 'workspace';
478
599
  }
600
+ if (sectionStat) {
601
+ invalidSource(`${section} must be a directory`);
602
+ }
479
603
  }
480
- return 'legacy-archive';
604
+ invalidSource('expected a current workspace containing active, blocked, completed, or archive sections');
481
605
  }
482
606
  function createReport(sourcePath, backupRoot) {
483
607
  return {
@@ -606,24 +730,6 @@ function mergeArchiveSection(sourceArchive, localArchive, report) {
606
730
  }
607
731
  return sourceTasks.length;
608
732
  }
609
- function printLegacyArchiveMessages(report, sourcePath) {
610
- const merged = report.sections.archive.copied;
611
- const skipped = report.sections.archive.skipped;
612
- if (merged.length === 0 && skipped.length === 0) {
613
- info(`No archived tasks found in ${sourcePath}`);
614
- }
615
- for (const task of merged) {
616
- ok(`Merged ${task.taskId} -> ${task.relativePath}`);
617
- }
618
- for (const task of skipped) {
619
- info(`Skipped ${task.taskId} (already exists at ${task.relativePath})`);
620
- }
621
- process.stdout.write('\n');
622
- info('Merge summary');
623
- info(`- Merged: ${merged.length}`);
624
- info(`- Skipped: ${skipped.length}`);
625
- process.stdout.write('\n');
626
- }
627
733
  function printSection(lines, name, counts) {
628
734
  const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (.agents/workspace/${name}/):`;
629
735
  lines.push(title);
@@ -709,7 +815,10 @@ async function cmdMerge(args) {
709
815
  if (!fs.existsSync(resolvedSource)) {
710
816
  throw new Error(`Source path does not exist: ${sourcePath}`);
711
817
  }
712
- if (!fs.statSync(resolvedSource).isDirectory()) {
818
+ if (fs.lstatSync(resolvedSource).isSymbolicLink()) {
819
+ throw new Error(`Invalid merge source: source path must be a real directory: ${sourcePath}`);
820
+ }
821
+ if (!fs.lstatSync(resolvedSource).isDirectory()) {
713
822
  throw new Error(`Source path is not a directory: ${sourcePath}`);
714
823
  }
715
824
  const workspaceDir = path.join(process.cwd(), '.agents', 'workspace');
@@ -718,30 +827,23 @@ async function cmdMerge(args) {
718
827
  const backupRootRelative = `.agents/workspace/.merge-backup/${backupStamp}/`;
719
828
  const backupRoot = path.join(workspaceDir, '.merge-backup', backupStamp);
720
829
  const report = createReport(resolvedSource, backupRootRelative);
721
- const mode = detectSourceMode(resolvedSource);
830
+ detectSourceMode(resolvedSource);
831
+ validateSourceWorkspace(resolvedSource);
722
832
  for (const section of ALL_SECTIONS) {
723
833
  fs.mkdirSync(path.join(workspaceDir, section), { recursive: true });
724
834
  }
725
- if (mode === 'legacy-archive') {
726
- info('Detected legacy archive source; treating the input as archive-only for backward compatibility.');
727
- mergeArchiveSection(resolvedSource, archiveDir, report);
728
- }
729
- else {
730
- mergeMutableSections({
731
- sourceWorkspace: resolvedSource,
732
- localWorkspace: workspaceDir,
733
- backupRoot,
734
- report
735
- });
736
- const sourceArchive = path.join(resolvedSource, 'archive');
737
- if (fs.existsSync(sourceArchive) && fs.statSync(sourceArchive).isDirectory()) {
738
- mergeArchiveSection(sourceArchive, archiveDir, report);
739
- }
835
+ mergeMutableSections({
836
+ sourceWorkspace: resolvedSource,
837
+ localWorkspace: workspaceDir,
838
+ backupRoot,
839
+ report
840
+ });
841
+ const sourceArchive = path.join(resolvedSource, 'archive');
842
+ const sourceArchiveStat = sourcePathStat(resolvedSource, sourceArchive);
843
+ if (sourceArchiveStat?.isDirectory()) {
844
+ mergeArchiveSection(sourceArchive, archiveDir, report);
740
845
  }
741
846
  rebuildManifests(archiveDir);
742
- if (mode === 'legacy-archive') {
743
- printLegacyArchiveMessages(report, sourcePath);
744
- }
745
847
  printReport(report);
746
848
  }
747
849
  export { cmdMerge, compareTimestamps, detectSourceMode, extractField, extractTitle, formatBackupTimestamp, getTaskTimestamp, rebuildManifests, scanSourceTasks, scanWorkspaceSection, taskExistsInArchive };
@@ -6,8 +6,14 @@ import semver from 'semver';
6
6
  const MINIMUM_GITHUB_CLI_VERSION = '2.72.0';
7
7
  const GITHUB_CLI_MAX_BUFFER = 64 * 1024 * 1024;
8
8
  const ERROR_DETAIL_LIMIT = 4096;
9
+ function redactDiagnostic(value) {
10
+ return value
11
+ .replace(/\b(?:gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+)\b/g, '[REDACTED_TOKEN]')
12
+ .replace(/\b(bearer|token)\s+[A-Za-z0-9._~+\/-]+/gi, '$1 [REDACTED_TOKEN]')
13
+ .replace(/([?&](?:access_token|token|client_secret|password)=)[^&\s]+/gi, '$1[REDACTED]');
14
+ }
9
15
  function boundedFailureDetail(result) {
10
- const diagnostic = `${result.stderr}\n${result.error?.message || ''}`.trim() || result.stdout.trim();
16
+ const diagnostic = redactDiagnostic(`${result.stderr}\n${result.error?.message || ''}`).trim() || redactDiagnostic(result.stdout.trim());
11
17
  return diagnostic.length <= ERROR_DETAIL_LIMIT
12
18
  ? diagnostic
13
19
  : `${diagnostic.slice(0, ERROR_DETAIL_LIMIT)}… [truncated]`;