agent-control-plane 0.1.2 → 0.1.4

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