agent-control-plane 0.1.1 → 0.1.3

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 (184) hide show
  1. package/README.md +6 -0
  2. package/npm/bin/agent-control-plane.js +149 -5
  3. package/package.json +2 -3
  4. package/tools/vendor/codex-quota/README.md +8 -16
  5. package/tools/vendor/codex-quota/lib/claude-accounts.js +2 -68
  6. package/tools/vendor/codex-quota/lib/claude-usage.js +76 -667
  7. package/tools/vendor/codex-quota/lib/display.js +6 -14
  8. package/tools/vendor/codex-quota/lib/handlers.js +32 -117
  9. package/tools/vendor/codex-quota/lib/sync.js +9 -14
  10. package/tools/tests/test-agent-control-plane-npm-cli.sh +0 -280
  11. package/tools/tests/test-agent-github-update-labels-falls-back-to-repository-id.sh +0 -56
  12. package/tools/tests/test-agent-project-claude-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -89
  13. package/tools/tests/test-agent-project-claude-session-wrapper-does-not-retry-provider-quota.sh +0 -82
  14. package/tools/tests/test-agent-project-claude-session-wrapper-retries-transient-failures.sh +0 -90
  15. package/tools/tests/test-agent-project-claude-session-wrapper-times-out.sh +0 -73
  16. package/tools/tests/test-agent-project-claude-session-wrapper.sh +0 -103
  17. package/tools/tests/test-agent-project-cleanup-session-orphan-fallback.sh +0 -90
  18. package/tools/tests/test-agent-project-cleanup-session-skip-worktree-cleanup.sh +0 -90
  19. package/tools/tests/test-agent-project-codex-live-thread-persist.sh +0 -76
  20. package/tools/tests/test-agent-project-codex-recovery.sh +0 -731
  21. package/tools/tests/test-agent-project-codex-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -105
  22. package/tools/tests/test-agent-project-codex-session-wrapper.sh +0 -97
  23. package/tools/tests/test-agent-project-open-pr-worktree-config-prefix.sh +0 -81
  24. package/tools/tests/test-agent-project-openclaw-session-wrapper-clears-stale-sandbox-artifacts.sh +0 -109
  25. package/tools/tests/test-agent-project-openclaw-session-wrapper-infers-blocked-result-contract.sh +0 -89
  26. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-literal-env-artifacts.sh +0 -113
  27. package/tools/tests/test-agent-project-openclaw-session-wrapper-recovers-version-mismatch.sh +0 -135
  28. package/tools/tests/test-agent-project-openclaw-session-wrapper-resident.sh +0 -179
  29. package/tools/tests/test-agent-project-openclaw-session-wrapper-reuses-existing-agent-after-add-race.sh +0 -119
  30. package/tools/tests/test-agent-project-openclaw-session-wrapper-terminates-rate-limit-hang.sh +0 -91
  31. package/tools/tests/test-agent-project-openclaw-session-wrapper.sh +0 -117
  32. package/tools/tests/test-agent-project-publish-issue-pr-prunes-stale-worktree-entry.sh +0 -148
  33. package/tools/tests/test-agent-project-publish-issue-pr-reads-archived-session.sh +0 -146
  34. package/tools/tests/test-agent-project-publish-issue-pr-recovers-final-head.sh +0 -145
  35. package/tools/tests/test-agent-project-publish-issue-pr-reuses-existing-worktree.sh +0 -147
  36. package/tools/tests/test-agent-project-reconcile-failure-reason.sh +0 -456
  37. package/tools/tests/test-agent-project-reconcile-issue-archived-session-fallback.sh +0 -96
  38. package/tools/tests/test-agent-project-reconcile-issue-before-blocked.sh +0 -90
  39. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery-uses-recovered-worktree.sh +0 -212
  40. package/tools/tests/test-agent-project-reconcile-issue-host-verification-recovery.sh +0 -207
  41. package/tools/tests/test-agent-project-reconcile-issue-provider-quota-schedules-provider-cooldown.sh +0 -101
  42. package/tools/tests/test-agent-project-reconcile-issue-session-backfills-lane-metadata-from-worker-key.sh +0 -113
  43. package/tools/tests/test-agent-project-reconcile-issue-session-clears-stale-failed-summary.sh +0 -117
  44. package/tools/tests/test-agent-project-reconcile-issue-session-initializes-shared-agent-home.sh +0 -55
  45. package/tools/tests/test-agent-project-reconcile-issue-session-normalizes-runner-state.sh +0 -125
  46. package/tools/tests/test-agent-project-reconcile-issue-session-records-invalid-contract-summary.sh +0 -118
  47. package/tools/tests/test-agent-project-reconcile-issue-session-skips-duplicate-blocked-comment.sh +0 -144
  48. package/tools/tests/test-agent-project-reconcile-issue-session-standardizes-no-commits-blocker.sh +0 -145
  49. package/tools/tests/test-agent-project-reconcile-issue-session-synthesizes-blocked-comment.sh +0 -139
  50. package/tools/tests/test-agent-project-reconcile-pr-blocked-host-recovery.sh +0 -242
  51. package/tools/tests/test-agent-project-reconcile-pr-guard-blocked-no-commit.sh +0 -142
  52. package/tools/tests/test-agent-project-reconcile-pr-provider-quota-schedules-provider-cooldown.sh +0 -106
  53. package/tools/tests/test-agent-project-reconcile-pr-session-initializes-shared-agent-home.sh +0 -66
  54. package/tools/tests/test-agent-project-reconcile-pr-updated-branch-noop.sh +0 -129
  55. package/tools/tests/test-audit-agent-worktrees-active-launch-skips-git-inspection.sh +0 -69
  56. package/tools/tests/test-audit-agent-worktrees-broken-worktree.sh +0 -43
  57. package/tools/tests/test-audit-agent-worktrees-pending-launch-owner.sh +0 -46
  58. package/tools/tests/test-audit-agent-worktrees-unreconciled-owner.sh +0 -79
  59. package/tools/tests/test-audit-issue-routing-managed-branch-globs.sh +0 -56
  60. package/tools/tests/test-branch-verification-guard-generated-artifacts.sh +0 -72
  61. package/tools/tests/test-branch-verification-guard-targeted-coverage.sh +0 -125
  62. package/tools/tests/test-codex-quota-manager-failure-driven-rotation.sh +0 -178
  63. package/tools/tests/test-codex-quota-wrapper.sh +0 -37
  64. package/tools/tests/test-contribution-docs.sh +0 -18
  65. package/tools/tests/test-control-plane-dashboard-runtime-smoke.sh +0 -343
  66. package/tools/tests/test-create-follow-up-issue.sh +0 -73
  67. package/tools/tests/test-dashboard-launchd-bootstrap.sh +0 -55
  68. package/tools/tests/test-flow-export-execution-env-exports-repo-id.sh +0 -30
  69. package/tools/tests/test-flow-export-github-cli-auth-env-prefers-git-credential.sh +0 -48
  70. package/tools/tests/test-flow-github-api-repo-fallback-preserves-input.sh +0 -85
  71. package/tools/tests/test-flow-github-api-repo-prefers-explicit-repository-id.sh +0 -60
  72. package/tools/tests/test-flow-github-issue-list-falls-back-to-repository-id.sh +0 -64
  73. package/tools/tests/test-flow-github-pr-list-falls-back-to-repository-id.sh +0 -77
  74. package/tools/tests/test-flow-resident-can-reuse-does-not-leak-metadata.sh +0 -52
  75. package/tools/tests/test-flow-resident-reap-stale-controllers.sh +0 -63
  76. package/tools/tests/test-flow-resolve-codex-quota-tools.sh +0 -104
  77. package/tools/tests/test-flow-runtime-doctor-profile-selection.sh +0 -27
  78. package/tools/tests/test-heartbeat-codex-pr-linked-issue-exclusion.sh +0 -79
  79. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-idle-controller.sh +0 -115
  80. package/tools/tests/test-heartbeat-hooks-enqueue-resident-issue-for-live-lane-controller.sh +0 -117
  81. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-claude.sh +0 -96
  82. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop-codex.sh +0 -96
  83. package/tools/tests/test-heartbeat-hooks-start-resident-issue-loop.sh +0 -96
  84. package/tools/tests/test-heartbeat-loop-auth-wait-does-not-consume-capacity.sh +0 -170
  85. package/tools/tests/test-heartbeat-loop-blocked-recovery-lane.sh +0 -201
  86. package/tools/tests/test-heartbeat-loop-blocked-recovery-vs-pr-reservation.sh +0 -201
  87. package/tools/tests/test-heartbeat-loop-idle-resident-controller-does-not-block-launches.sh +0 -160
  88. package/tools/tests/test-heartbeat-loop-pr-launch-dedup.sh +0 -133
  89. package/tools/tests/test-heartbeat-loop-provider-cooldown-suppresses-launches.sh +0 -157
  90. package/tools/tests/test-heartbeat-loop-reaps-stale-resident-controller.sh +0 -181
  91. package/tools/tests/test-heartbeat-loop-waiting-provider-resident-controller-does-not-block-launches.sh +0 -160
  92. package/tools/tests/test-heartbeat-ready-issues-blocked-recovery.sh +0 -134
  93. package/tools/tests/test-heartbeat-safe-auto-dynamic-concurrency.sh +0 -162
  94. package/tools/tests/test-heartbeat-safe-auto-no-tmux-sessions.sh +0 -136
  95. package/tools/tests/test-heartbeat-safe-auto-openclaw-skips-codex-quota.sh +0 -139
  96. package/tools/tests/test-heartbeat-safe-auto-quota-health-signal.sh +0 -119
  97. package/tools/tests/test-heartbeat-safe-auto-stale-shared-loop-pid-does-not-skip.sh +0 -140
  98. package/tools/tests/test-heartbeat-safe-auto-static-capacity-without-quota-cache.sh +0 -142
  99. package/tools/tests/test-heartbeat-safe-auto-zero-healthy-pools.sh +0 -141
  100. package/tools/tests/test-heartbeat-sync-issue-labels-empty-schedule.sh +0 -65
  101. package/tools/tests/test-heartbeat-sync-open-agent-prs-terminal-clears-running.sh +0 -179
  102. package/tools/tests/test-install-dashboard-launchd.sh +0 -78
  103. package/tools/tests/test-install-project-launchd-adds-tool-paths.sh +0 -87
  104. package/tools/tests/test-install-project-launchd.sh +0 -110
  105. package/tools/tests/test-issue-local-workspace-install-policy.sh +0 -81
  106. package/tools/tests/test-issue-publish-scope-guard-docs-signal.sh +0 -70
  107. package/tools/tests/test-issue-reconcile-hooks-success-clears-blocked.sh +0 -36
  108. package/tools/tests/test-kick-scheduler-requires-explicit-profile.sh +0 -47
  109. package/tools/tests/test-label-follow-up-issues-falls-back-to-repository-id.sh +0 -132
  110. package/tools/tests/test-manual-operator-entrypoints-require-explicit-profile.sh +0 -64
  111. package/tools/tests/test-package-funding-metadata.sh +0 -21
  112. package/tools/tests/test-package-public-metadata.sh +0 -62
  113. package/tools/tests/test-placeholder-worker-adapters.sh +0 -38
  114. package/tools/tests/test-pr-reconcile-hooks-refreshes-recurring-issue-checklist.sh +0 -110
  115. package/tools/tests/test-pr-risk-cohesive-mobile-locale-scope.sh +0 -70
  116. package/tools/tests/test-pr-risk-fix-label-semantics.sh +0 -114
  117. package/tools/tests/test-pr-risk-local-first-no-checks.sh +0 -70
  118. package/tools/tests/test-prepare-worktree-simple-repo-baseline.sh +0 -67
  119. package/tools/tests/test-profile-activate.sh +0 -33
  120. package/tools/tests/test-profile-adopt-allow-missing-repo.sh +0 -68
  121. package/tools/tests/test-profile-adopt-skip-workspace-sync-missing-file.sh +0 -61
  122. package/tools/tests/test-profile-adopt-syncs-anchor-and-workspace.sh +0 -90
  123. package/tools/tests/test-profile-smoke-collision.sh +0 -44
  124. package/tools/tests/test-profile-smoke-invalid-claude-config.sh +0 -31
  125. package/tools/tests/test-profile-smoke-invalid-provider-pool.sh +0 -68
  126. package/tools/tests/test-profile-smoke-repo-slug-mismatch.sh +0 -36
  127. package/tools/tests/test-profile-smoke.sh +0 -45
  128. package/tools/tests/test-project-init-force-and-skip-sync.sh +0 -61
  129. package/tools/tests/test-project-init-repo-slug-mismatch.sh +0 -29
  130. package/tools/tests/test-project-init.sh +0 -66
  131. package/tools/tests/test-project-launchd-bootstrap.sh +0 -66
  132. package/tools/tests/test-project-remove.sh +0 -150
  133. package/tools/tests/test-project-runtime-supervisor.sh +0 -47
  134. package/tools/tests/test-project-runtimectl-launchd.sh +0 -115
  135. package/tools/tests/test-project-runtimectl-missing-profile.sh +0 -54
  136. package/tools/tests/test-project-runtimectl-start-falls-back-to-bootstrap.sh +0 -108
  137. package/tools/tests/test-project-runtimectl-status-reports-supervisor-as-heartbeat-parent.sh +0 -95
  138. package/tools/tests/test-project-runtimectl-status-supervisor-running.sh +0 -59
  139. package/tools/tests/test-project-runtimectl-stop-cancels-pending-kick.sh +0 -85
  140. package/tools/tests/test-project-runtimectl-stop-clears-running-labels.sh +0 -78
  141. package/tools/tests/test-project-runtimectl.sh +0 -212
  142. package/tools/tests/test-provider-cooldown-state-prefers-runtime-worker-context.sh +0 -39
  143. package/tools/tests/test-provider-cooldown-state.sh +0 -59
  144. package/tools/tests/test-public-repo-docs.sh +0 -160
  145. package/tools/tests/test-reconcile-pr-worker-acp-config-routing.sh +0 -75
  146. package/tools/tests/test-render-dashboard-snapshot.sh +0 -149
  147. package/tools/tests/test-render-flow-config-demo-profile.sh +0 -36
  148. package/tools/tests/test-render-flow-config-provider-pool-fallback.sh +0 -81
  149. package/tools/tests/test-render-flow-config.sh +0 -52
  150. package/tools/tests/test-run-codex-task-claude-routing.sh +0 -125
  151. package/tools/tests/test-run-codex-task-codex-resident-routing.sh +0 -108
  152. package/tools/tests/test-run-codex-task-kilo-routing.sh +0 -98
  153. package/tools/tests/test-run-codex-task-openclaw-resident-routing.sh +0 -117
  154. package/tools/tests/test-run-codex-task-openclaw-routing.sh +0 -113
  155. package/tools/tests/test-run-codex-task-opencode-routing.sh +0 -98
  156. package/tools/tests/test-run-codex-task-provider-pool-fallback-routing.sh +0 -146
  157. package/tools/tests/test-scaffold-profile.sh +0 -108
  158. package/tools/tests/test-serve-dashboard.sh +0 -93
  159. package/tools/tests/test-start-issue-worker-blocked-context.sh +0 -129
  160. package/tools/tests/test-start-issue-worker-blocks-complete-recurring-checklist.sh +0 -189
  161. package/tools/tests/test-start-issue-worker-local-install-routing.sh +0 -157
  162. package/tools/tests/test-start-issue-worker-profile-template-routing.sh +0 -149
  163. package/tools/tests/test-start-issue-worker-recurring-resident-reuse-codex.sh +0 -212
  164. package/tools/tests/test-start-issue-worker-recurring-resident-reuse.sh +0 -219
  165. package/tools/tests/test-start-issue-worker-renders-verification-snippet.sh +0 -155
  166. package/tools/tests/test-start-issue-worker-resident-reuse-falls-back-to-new-worktree.sh +0 -199
  167. package/tools/tests/test-start-pr-fix-worker-host-blocker-context.sh +0 -275
  168. package/tools/tests/test-start-resident-issue-loop-adopts-next-recurring-issue.sh +0 -185
  169. package/tools/tests/test-start-resident-issue-loop-clears-pending-while-waiting-due.sh +0 -152
  170. package/tools/tests/test-start-resident-issue-loop-consumes-queued-lease.sh +0 -186
  171. package/tools/tests/test-start-resident-issue-loop-fails-over-provider-pool.sh +0 -212
  172. package/tools/tests/test-start-resident-issue-loop-immediate-cycles.sh +0 -148
  173. package/tools/tests/test-start-resident-issue-loop-waits-for-provider.sh +0 -194
  174. package/tools/tests/test-start-resident-issue-loop-waits-for-terminal-reconcile-status.sh +0 -198
  175. package/tools/tests/test-start-resident-issue-loop-yields-to-live-lane-controller.sh +0 -145
  176. package/tools/tests/test-sync-pr-labels-fix-lane-uses-repair-queued.sh +0 -67
  177. package/tools/tests/test-sync-recurring-issue-checklist-backfills-workflow-complete-blocker.sh +0 -70
  178. package/tools/tests/test-sync-recurring-issue-checklist.sh +0 -95
  179. package/tools/tests/test-sync-shared-agent-home-local-source-root.sh +0 -66
  180. package/tools/tests/test-sync-shared-agent-home-preserves-unrelated-workflow-catalog-skill.sh +0 -47
  181. package/tools/tests/test-test-smoke.sh +0 -86
  182. package/tools/tests/test-uninstall-project-launchd.sh +0 -37
  183. package/tools/tests/test-update-github-labels-prefers-sibling-helper.sh +0 -49
  184. package/tools/tests/test-workflow-catalog.sh +0 -43
@@ -1,731 +0,0 @@
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"