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