agent-control-plane 0.1.0

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 (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +589 -0
  3. package/SKILL.md +149 -0
  4. package/assets/workflow-catalog.json +57 -0
  5. package/bin/audit-issue-routing.sh +74 -0
  6. package/bin/issue-resource-class.sh +58 -0
  7. package/bin/label-follow-up-issues.sh +114 -0
  8. package/bin/pr-risk.sh +532 -0
  9. package/bin/sync-pr-labels.sh +112 -0
  10. package/hooks/heartbeat-hooks.sh +573 -0
  11. package/hooks/issue-reconcile-hooks.sh +217 -0
  12. package/hooks/pr-reconcile-hooks.sh +225 -0
  13. package/npm/bin/agent-control-plane.js +1984 -0
  14. package/npm/public-bin/agent-control-plane +3 -0
  15. package/package.json +61 -0
  16. package/tools/bin/agent-cleanup-worktree +247 -0
  17. package/tools/bin/agent-github-update-labels +66 -0
  18. package/tools/bin/agent-init-worktree +216 -0
  19. package/tools/bin/agent-project-archive-run +52 -0
  20. package/tools/bin/agent-project-capture-worker +46 -0
  21. package/tools/bin/agent-project-catch-up-merged-prs +137 -0
  22. package/tools/bin/agent-project-cleanup-session +244 -0
  23. package/tools/bin/agent-project-detached-launch +107 -0
  24. package/tools/bin/agent-project-heartbeat-loop +2347 -0
  25. package/tools/bin/agent-project-open-issue-worktree +89 -0
  26. package/tools/bin/agent-project-open-pr-worktree +80 -0
  27. package/tools/bin/agent-project-publish-issue-pr +349 -0
  28. package/tools/bin/agent-project-reconcile-issue-session +1128 -0
  29. package/tools/bin/agent-project-reconcile-pr-session +1005 -0
  30. package/tools/bin/agent-project-retry-state +147 -0
  31. package/tools/bin/agent-project-run-claude-session +657 -0
  32. package/tools/bin/agent-project-run-codex-resilient +718 -0
  33. package/tools/bin/agent-project-run-codex-session +316 -0
  34. package/tools/bin/agent-project-run-kilo-session +27 -0
  35. package/tools/bin/agent-project-run-openclaw-session +984 -0
  36. package/tools/bin/agent-project-run-opencode-session +27 -0
  37. package/tools/bin/agent-project-sync-anchor-repo +128 -0
  38. package/tools/bin/agent-project-worker-status +143 -0
  39. package/tools/bin/audit-agent-worktrees.sh +310 -0
  40. package/tools/bin/audit-issue-routing.sh +11 -0
  41. package/tools/bin/audit-retained-layout.sh +58 -0
  42. package/tools/bin/audit-retained-overlap.sh +135 -0
  43. package/tools/bin/audit-retained-worktrees.sh +228 -0
  44. package/tools/bin/branch-verification-guard.sh +351 -0
  45. package/tools/bin/capture-worker.sh +18 -0
  46. package/tools/bin/check-skill-contracts.sh +324 -0
  47. package/tools/bin/cleanup-worktree.sh +44 -0
  48. package/tools/bin/codex-quota +31 -0
  49. package/tools/bin/create-follow-up-issue.sh +114 -0
  50. package/tools/bin/dashboard-launchd-bootstrap.sh +38 -0
  51. package/tools/bin/flow-config-lib.sh +2127 -0
  52. package/tools/bin/flow-resident-worker-lib.sh +683 -0
  53. package/tools/bin/flow-runtime-doctor.sh +97 -0
  54. package/tools/bin/flow-shell-lib.sh +266 -0
  55. package/tools/bin/heartbeat-recovery-preflight.sh +106 -0
  56. package/tools/bin/heartbeat-safe-auto.sh +551 -0
  57. package/tools/bin/install-dashboard-launchd.sh +152 -0
  58. package/tools/bin/install-project-launchd.sh +219 -0
  59. package/tools/bin/issue-publish-scope-guard.sh +242 -0
  60. package/tools/bin/issue-requires-local-workspace-install.sh +31 -0
  61. package/tools/bin/issue-resource-class.sh +12 -0
  62. package/tools/bin/kick-scheduler.sh +75 -0
  63. package/tools/bin/label-follow-up-issues.sh +14 -0
  64. package/tools/bin/new-pr-worktree.sh +50 -0
  65. package/tools/bin/new-worktree.sh +49 -0
  66. package/tools/bin/pr-risk.sh +12 -0
  67. package/tools/bin/prepare-worktree.sh +140 -0
  68. package/tools/bin/profile-activate.sh +109 -0
  69. package/tools/bin/profile-adopt.sh +219 -0
  70. package/tools/bin/profile-smoke.sh +461 -0
  71. package/tools/bin/project-init.sh +189 -0
  72. package/tools/bin/project-launchd-bootstrap.sh +54 -0
  73. package/tools/bin/project-remove.sh +155 -0
  74. package/tools/bin/project-runtime-supervisor.sh +56 -0
  75. package/tools/bin/project-runtimectl.sh +586 -0
  76. package/tools/bin/provider-cooldown-state.sh +166 -0
  77. package/tools/bin/publish-issue-worker.sh +31 -0
  78. package/tools/bin/reconcile-issue-worker.sh +34 -0
  79. package/tools/bin/reconcile-pr-worker.sh +34 -0
  80. package/tools/bin/record-verification.sh +71 -0
  81. package/tools/bin/render-architecture-infographics.sh +110 -0
  82. package/tools/bin/render-dashboard-demo-media.sh +333 -0
  83. package/tools/bin/render-dashboard-snapshot.py +16 -0
  84. package/tools/bin/render-flow-config.sh +86 -0
  85. package/tools/bin/retry-state.sh +31 -0
  86. package/tools/bin/reuse-issue-worktree.sh +75 -0
  87. package/tools/bin/run-codex-bypass.sh +3 -0
  88. package/tools/bin/run-codex-safe.sh +3 -0
  89. package/tools/bin/run-codex-task.sh +231 -0
  90. package/tools/bin/scaffold-profile.sh +374 -0
  91. package/tools/bin/serve-dashboard.sh +5 -0
  92. package/tools/bin/split-retained-slice.sh +124 -0
  93. package/tools/bin/start-issue-worker.sh +796 -0
  94. package/tools/bin/start-pr-fix-worker.sh +458 -0
  95. package/tools/bin/start-pr-merge-repair-worker.sh +8 -0
  96. package/tools/bin/start-pr-review-worker.sh +227 -0
  97. package/tools/bin/start-resident-issue-loop.sh +908 -0
  98. package/tools/bin/sync-agent-repo.sh +52 -0
  99. package/tools/bin/sync-dependency-baseline.sh +247 -0
  100. package/tools/bin/sync-pr-labels.sh +12 -0
  101. package/tools/bin/sync-recurring-issue-checklist.sh +274 -0
  102. package/tools/bin/sync-shared-agent-home.sh +214 -0
  103. package/tools/bin/sync-vscode-workspace.sh +157 -0
  104. package/tools/bin/test-smoke.sh +63 -0
  105. package/tools/bin/uninstall-project-launchd.sh +55 -0
  106. package/tools/bin/update-github-labels.sh +14 -0
  107. package/tools/bin/worker-status.sh +19 -0
  108. package/tools/bin/workflow-catalog.sh +77 -0
  109. package/tools/dashboard/app.js +286 -0
  110. package/tools/dashboard/dashboard_snapshot.py +466 -0
  111. package/tools/dashboard/index.html +41 -0
  112. package/tools/dashboard/server.py +64 -0
  113. package/tools/dashboard/styles.css +351 -0
  114. package/tools/templates/issue-prompt-template.md +109 -0
  115. package/tools/templates/pr-fix-template.md +120 -0
  116. package/tools/templates/pr-merge-repair-template.md +91 -0
  117. package/tools/templates/pr-review-template.md +62 -0
  118. package/tools/templates/scheduled-issue-prompt-template.md +62 -0
  119. package/tools/tests/test-agent-control-plane-npm-cli.sh +279 -0
  120. package/tools/tests/test-agent-github-update-labels-falls-back-to-repository-id.sh +56 -0
  121. package/tools/tests/test-agent-project-claude-session-wrapper-clears-stale-sandbox-artifacts.sh +89 -0
  122. package/tools/tests/test-agent-project-claude-session-wrapper-does-not-retry-provider-quota.sh +82 -0
  123. package/tools/tests/test-agent-project-claude-session-wrapper-retries-transient-failures.sh +90 -0
  124. package/tools/tests/test-agent-project-claude-session-wrapper-times-out.sh +73 -0
  125. package/tools/tests/test-agent-project-claude-session-wrapper.sh +103 -0
  126. package/tools/tests/test-agent-project-cleanup-session-orphan-fallback.sh +90 -0
  127. package/tools/tests/test-agent-project-cleanup-session-skip-worktree-cleanup.sh +90 -0
  128. package/tools/tests/test-agent-project-codex-live-thread-persist.sh +76 -0
  129. package/tools/tests/test-agent-project-codex-recovery.sh +731 -0
  130. package/tools/tests/test-agent-project-codex-session-wrapper-clears-stale-sandbox-artifacts.sh +105 -0
  131. package/tools/tests/test-agent-project-codex-session-wrapper.sh +97 -0
  132. package/tools/tests/test-agent-project-open-pr-worktree-config-prefix.sh +81 -0
  133. package/tools/tests/test-agent-project-openclaw-session-wrapper-clears-stale-sandbox-artifacts.sh +109 -0
  134. package/tools/tests/test-agent-project-openclaw-session-wrapper-infers-blocked-result-contract.sh +89 -0
  135. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-literal-env-artifacts.sh +113 -0
  136. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-version-mismatch.sh +135 -0
  137. package/tools/tests/test-agent-project-openclaw-session-wrapper-resident.sh +179 -0
  138. package/tools/tests/test-agent-project-openclaw-session-wrapper-reuses-existing-agent-after-add-race.sh +119 -0
  139. package/tools/tests/test-agent-project-openclaw-session-wrapper-terminates-rate-limit-hang.sh +91 -0
  140. package/tools/tests/test-agent-project-openclaw-session-wrapper.sh +117 -0
  141. package/tools/tests/test-agent-project-publish-issue-pr-prunes-stale-worktree-entry.sh +148 -0
  142. package/tools/tests/test-agent-project-publish-issue-pr-reads-archived-session.sh +146 -0
  143. package/tools/tests/test-agent-project-publish-issue-pr-recovers-final-head.sh +145 -0
  144. package/tools/tests/test-agent-project-publish-issue-pr-reuses-existing-worktree.sh +147 -0
  145. package/tools/tests/test-agent-project-reconcile-failure-reason.sh +456 -0
  146. package/tools/tests/test-agent-project-reconcile-issue-archived-session-fallback.sh +96 -0
  147. package/tools/tests/test-agent-project-reconcile-issue-before-blocked.sh +90 -0
  148. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery-uses-recovered-worktree.sh +212 -0
  149. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery.sh +207 -0
  150. package/tools/tests/test-agent-project-reconcile-issue-provider-quota-schedules-provider-cooldown.sh +101 -0
  151. package/tools/tests/test-agent-project-reconcile-issue-session-backfills-lane-metadata-from-worker-key.sh +113 -0
  152. package/tools/tests/test-agent-project-reconcile-issue-session-clears-stale-failed-summary.sh +117 -0
  153. package/tools/tests/test-agent-project-reconcile-issue-session-initializes-shared-agent-home.sh +55 -0
  154. package/tools/tests/test-agent-project-reconcile-issue-session-normalizes-runner-state.sh +125 -0
  155. package/tools/tests/test-agent-project-reconcile-issue-session-records-invalid-contract-summary.sh +118 -0
  156. package/tools/tests/test-agent-project-reconcile-issue-session-skips-duplicate-blocked-comment.sh +144 -0
  157. package/tools/tests/test-agent-project-reconcile-issue-session-standardizes-no-commits-blocker.sh +145 -0
  158. package/tools/tests/test-agent-project-reconcile-issue-session-synthesizes-blocked-comment.sh +139 -0
  159. package/tools/tests/test-agent-project-reconcile-pr-blocked-host-recovery.sh +242 -0
  160. package/tools/tests/test-agent-project-reconcile-pr-guard-blocked-no-commit.sh +142 -0
  161. package/tools/tests/test-agent-project-reconcile-pr-provider-quota-schedules-provider-cooldown.sh +106 -0
  162. package/tools/tests/test-agent-project-reconcile-pr-session-initializes-shared-agent-home.sh +66 -0
  163. package/tools/tests/test-agent-project-reconcile-pr-updated-branch-noop.sh +129 -0
  164. package/tools/tests/test-audit-agent-worktrees-active-launch-skips-git-inspection.sh +69 -0
  165. package/tools/tests/test-audit-agent-worktrees-broken-worktree.sh +43 -0
  166. package/tools/tests/test-audit-agent-worktrees-pending-launch-owner.sh +46 -0
  167. package/tools/tests/test-audit-agent-worktrees-unreconciled-owner.sh +79 -0
  168. package/tools/tests/test-audit-issue-routing-managed-branch-globs.sh +56 -0
  169. package/tools/tests/test-branch-verification-guard-generated-artifacts.sh +72 -0
  170. package/tools/tests/test-branch-verification-guard-targeted-coverage.sh +125 -0
  171. package/tools/tests/test-codex-quota-manager-failure-driven-rotation.sh +178 -0
  172. package/tools/tests/test-codex-quota-wrapper.sh +37 -0
  173. package/tools/tests/test-contribution-docs.sh +18 -0
  174. package/tools/tests/test-control-plane-dashboard-runtime-smoke.sh +343 -0
  175. package/tools/tests/test-create-follow-up-issue.sh +73 -0
  176. package/tools/tests/test-dashboard-launchd-bootstrap.sh +55 -0
  177. package/tools/tests/test-flow-export-execution-env-exports-repo-id.sh +30 -0
  178. package/tools/tests/test-flow-export-github-cli-auth-env-prefers-git-credential.sh +48 -0
  179. package/tools/tests/test-flow-github-api-repo-fallback-preserves-input.sh +85 -0
  180. package/tools/tests/test-flow-github-api-repo-prefers-explicit-repository-id.sh +60 -0
  181. package/tools/tests/test-flow-github-issue-list-falls-back-to-repository-id.sh +64 -0
  182. package/tools/tests/test-flow-github-pr-list-falls-back-to-repository-id.sh +77 -0
  183. package/tools/tests/test-flow-resident-can-reuse-does-not-leak-metadata.sh +52 -0
  184. package/tools/tests/test-flow-resident-reap-stale-controllers.sh +63 -0
  185. package/tools/tests/test-flow-resolve-codex-quota-tools.sh +104 -0
  186. package/tools/tests/test-flow-runtime-doctor-profile-selection.sh +27 -0
  187. package/tools/tests/test-heartbeat-codex-pr-linked-issue-exclusion.sh +79 -0
  188. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-idle-controller.sh +115 -0
  189. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-live-lane-controller.sh +117 -0
  190. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-claude.sh +96 -0
  191. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-codex.sh +96 -0
  192. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop.sh +96 -0
  193. package/tools/tests/test-heartbeat-loop-auth-wait-does-not-consume-capacity.sh +170 -0
  194. package/tools/tests/test-heartbeat-loop-blocked-recovery-lane.sh +201 -0
  195. package/tools/tests/test-heartbeat-loop-blocked-recovery-vs-pr-reservation.sh +201 -0
  196. package/tools/tests/test-heartbeat-loop-idle-resident-controller-does-not-block-launches.sh +160 -0
  197. package/tools/tests/test-heartbeat-loop-pr-launch-dedup.sh +133 -0
  198. package/tools/tests/test-heartbeat-loop-provider-cooldown-suppresses-launches.sh +157 -0
  199. package/tools/tests/test-heartbeat-loop-reaps-stale-resident-controller.sh +181 -0
  200. package/tools/tests/test-heartbeat-loop-waiting-provider-resident-controller-does-not-block-launches.sh +160 -0
  201. package/tools/tests/test-heartbeat-ready-issues-blocked-recovery.sh +134 -0
  202. package/tools/tests/test-heartbeat-safe-auto-dynamic-concurrency.sh +162 -0
  203. package/tools/tests/test-heartbeat-safe-auto-no-tmux-sessions.sh +136 -0
  204. package/tools/tests/test-heartbeat-safe-auto-openclaw-skips-codex-quota.sh +139 -0
  205. package/tools/tests/test-heartbeat-safe-auto-quota-health-signal.sh +119 -0
  206. package/tools/tests/test-heartbeat-safe-auto-stale-shared-loop-pid-does-not-skip.sh +140 -0
  207. package/tools/tests/test-heartbeat-safe-auto-static-capacity-without-quota-cache.sh +142 -0
  208. package/tools/tests/test-heartbeat-safe-auto-zero-healthy-pools.sh +141 -0
  209. package/tools/tests/test-heartbeat-sync-issue-labels-empty-schedule.sh +65 -0
  210. package/tools/tests/test-heartbeat-sync-open-agent-prs-terminal-clears-running.sh +179 -0
  211. package/tools/tests/test-install-dashboard-launchd.sh +78 -0
  212. package/tools/tests/test-install-project-launchd-adds-tool-paths.sh +87 -0
  213. package/tools/tests/test-install-project-launchd.sh +110 -0
  214. package/tools/tests/test-issue-local-workspace-install-policy.sh +81 -0
  215. package/tools/tests/test-issue-publish-scope-guard-docs-signal.sh +70 -0
  216. package/tools/tests/test-issue-reconcile-hooks-success-clears-blocked.sh +36 -0
  217. package/tools/tests/test-kick-scheduler-requires-explicit-profile.sh +47 -0
  218. package/tools/tests/test-label-follow-up-issues-falls-back-to-repository-id.sh +132 -0
  219. package/tools/tests/test-manual-operator-entrypoints-require-explicit-profile.sh +64 -0
  220. package/tools/tests/test-package-funding-metadata.sh +21 -0
  221. package/tools/tests/test-package-public-metadata.sh +62 -0
  222. package/tools/tests/test-placeholder-worker-adapters.sh +38 -0
  223. package/tools/tests/test-pr-reconcile-hooks-refreshes-recurring-issue-checklist.sh +110 -0
  224. package/tools/tests/test-pr-risk-cohesive-mobile-locale-scope.sh +70 -0
  225. package/tools/tests/test-pr-risk-fix-label-semantics.sh +114 -0
  226. package/tools/tests/test-pr-risk-local-first-no-checks.sh +70 -0
  227. package/tools/tests/test-prepare-worktree-simple-repo-baseline.sh +67 -0
  228. package/tools/tests/test-profile-activate.sh +33 -0
  229. package/tools/tests/test-profile-adopt-allow-missing-repo.sh +68 -0
  230. package/tools/tests/test-profile-adopt-skip-workspace-sync-missing-file.sh +61 -0
  231. package/tools/tests/test-profile-adopt-syncs-anchor-and-workspace.sh +90 -0
  232. package/tools/tests/test-profile-smoke-collision.sh +44 -0
  233. package/tools/tests/test-profile-smoke-invalid-claude-config.sh +31 -0
  234. package/tools/tests/test-profile-smoke-invalid-provider-pool.sh +68 -0
  235. package/tools/tests/test-profile-smoke-repo-slug-mismatch.sh +36 -0
  236. package/tools/tests/test-profile-smoke.sh +45 -0
  237. package/tools/tests/test-project-init-force-and-skip-sync.sh +61 -0
  238. package/tools/tests/test-project-init-repo-slug-mismatch.sh +29 -0
  239. package/tools/tests/test-project-init.sh +66 -0
  240. package/tools/tests/test-project-launchd-bootstrap.sh +66 -0
  241. package/tools/tests/test-project-remove.sh +150 -0
  242. package/tools/tests/test-project-runtime-supervisor.sh +47 -0
  243. package/tools/tests/test-project-runtimectl-launchd.sh +115 -0
  244. package/tools/tests/test-project-runtimectl-missing-profile.sh +54 -0
  245. package/tools/tests/test-project-runtimectl-start-falls-back-to-bootstrap.sh +108 -0
  246. package/tools/tests/test-project-runtimectl-status-reports-supervisor-as-heartbeat-parent.sh +95 -0
  247. package/tools/tests/test-project-runtimectl-status-supervisor-running.sh +59 -0
  248. package/tools/tests/test-project-runtimectl-stop-cancels-pending-kick.sh +85 -0
  249. package/tools/tests/test-project-runtimectl-stop-clears-running-labels.sh +78 -0
  250. package/tools/tests/test-project-runtimectl.sh +212 -0
  251. package/tools/tests/test-provider-cooldown-state-prefers-runtime-worker-context.sh +39 -0
  252. package/tools/tests/test-provider-cooldown-state.sh +59 -0
  253. package/tools/tests/test-public-repo-docs.sh +159 -0
  254. package/tools/tests/test-reconcile-pr-worker-acp-config-routing.sh +75 -0
  255. package/tools/tests/test-render-dashboard-snapshot.sh +149 -0
  256. package/tools/tests/test-render-flow-config-demo-profile.sh +36 -0
  257. package/tools/tests/test-render-flow-config-provider-pool-fallback.sh +81 -0
  258. package/tools/tests/test-render-flow-config.sh +52 -0
  259. package/tools/tests/test-run-codex-task-claude-routing.sh +125 -0
  260. package/tools/tests/test-run-codex-task-codex-resident-routing.sh +108 -0
  261. package/tools/tests/test-run-codex-task-kilo-routing.sh +98 -0
  262. package/tools/tests/test-run-codex-task-openclaw-resident-routing.sh +117 -0
  263. package/tools/tests/test-run-codex-task-openclaw-routing.sh +113 -0
  264. package/tools/tests/test-run-codex-task-opencode-routing.sh +98 -0
  265. package/tools/tests/test-run-codex-task-provider-pool-fallback-routing.sh +146 -0
  266. package/tools/tests/test-scaffold-profile.sh +108 -0
  267. package/tools/tests/test-serve-dashboard.sh +93 -0
  268. package/tools/tests/test-start-issue-worker-blocked-context.sh +129 -0
  269. package/tools/tests/test-start-issue-worker-blocks-complete-recurring-checklist.sh +189 -0
  270. package/tools/tests/test-start-issue-worker-local-install-routing.sh +157 -0
  271. package/tools/tests/test-start-issue-worker-profile-template-routing.sh +149 -0
  272. package/tools/tests/test-start-issue-worker-recurring-resident-reuse-codex.sh +212 -0
  273. package/tools/tests/test-start-issue-worker-recurring-resident-reuse.sh +219 -0
  274. package/tools/tests/test-start-issue-worker-renders-verification-snippet.sh +155 -0
  275. package/tools/tests/test-start-issue-worker-resident-reuse-falls-back-to-new-worktree.sh +199 -0
  276. package/tools/tests/test-start-pr-fix-worker-host-blocker-context.sh +275 -0
  277. package/tools/tests/test-start-resident-issue-loop-adopts-next-recurring-issue.sh +185 -0
  278. package/tools/tests/test-start-resident-issue-loop-clears-pending-while-waiting-due.sh +152 -0
  279. package/tools/tests/test-start-resident-issue-loop-consumes-queued-lease.sh +186 -0
  280. package/tools/tests/test-start-resident-issue-loop-fails-over-provider-pool.sh +212 -0
  281. package/tools/tests/test-start-resident-issue-loop-immediate-cycles.sh +148 -0
  282. package/tools/tests/test-start-resident-issue-loop-waits-for-provider.sh +194 -0
  283. package/tools/tests/test-start-resident-issue-loop-waits-for-terminal-reconcile-status.sh +198 -0
  284. package/tools/tests/test-start-resident-issue-loop-yields-to-live-lane-controller.sh +145 -0
  285. package/tools/tests/test-sync-pr-labels-fix-lane-uses-repair-queued.sh +67 -0
  286. package/tools/tests/test-sync-recurring-issue-checklist-backfills-workflow-complete-blocker.sh +70 -0
  287. package/tools/tests/test-sync-recurring-issue-checklist.sh +95 -0
  288. package/tools/tests/test-sync-shared-agent-home-local-source-root.sh +66 -0
  289. package/tools/tests/test-sync-shared-agent-home-preserves-unrelated-workflow-catalog-skill.sh +47 -0
  290. package/tools/tests/test-test-smoke.sh +86 -0
  291. package/tools/tests/test-uninstall-project-launchd.sh +37 -0
  292. package/tools/tests/test-update-github-labels-prefers-sibling-helper.sh +49 -0
  293. package/tools/tests/test-workflow-catalog.sh +43 -0
  294. package/tools/vendor/codex-quota/LICENSE +21 -0
  295. package/tools/vendor/codex-quota/README.md +459 -0
  296. package/tools/vendor/codex-quota/codex-quota.js +261 -0
  297. package/tools/vendor/codex-quota/lib/claude-accounts.js +226 -0
  298. package/tools/vendor/codex-quota/lib/claude-oauth.js +174 -0
  299. package/tools/vendor/codex-quota/lib/claude-tokens.js +471 -0
  300. package/tools/vendor/codex-quota/lib/claude-usage.js +929 -0
  301. package/tools/vendor/codex-quota/lib/codex-accounts.js +205 -0
  302. package/tools/vendor/codex-quota/lib/codex-tokens.js +326 -0
  303. package/tools/vendor/codex-quota/lib/codex-usage.js +32 -0
  304. package/tools/vendor/codex-quota/lib/color.js +72 -0
  305. package/tools/vendor/codex-quota/lib/constants.js +57 -0
  306. package/tools/vendor/codex-quota/lib/container.js +143 -0
  307. package/tools/vendor/codex-quota/lib/display.js +1111 -0
  308. package/tools/vendor/codex-quota/lib/fs.js +63 -0
  309. package/tools/vendor/codex-quota/lib/handlers.js +2060 -0
  310. package/tools/vendor/codex-quota/lib/jwt.js +33 -0
  311. package/tools/vendor/codex-quota/lib/oauth.js +486 -0
  312. package/tools/vendor/codex-quota/lib/paths.js +34 -0
  313. package/tools/vendor/codex-quota/lib/prompts.js +44 -0
  314. package/tools/vendor/codex-quota/lib/sync.js +1438 -0
  315. package/tools/vendor/codex-quota/lib/token-match.js +96 -0
  316. package/tools/vendor/codex-quota-manager/scripts/auto-switch.sh +500 -0
  317. package/tools/vendor/codex-quota-manager/scripts/batch-add.sh +123 -0
@@ -0,0 +1,731 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ FLOW_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
+ HELPER_BIN="${FLOW_ROOT}/tools/bin/agent-project-run-codex-resilient"
6
+ STATUS_BIN="${FLOW_ROOT}/tools/bin/agent-project-worker-status"
7
+
8
+ tmpdir="$(mktemp -d)"
9
+ trap 'rm -rf "$tmpdir"' EXIT
10
+
11
+ create_mock_runtime() {
12
+ local case_dir="${1:?case dir required}"
13
+ local state_dir="${case_dir}/state"
14
+ local home_dir="${case_dir}/home"
15
+ local bin_dir="${case_dir}/bin"
16
+ local shared_agent_home="${case_dir}/shared-agent-home"
17
+ local quota_script_dir="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts"
18
+
19
+ mkdir -p "$state_dir" "$home_dir/.codex" "$bin_dir" "$quota_script_dir"
20
+
21
+ cat >"${bin_dir}/codex" <<'EOF'
22
+ #!/usr/bin/env bash
23
+ set -euo pipefail
24
+
25
+ state_dir="${MOCK_CODEX_STATE_DIR:?}"
26
+ scenario="${MOCK_CODEX_SCENARIO:-usage-limit}"
27
+ invocations_file="${state_dir}/invocations.log"
28
+ attempt_file="${state_dir}/attempt"
29
+
30
+ if [[ "${1:-}" == "login" && "${2:-}" == "status" ]]; then
31
+ if [[ "$scenario" == "auth-recovery" && ! -f "${state_dir}/auth-ready" ]]; then
32
+ printf 'Authentication required\n' >&2
33
+ exit 1
34
+ fi
35
+ printf 'Logged in using ChatGPT\n'
36
+ exit 0
37
+ fi
38
+
39
+ printf '%s\n' "$*" >>"$invocations_file"
40
+
41
+ attempt=0
42
+ if [[ -f "$attempt_file" ]]; then
43
+ attempt="$(cat "$attempt_file")"
44
+ fi
45
+
46
+ if [[ "${1:-}" == "exec" && "${2:-}" == "resume" ]]; then
47
+ printf '%s\n' '{"type":"thread.started","thread_id":"thread-mock-123"}'
48
+ if [[ "$scenario" == "usage-limit-repeat-after-switch" ]]; then
49
+ printf '%s\n' 'You have reached your Codex usage limits. Please visit https://chatgpt.com/codex/settings/usage'
50
+ echo "$((attempt + 1))" >"$attempt_file"
51
+ exit 1
52
+ fi
53
+ printf '%s\n' '{"type":"turn.started"}'
54
+ printf '%s\n' '{"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"resume-ok"}}'
55
+ printf '%s\n' '{"type":"turn.completed","usage":{"input_tokens":1,"output_tokens":1}}'
56
+ echo "$((attempt + 1))" >"$attempt_file"
57
+ exit 0
58
+ fi
59
+
60
+ if [[ "${1:-}" == "exec" ]]; then
61
+ printf '%s\n' '{"type":"thread.started","thread_id":"thread-mock-123"}'
62
+ case "$scenario" in
63
+ usage-limit)
64
+ printf '%s\n' 'You have reached your Codex usage limits. Please visit https://chatgpt.com/codex/settings/usage'
65
+ ;;
66
+ usage-limit-alt-wording)
67
+ printf '%s\n' 'Rate limit exceeded for the active Codex account. Usage cap reached.'
68
+ ;;
69
+ auth-401)
70
+ printf '%s\n' 'HTTP 401 Unauthorized: invalid credentials for the active Codex account.'
71
+ ;;
72
+ usage-limit-pre-switched)
73
+ printf '%s\n' 'You have reached your Codex usage limits. Please visit https://chatgpt.com/codex/settings/usage'
74
+ printf 'manager-staging\n' >"${state_dir}/active-label"
75
+ ;;
76
+ usage-limit-deferred)
77
+ printf '%s\n' 'You have reached your Codex usage limits. Please visit https://chatgpt.com/codex/settings/usage'
78
+ ;;
79
+ usage-limit-repeat-after-switch)
80
+ printf '%s\n' 'You have reached your Codex usage limits. Please visit https://chatgpt.com/codex/settings/usage'
81
+ ;;
82
+ auth-recovery)
83
+ printf '%s\n' 'Authentication required. Please log in.'
84
+ ;;
85
+ auth-recovery-alt-wording)
86
+ printf '%s\n' 'Please authenticate to continue. Login required.'
87
+ ;;
88
+ *)
89
+ echo "unexpected mock codex scenario: $scenario" >&2
90
+ exit 1
91
+ ;;
92
+ esac
93
+ echo "$((attempt + 1))" >"$attempt_file"
94
+ exit 1
95
+ fi
96
+
97
+ echo "unexpected codex invocation: $*" >&2
98
+ exit 1
99
+ EOF
100
+
101
+ cat >"${bin_dir}/codex-quota" <<'EOF'
102
+ #!/usr/bin/env bash
103
+ set -euo pipefail
104
+
105
+ state_dir="${MOCK_CODEX_STATE_DIR:?}"
106
+ active_label_file="${state_dir}/active-label"
107
+ active_label="initial-over-limit"
108
+ if [[ -f "$active_label_file" ]]; then
109
+ active_label="$(cat "$active_label_file")"
110
+ fi
111
+
112
+ if [[ "${1:-}" == "codex" && "${2:-}" == "list" && "${3:-}" == "--json" ]]; then
113
+ cat <<JSON
114
+ {"activeInfo":{"trackedLabel":"${active_label}"},"accounts":[{"label":"${active_label}","isActive":true,"isNativeActive":false}]}
115
+ JSON
116
+ exit 0
117
+ fi
118
+
119
+ exit 0
120
+ EOF
121
+
122
+ cat >"${quota_script_dir}/auto-switch.sh" <<'EOF'
123
+ #!/usr/bin/env bash
124
+ set -euo pipefail
125
+
126
+ state_dir="${MOCK_CODEX_STATE_DIR:?}"
127
+ scenario="${MOCK_CODEX_SCENARIO:-usage-limit}"
128
+ printf '%s\n' "$*" >>"${state_dir}/auto-switch.log"
129
+ case "$scenario" in
130
+ usage-limit-pre-switched)
131
+ printf 'SWITCH_DECISION=current-ok\n'
132
+ printf 'SELECTED_LABEL=manager-staging\n'
133
+ printf 'OK: manager-staging (5h used: 8%% < 70%%, weekly used: 63%% < 90%%, workers: 17).\n'
134
+ ;;
135
+ usage-limit-deferred)
136
+ printf 'SWITCH_DECISION=deferred\n'
137
+ printf 'NEXT_RETRY_AT=%s\n' "$(( $(date +%s) + 1800 ))"
138
+ exit 10
139
+ ;;
140
+ *)
141
+ printf '{"account":"rotated","ts":"%s"}\n' "$(date +%s)" >"$HOME/.codex/auth.json"
142
+ printf 'mock-next-account\n' >"${state_dir}/active-label"
143
+ printf 'SWITCH_DECISION=switched\n'
144
+ printf 'SELECTED_LABEL=mock-next-account\n'
145
+ printf 'Switched to mock-next-account.\n'
146
+ ;;
147
+ esac
148
+ EOF
149
+
150
+ chmod +x "${bin_dir}/codex" "${bin_dir}/codex-quota" "${quota_script_dir}/auto-switch.sh"
151
+ }
152
+
153
+ assert_no_failure_reason() {
154
+ local status_out="${1:-}"
155
+ if grep -q '^FAILURE_REASON=' <<<"$status_out"; then
156
+ echo "unexpected FAILURE_REASON in status output:" >&2
157
+ printf '%s\n' "$status_out" >&2
158
+ exit 1
159
+ fi
160
+ }
161
+
162
+ run_usage_limit_recovery_case() {
163
+ local case_dir="${tmpdir}/usage-limit"
164
+ local session="usage-limit-session"
165
+ local runs_root="${case_dir}/runs"
166
+ local run_dir="${runs_root}/${session}"
167
+ local output_file="${run_dir}/${session}.log"
168
+ local prompt_file="${case_dir}/prompt.md"
169
+ local state_dir="${case_dir}/state"
170
+ local home_dir="${case_dir}/home"
171
+ local bin_dir="${case_dir}/bin"
172
+ local shared_agent_home="${case_dir}/shared-agent-home"
173
+ local worktree="${case_dir}/worktree"
174
+ local status_out
175
+
176
+ mkdir -p "$run_dir" "$worktree"
177
+ create_mock_runtime "$case_dir"
178
+ printf 'Implement exactly once.\n' >"$prompt_file"
179
+ printf '{"account":"initial"}\n' >"${home_dir}/.codex/auth.json"
180
+ printf 'initial-over-limit\n' >"${state_dir}/active-label"
181
+
182
+ PATH="${bin_dir}:$PATH" \
183
+ HOME="$home_dir" \
184
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
185
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
186
+ SHARED_AGENT_HOME="$shared_agent_home" \
187
+ MOCK_CODEX_STATE_DIR="$state_dir" \
188
+ MOCK_CODEX_SCENARIO="usage-limit" \
189
+ bash "$HELPER_BIN" \
190
+ --mode safe \
191
+ --worktree "$worktree" \
192
+ --prompt-file "$prompt_file" \
193
+ --output-file "$output_file" \
194
+ --host-run-dir "$run_dir" \
195
+ --sandbox-run-dir "${case_dir}/sandbox" \
196
+ --safe-profile mock-safe \
197
+ --codex-bin "${bin_dir}/codex" \
198
+ --max-resume-attempts 2 \
199
+ --auth-refresh-timeout-seconds 10 \
200
+ --auth-refresh-poll-seconds 1
201
+
202
+ set -a
203
+ # shellcheck source=/dev/null
204
+ source "${run_dir}/runner.env"
205
+ set +a
206
+
207
+ test "$RUNNER_STATE" = "succeeded"
208
+ test "$THREAD_ID" = "thread-mock-123"
209
+ test "$RESUME_COUNT" = "1"
210
+
211
+ grep -q 'usage limits' "$output_file"
212
+ grep -q 'resume-ok' "$output_file"
213
+ grep -q 'usage-limit detected; attempting failure-driven Codex account switch' "$output_file"
214
+ grep -q 'Switched to mock-next-account.' "$output_file"
215
+ grep -q '^exec --json --profile mock-safe --full-auto$' "${state_dir}/invocations.log"
216
+ grep -q '^exec resume --json --full-auto thread-mock-123 -$' "${state_dir}/invocations.log"
217
+
218
+ status_out="$(
219
+ PATH="${bin_dir}:$PATH" \
220
+ HOME="$home_dir" \
221
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
222
+ )"
223
+ grep -q '^STATUS=SUCCEEDED$' <<<"$status_out"
224
+ grep -q '^THREAD_ID=thread-mock-123$' <<<"$status_out"
225
+ assert_no_failure_reason "$status_out"
226
+ }
227
+
228
+ run_usage_limit_recovery_with_preswitched_account_case() {
229
+ local case_dir="${tmpdir}/usage-limit-pre-switched"
230
+ local session="usage-limit-pre-switched-session"
231
+ local runs_root="${case_dir}/runs"
232
+ local run_dir="${runs_root}/${session}"
233
+ local output_file="${run_dir}/${session}.log"
234
+ local prompt_file="${case_dir}/prompt.md"
235
+ local state_dir="${case_dir}/state"
236
+ local home_dir="${case_dir}/home"
237
+ local bin_dir="${case_dir}/bin"
238
+ local shared_agent_home="${case_dir}/shared-agent-home"
239
+ local worktree="${case_dir}/worktree"
240
+ local status_out
241
+
242
+ mkdir -p "$run_dir" "$worktree"
243
+ create_mock_runtime "$case_dir"
244
+ printf 'Resume after quota rotation.\n' >"$prompt_file"
245
+ printf '{"account":"stable-auth"}\n' >"${home_dir}/.codex/auth.json"
246
+ printf 'initial-over-limit\n' >"${state_dir}/active-label"
247
+
248
+ PATH="${bin_dir}:$PATH" \
249
+ HOME="$home_dir" \
250
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
251
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
252
+ SHARED_AGENT_HOME="$shared_agent_home" \
253
+ MOCK_CODEX_STATE_DIR="$state_dir" \
254
+ MOCK_CODEX_SCENARIO="usage-limit-pre-switched" \
255
+ bash "$HELPER_BIN" \
256
+ --mode safe \
257
+ --worktree "$worktree" \
258
+ --prompt-file "$prompt_file" \
259
+ --output-file "$output_file" \
260
+ --host-run-dir "$run_dir" \
261
+ --sandbox-run-dir "${case_dir}/sandbox" \
262
+ --safe-profile mock-safe \
263
+ --codex-bin "${bin_dir}/codex" \
264
+ --max-resume-attempts 2 \
265
+ --auth-refresh-timeout-seconds 10 \
266
+ --auth-refresh-poll-seconds 1
267
+
268
+ set -a
269
+ # shellcheck source=/dev/null
270
+ source "${run_dir}/runner.env"
271
+ set +a
272
+
273
+ test "$RUNNER_STATE" = "succeeded"
274
+ test "$THREAD_ID" = "thread-mock-123"
275
+ test "$RESUME_COUNT" = "1"
276
+
277
+ grep -q 'usage limits' "$output_file"
278
+ grep -q 'OK: manager-staging' "$output_file"
279
+ grep -q 'detected rotated Codex quota account (initial-over-limit -> manager-staging); resuming thread thread-mock-123' "$output_file"
280
+ grep -q 'resume-ok' "$output_file"
281
+ grep -q '^exec --json --profile mock-safe --full-auto$' "${state_dir}/invocations.log"
282
+ grep -q '^exec resume --json --full-auto thread-mock-123 -$' "${state_dir}/invocations.log"
283
+
284
+ status_out="$(
285
+ PATH="${bin_dir}:$PATH" \
286
+ HOME="$home_dir" \
287
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
288
+ )"
289
+ grep -q '^STATUS=SUCCEEDED$' <<<"$status_out"
290
+ grep -q '^THREAD_ID=thread-mock-123$' <<<"$status_out"
291
+ assert_no_failure_reason "$status_out"
292
+ }
293
+
294
+ run_usage_limit_recovery_alt_wording_case() {
295
+ local case_dir="${tmpdir}/usage-limit-alt-wording"
296
+ local session="usage-limit-alt-wording-session"
297
+ local runs_root="${case_dir}/runs"
298
+ local run_dir="${runs_root}/${session}"
299
+ local output_file="${run_dir}/${session}.log"
300
+ local prompt_file="${case_dir}/prompt.md"
301
+ local state_dir="${case_dir}/state"
302
+ local home_dir="${case_dir}/home"
303
+ local bin_dir="${case_dir}/bin"
304
+ local shared_agent_home="${case_dir}/shared-agent-home"
305
+ local worktree="${case_dir}/worktree"
306
+ local status_out
307
+
308
+ mkdir -p "$run_dir" "$worktree"
309
+ create_mock_runtime "$case_dir"
310
+ printf 'Recover after alternate quota wording.\n' >"$prompt_file"
311
+ printf '{"account":"initial"}\n' >"${home_dir}/.codex/auth.json"
312
+ printf 'initial-over-limit\n' >"${state_dir}/active-label"
313
+
314
+ PATH="${bin_dir}:$PATH" \
315
+ HOME="$home_dir" \
316
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
317
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
318
+ SHARED_AGENT_HOME="$shared_agent_home" \
319
+ MOCK_CODEX_STATE_DIR="$state_dir" \
320
+ MOCK_CODEX_SCENARIO="usage-limit-alt-wording" \
321
+ bash "$HELPER_BIN" \
322
+ --mode safe \
323
+ --worktree "$worktree" \
324
+ --prompt-file "$prompt_file" \
325
+ --output-file "$output_file" \
326
+ --host-run-dir "$run_dir" \
327
+ --sandbox-run-dir "${case_dir}/sandbox" \
328
+ --safe-profile mock-safe \
329
+ --codex-bin "${bin_dir}/codex" \
330
+ --max-resume-attempts 2 \
331
+ --auth-refresh-timeout-seconds 10 \
332
+ --auth-refresh-poll-seconds 1
333
+
334
+ set -a
335
+ # shellcheck source=/dev/null
336
+ source "${run_dir}/runner.env"
337
+ set +a
338
+
339
+ test "$RUNNER_STATE" = "succeeded"
340
+ test "$THREAD_ID" = "thread-mock-123"
341
+ test "$RESUME_COUNT" = "1"
342
+
343
+ grep -q 'Rate limit exceeded for the active Codex account. Usage cap reached.' "$output_file"
344
+ grep -q 'usage-limit detected; attempting failure-driven Codex account switch' "$output_file"
345
+ grep -q 'resume-ok' "$output_file"
346
+
347
+ status_out="$(
348
+ PATH="${bin_dir}:$PATH" \
349
+ HOME="$home_dir" \
350
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
351
+ )"
352
+ grep -q '^STATUS=SUCCEEDED$' <<<"$status_out"
353
+ grep -q '^THREAD_ID=thread-mock-123$' <<<"$status_out"
354
+ assert_no_failure_reason "$status_out"
355
+ }
356
+
357
+ run_auth_recovery_without_fingerprint_change_case() {
358
+ local case_dir="${tmpdir}/auth-recovery"
359
+ local session="auth-recovery-session"
360
+ local runs_root="${case_dir}/runs"
361
+ local run_dir="${runs_root}/${session}"
362
+ local output_file="${run_dir}/${session}.log"
363
+ local prompt_file="${case_dir}/prompt.md"
364
+ local state_dir="${case_dir}/state"
365
+ local home_dir="${case_dir}/home"
366
+ local bin_dir="${case_dir}/bin"
367
+ local shared_agent_home="${case_dir}/shared-agent-home"
368
+ local worktree="${case_dir}/worktree"
369
+ local status_out
370
+ local auth_ready_pid
371
+
372
+ mkdir -p "$run_dir" "$worktree"
373
+ create_mock_runtime "$case_dir"
374
+ printf 'Recover the interrupted task.\n' >"$prompt_file"
375
+ printf '{"account":"stable"}\n' >"${home_dir}/.codex/auth.json"
376
+
377
+ (
378
+ sleep 2
379
+ [[ -d "${state_dir}" ]] && touch "${state_dir}/auth-ready"
380
+ ) &
381
+ auth_ready_pid=$!
382
+
383
+ PATH="${bin_dir}:$PATH" \
384
+ HOME="$home_dir" \
385
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
386
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
387
+ SHARED_AGENT_HOME="$shared_agent_home" \
388
+ MOCK_CODEX_STATE_DIR="$state_dir" \
389
+ MOCK_CODEX_SCENARIO="auth-recovery" \
390
+ bash "$HELPER_BIN" \
391
+ --mode safe \
392
+ --worktree "$worktree" \
393
+ --prompt-file "$prompt_file" \
394
+ --output-file "$output_file" \
395
+ --host-run-dir "$run_dir" \
396
+ --sandbox-run-dir "${case_dir}/sandbox" \
397
+ --safe-profile mock-safe \
398
+ --codex-bin "${bin_dir}/codex" \
399
+ --max-resume-attempts 2 \
400
+ --auth-refresh-timeout-seconds 10 \
401
+ --auth-refresh-poll-seconds 1
402
+
403
+ set -a
404
+ # shellcheck source=/dev/null
405
+ source "${run_dir}/runner.env"
406
+ set +a
407
+
408
+ test "$RUNNER_STATE" = "succeeded"
409
+ test "$THREAD_ID" = "thread-mock-123"
410
+ test "$RESUME_COUNT" = "1"
411
+
412
+ grep -q 'Authentication required. Please log in.' "$output_file"
413
+ grep -q 'Codex auth is healthy again; resuming thread thread-mock-123' "$output_file"
414
+ grep -q 'resume-ok' "$output_file"
415
+ grep -q '^exec --json --profile mock-safe --full-auto$' "${state_dir}/invocations.log"
416
+ grep -q '^exec resume --json --full-auto thread-mock-123 -$' "${state_dir}/invocations.log"
417
+
418
+ status_out="$(
419
+ PATH="${bin_dir}:$PATH" \
420
+ HOME="$home_dir" \
421
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
422
+ )"
423
+ grep -q '^STATUS=SUCCEEDED$' <<<"$status_out"
424
+ grep -q '^THREAD_ID=thread-mock-123$' <<<"$status_out"
425
+ assert_no_failure_reason "$status_out"
426
+ wait "$auth_ready_pid"
427
+ }
428
+
429
+ run_auth_recovery_alt_wording_case() {
430
+ local case_dir="${tmpdir}/auth-recovery-alt-wording"
431
+ local session="auth-recovery-alt-wording-session"
432
+ local runs_root="${case_dir}/runs"
433
+ local run_dir="${runs_root}/${session}"
434
+ local output_file="${run_dir}/${session}.log"
435
+ local prompt_file="${case_dir}/prompt.md"
436
+ local state_dir="${case_dir}/state"
437
+ local home_dir="${case_dir}/home"
438
+ local bin_dir="${case_dir}/bin"
439
+ local shared_agent_home="${case_dir}/shared-agent-home"
440
+ local worktree="${case_dir}/worktree"
441
+ local status_out
442
+ local auth_ready_pid
443
+
444
+ mkdir -p "$run_dir" "$worktree"
445
+ create_mock_runtime "$case_dir"
446
+ printf 'Recover the interrupted task with alternate auth wording.\n' >"$prompt_file"
447
+ printf '{"account":"stable"}\n' >"${home_dir}/.codex/auth.json"
448
+
449
+ (
450
+ sleep 2
451
+ [[ -d "${state_dir}" ]] && touch "${state_dir}/auth-ready"
452
+ ) &
453
+ auth_ready_pid=$!
454
+
455
+ PATH="${bin_dir}:$PATH" \
456
+ HOME="$home_dir" \
457
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
458
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
459
+ SHARED_AGENT_HOME="$shared_agent_home" \
460
+ MOCK_CODEX_STATE_DIR="$state_dir" \
461
+ MOCK_CODEX_SCENARIO="auth-recovery-alt-wording" \
462
+ bash "$HELPER_BIN" \
463
+ --mode safe \
464
+ --worktree "$worktree" \
465
+ --prompt-file "$prompt_file" \
466
+ --output-file "$output_file" \
467
+ --host-run-dir "$run_dir" \
468
+ --sandbox-run-dir "${case_dir}/sandbox" \
469
+ --safe-profile mock-safe \
470
+ --codex-bin "${bin_dir}/codex" \
471
+ --max-resume-attempts 2 \
472
+ --auth-refresh-timeout-seconds 10 \
473
+ --auth-refresh-poll-seconds 1
474
+
475
+ set -a
476
+ # shellcheck source=/dev/null
477
+ source "${run_dir}/runner.env"
478
+ set +a
479
+
480
+ test "$RUNNER_STATE" = "succeeded"
481
+ test "$THREAD_ID" = "thread-mock-123"
482
+ test "$RESUME_COUNT" = "1"
483
+
484
+ grep -q 'Please authenticate to continue. Login required.' "$output_file"
485
+ grep -q 'Codex auth is healthy again; resuming thread thread-mock-123' "$output_file"
486
+ grep -q 'resume-ok' "$output_file"
487
+
488
+ status_out="$(
489
+ PATH="${bin_dir}:$PATH" \
490
+ HOME="$home_dir" \
491
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
492
+ )"
493
+ grep -q '^STATUS=SUCCEEDED$' <<<"$status_out"
494
+ grep -q '^THREAD_ID=thread-mock-123$' <<<"$status_out"
495
+ assert_no_failure_reason "$status_out"
496
+ wait "$auth_ready_pid"
497
+ }
498
+
499
+ run_auth_401_rotation_case() {
500
+ local case_dir="${tmpdir}/auth-401"
501
+ local session="auth-401-session"
502
+ local runs_root="${case_dir}/runs"
503
+ local run_dir="${runs_root}/${session}"
504
+ local output_file="${run_dir}/${session}.log"
505
+ local prompt_file="${case_dir}/prompt.md"
506
+ local state_dir="${case_dir}/state"
507
+ local home_dir="${case_dir}/home"
508
+ local bin_dir="${case_dir}/bin"
509
+ local shared_agent_home="${case_dir}/shared-agent-home"
510
+ local worktree="${case_dir}/worktree"
511
+ local status_out
512
+
513
+ mkdir -p "$run_dir" "$worktree"
514
+ create_mock_runtime "$case_dir"
515
+ printf 'Recover after unauthorized account rotation.\n' >"$prompt_file"
516
+ printf '{"account":"initial"}\n' >"${home_dir}/.codex/auth.json"
517
+ printf 'initial-over-limit\n' >"${state_dir}/active-label"
518
+
519
+ PATH="${bin_dir}:$PATH" \
520
+ HOME="$home_dir" \
521
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
522
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
523
+ SHARED_AGENT_HOME="$shared_agent_home" \
524
+ MOCK_CODEX_STATE_DIR="$state_dir" \
525
+ MOCK_CODEX_SCENARIO="auth-401" \
526
+ bash "$HELPER_BIN" \
527
+ --mode safe \
528
+ --worktree "$worktree" \
529
+ --prompt-file "$prompt_file" \
530
+ --output-file "$output_file" \
531
+ --host-run-dir "$run_dir" \
532
+ --sandbox-run-dir "${case_dir}/sandbox" \
533
+ --safe-profile mock-safe \
534
+ --codex-bin "${bin_dir}/codex" \
535
+ --max-resume-attempts 2 \
536
+ --auth-refresh-timeout-seconds 10 \
537
+ --auth-refresh-poll-seconds 1
538
+
539
+ set -a
540
+ # shellcheck source=/dev/null
541
+ source "${run_dir}/runner.env"
542
+ set +a
543
+
544
+ test "$RUNNER_STATE" = "succeeded"
545
+ test "$THREAD_ID" = "thread-mock-123"
546
+ test "$RESUME_COUNT" = "1"
547
+
548
+ grep -q 'HTTP 401 Unauthorized: invalid credentials for the active Codex account.' "$output_file"
549
+ grep -q 'auth-401 detected; attempting failure-driven Codex account switch' "$output_file"
550
+ grep -q 'Switched to mock-next-account.' "$output_file"
551
+ grep -q 'resume-ok' "$output_file"
552
+
553
+ status_out="$(
554
+ PATH="${bin_dir}:$PATH" \
555
+ HOME="$home_dir" \
556
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
557
+ )"
558
+ grep -q '^STATUS=SUCCEEDED$' <<<"$status_out"
559
+ grep -q '^THREAD_ID=thread-mock-123$' <<<"$status_out"
560
+ assert_no_failure_reason "$status_out"
561
+ }
562
+
563
+ run_usage_limit_deferred_fails_without_timed_retry_case() {
564
+ local case_dir="${tmpdir}/usage-limit-deferred"
565
+ local session="usage-limit-deferred-session"
566
+ local runs_root="${case_dir}/runs"
567
+ local run_dir="${runs_root}/${session}"
568
+ local output_file="${run_dir}/${session}.log"
569
+ local prompt_file="${case_dir}/prompt.md"
570
+ local state_dir="${case_dir}/state"
571
+ local home_dir="${case_dir}/home"
572
+ local bin_dir="${case_dir}/bin"
573
+ local shared_agent_home="${case_dir}/shared-agent-home"
574
+ local worktree="${case_dir}/worktree"
575
+ local status_out
576
+ local started_at ended_at elapsed
577
+
578
+ mkdir -p "$run_dir" "$worktree"
579
+ create_mock_runtime "$case_dir"
580
+ printf 'Do not keep rotating accounts in the background.\n' >"$prompt_file"
581
+ printf '{"account":"initial"}\n' >"${home_dir}/.codex/auth.json"
582
+ printf 'initial-over-limit\n' >"${state_dir}/active-label"
583
+
584
+ started_at="$(date +%s)"
585
+ set +e
586
+ PATH="${bin_dir}:$PATH" \
587
+ HOME="$home_dir" \
588
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
589
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
590
+ SHARED_AGENT_HOME="$shared_agent_home" \
591
+ MOCK_CODEX_STATE_DIR="$state_dir" \
592
+ MOCK_CODEX_SCENARIO="usage-limit-deferred" \
593
+ bash "$HELPER_BIN" \
594
+ --mode safe \
595
+ --worktree "$worktree" \
596
+ --prompt-file "$prompt_file" \
597
+ --output-file "$output_file" \
598
+ --host-run-dir "$run_dir" \
599
+ --sandbox-run-dir "${case_dir}/sandbox" \
600
+ --safe-profile mock-safe \
601
+ --codex-bin "${bin_dir}/codex" \
602
+ --max-resume-attempts 2 \
603
+ --auth-refresh-timeout-seconds 30 \
604
+ --auth-refresh-poll-seconds 1
605
+ status=$?
606
+ set -e
607
+ ended_at="$(date +%s)"
608
+ elapsed=$((ended_at - started_at))
609
+
610
+ test "$status" != "0"
611
+ test "$elapsed" -lt 5
612
+
613
+ set -a
614
+ # shellcheck source=/dev/null
615
+ source "${run_dir}/runner.env"
616
+ set +a
617
+
618
+ test "$RUNNER_STATE" = "failed"
619
+ test "$LAST_FAILURE_REASON" = "quota-switch-deferred"
620
+ grep -q 'automatic timed re-tries are disabled for safety' "$output_file"
621
+ test "$(wc -l <"${state_dir}/auto-switch.log" | tr -d '[:space:]')" = "1"
622
+
623
+ status_out="$(
624
+ PATH="${bin_dir}:$PATH" \
625
+ HOME="$home_dir" \
626
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
627
+ )"
628
+ grep -q '^STATUS=FAILED$' <<<"$status_out"
629
+ grep -q '^FAILURE_REASON=quota-switch-deferred$' <<<"$status_out"
630
+ }
631
+
632
+ run_usage_limit_repeat_after_switch_fails_after_one_rotation_case() {
633
+ local case_dir="${tmpdir}/usage-limit-repeat-after-switch"
634
+ local session="usage-limit-repeat-after-switch-session"
635
+ local runs_root="${case_dir}/runs"
636
+ local run_dir="${runs_root}/${session}"
637
+ local output_file="${run_dir}/${session}.log"
638
+ local prompt_file="${case_dir}/prompt.md"
639
+ local state_dir="${case_dir}/state"
640
+ local home_dir="${case_dir}/home"
641
+ local bin_dir="${case_dir}/bin"
642
+ local shared_agent_home="${case_dir}/shared-agent-home"
643
+ local worktree="${case_dir}/worktree"
644
+ local status_out
645
+
646
+ mkdir -p "$run_dir" "$worktree"
647
+ create_mock_runtime "$case_dir"
648
+ printf 'If quota still fails after one switch, stop and surface it.\n' >"$prompt_file"
649
+ printf '{"account":"initial"}\n' >"${home_dir}/.codex/auth.json"
650
+ printf 'initial-over-limit\n' >"${state_dir}/active-label"
651
+
652
+ set +e
653
+ PATH="${bin_dir}:$PATH" \
654
+ HOME="$home_dir" \
655
+ ACP_CODEX_QUOTA_BIN="${bin_dir}/codex-quota" \
656
+ ACP_CODEX_QUOTA_MANAGER_SCRIPT="${shared_agent_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh" \
657
+ SHARED_AGENT_HOME="$shared_agent_home" \
658
+ MOCK_CODEX_STATE_DIR="$state_dir" \
659
+ MOCK_CODEX_SCENARIO="usage-limit-repeat-after-switch" \
660
+ bash "$HELPER_BIN" \
661
+ --mode safe \
662
+ --worktree "$worktree" \
663
+ --prompt-file "$prompt_file" \
664
+ --output-file "$output_file" \
665
+ --host-run-dir "$run_dir" \
666
+ --sandbox-run-dir "${case_dir}/sandbox" \
667
+ --safe-profile mock-safe \
668
+ --codex-bin "${bin_dir}/codex" \
669
+ --max-resume-attempts 3 \
670
+ --auth-refresh-timeout-seconds 10 \
671
+ --auth-refresh-poll-seconds 1
672
+ status=$?
673
+ set -e
674
+
675
+ test "$status" != "0"
676
+
677
+ set -a
678
+ # shellcheck source=/dev/null
679
+ source "${run_dir}/runner.env"
680
+ set +a
681
+
682
+ test "$RUNNER_STATE" = "failed"
683
+ test "$RESUME_COUNT" = "1"
684
+ test "$LAST_FAILURE_REASON" = "quota-switch-attempt-limit"
685
+ grep -q 'automatic Codex quota switching already ran 1 time(s) in this worker; refusing another rotation' "$output_file"
686
+ test "$(wc -l <"${state_dir}/auto-switch.log" | tr -d '[:space:]')" = "1"
687
+
688
+ status_out="$(
689
+ PATH="${bin_dir}:$PATH" \
690
+ HOME="$home_dir" \
691
+ bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session"
692
+ )"
693
+ grep -q '^STATUS=FAILED$' <<<"$status_out"
694
+ grep -q '^FAILURE_REASON=quota-switch-attempt-limit$' <<<"$status_out"
695
+ }
696
+
697
+ run_result_env_completion_override_case() {
698
+ local case_dir="${tmpdir}/result-only"
699
+ local runs_root="${case_dir}/runs"
700
+ local session="result-only-session"
701
+ local run_dir="${runs_root}/${session}"
702
+ local output_file="${run_dir}/${session}.log"
703
+ local result_file="${run_dir}/result.env"
704
+ local status_out
705
+
706
+ mkdir -p "$run_dir"
707
+ cat >"$output_file" <<'EOF'
708
+ You have reached your Codex usage limits. Please visit https://chatgpt.com/codex/settings/usage
709
+ EOF
710
+ cat >"$result_file" <<'EOF'
711
+ OUTCOME=reported
712
+ ACTION=host-comment-scheduled-report
713
+ EOF
714
+
715
+ status_out="$(bash "$STATUS_BIN" --runs-root "$runs_root" --session "$session")"
716
+ grep -q '^STATUS=SUCCEEDED$' <<<"$status_out"
717
+ grep -q '^RESULT_ONLY_COMPLETION=yes$' <<<"$status_out"
718
+ assert_no_failure_reason "$status_out"
719
+ }
720
+
721
+ run_usage_limit_recovery_case
722
+ run_usage_limit_recovery_with_preswitched_account_case
723
+ run_usage_limit_recovery_alt_wording_case
724
+ run_auth_recovery_without_fingerprint_change_case
725
+ run_auth_recovery_alt_wording_case
726
+ run_auth_401_rotation_case
727
+ run_usage_limit_deferred_fails_without_timed_retry_case
728
+ run_usage_limit_repeat_after_switch_fails_after_one_rotation_case
729
+ run_result_env_completion_override_case
730
+
731
+ echo "agent-project codex recovery test passed"