agent-control-plane 0.4.9 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +109 -13
  2. package/npm/bin/agent-control-plane.js +1 -1
  3. package/package.json +39 -33
  4. package/tools/bin/debug-session.sh +106 -0
  5. package/tools/bin/flow-config-lib.sh +13 -3508
  6. package/tools/bin/flow-execution-lib.sh +243 -0
  7. package/tools/bin/flow-forge-lib.sh +1770 -0
  8. package/tools/bin/flow-profile-lib.sh +335 -0
  9. package/tools/bin/flow-provider-lib.sh +981 -0
  10. package/tools/bin/flow-runtime-doctor-linux.sh +136 -0
  11. package/tools/bin/flow-runtime-doctor.sh +5 -1
  12. package/tools/bin/flow-session-lib.sh +317 -0
  13. package/tools/bin/install-project-systemd.sh +255 -0
  14. package/tools/bin/project-runtimectl.sh +45 -0
  15. package/tools/bin/project-systemd-bootstrap.sh +74 -0
  16. package/tools/bin/uninstall-project-systemd.sh +87 -0
  17. package/tools/dashboard/app.js +238 -8
  18. package/tools/dashboard/issue_queue_state.py +101 -0
  19. package/tools/dashboard/requirements.txt +3 -0
  20. package/tools/dashboard/server.py +250 -30
  21. package/tools/dashboard/styles.css +526 -455
  22. package/tools/bin/agent-cleanup-worktree +0 -247
  23. package/tools/bin/agent-github-update-labels +0 -105
  24. package/tools/bin/agent-init-worktree +0 -216
  25. package/tools/bin/agent-project-archive-run +0 -52
  26. package/tools/bin/agent-project-capture-worker +0 -46
  27. package/tools/bin/agent-project-catch-up-issue-pr-links +0 -118
  28. package/tools/bin/agent-project-catch-up-merged-prs +0 -195
  29. package/tools/bin/agent-project-catch-up-scheduled-issue-retries +0 -123
  30. package/tools/bin/agent-project-cleanup-session +0 -513
  31. package/tools/bin/agent-project-detached-launch +0 -127
  32. package/tools/bin/agent-project-heartbeat-loop +0 -1029
  33. package/tools/bin/agent-project-open-issue-worktree +0 -89
  34. package/tools/bin/agent-project-open-pr-worktree +0 -80
  35. package/tools/bin/agent-project-publish-issue-pr +0 -468
  36. package/tools/bin/agent-project-reconcile-issue-session +0 -1409
  37. package/tools/bin/agent-project-reconcile-pr-session +0 -1288
  38. package/tools/bin/agent-project-retry-state +0 -158
  39. package/tools/bin/agent-project-run-claude-session +0 -805
  40. package/tools/bin/agent-project-run-codex-resilient +0 -963
  41. package/tools/bin/agent-project-run-codex-session +0 -435
  42. package/tools/bin/agent-project-run-kilo-session +0 -369
  43. package/tools/bin/agent-project-run-ollama-session +0 -658
  44. package/tools/bin/agent-project-run-openclaw-session +0 -1309
  45. package/tools/bin/agent-project-run-opencode-session +0 -377
  46. package/tools/bin/agent-project-run-pi-session +0 -479
  47. package/tools/bin/agent-project-sync-anchor-repo +0 -139
  48. package/tools/bin/agent-project-sync-source-repo-main +0 -163
  49. package/tools/bin/agent-project-worker-status +0 -188
  50. package/tools/bin/branch-verification-guard.sh +0 -364
  51. package/tools/bin/capture-worker.sh +0 -18
  52. package/tools/bin/cleanup-worktree.sh +0 -52
  53. package/tools/bin/codex-quota +0 -31
  54. package/tools/bin/create-follow-up-issue.sh +0 -114
  55. package/tools/bin/dashboard-launchd-bootstrap.sh +0 -50
  56. package/tools/bin/issue-publish-localization-guard.sh +0 -142
  57. package/tools/bin/issue-publish-scope-guard.sh +0 -242
  58. package/tools/bin/issue-requires-local-workspace-install.sh +0 -31
  59. package/tools/bin/issue-resource-class.sh +0 -12
  60. package/tools/bin/kick-scheduler.sh +0 -75
  61. package/tools/bin/label-follow-up-issues.sh +0 -14
  62. package/tools/bin/new-pr-worktree.sh +0 -50
  63. package/tools/bin/new-worktree.sh +0 -49
  64. package/tools/bin/pr-risk.sh +0 -12
  65. package/tools/bin/prepare-worktree.sh +0 -142
  66. package/tools/bin/provider-cooldown-state.sh +0 -204
  67. package/tools/bin/publish-issue-worker.sh +0 -31
  68. package/tools/bin/reconcile-bootstrap-lib.sh +0 -113
  69. package/tools/bin/reconcile-issue-worker.sh +0 -34
  70. package/tools/bin/reconcile-pr-worker.sh +0 -34
  71. package/tools/bin/record-verification.sh +0 -71
  72. package/tools/bin/render-flow-config.sh +0 -98
  73. package/tools/bin/resident-issue-controller-lib.sh +0 -448
  74. package/tools/bin/retry-state.sh +0 -31
  75. package/tools/bin/reuse-issue-worktree.sh +0 -121
  76. package/tools/bin/run-codex-bypass.sh +0 -3
  77. package/tools/bin/run-codex-safe.sh +0 -3
  78. package/tools/bin/run-codex-task.sh +0 -280
  79. package/tools/bin/serve-dashboard.sh +0 -5
  80. package/tools/bin/start-issue-worker.sh +0 -943
  81. package/tools/bin/start-pr-fix-worker.sh +0 -528
  82. package/tools/bin/start-pr-merge-repair-worker.sh +0 -8
  83. package/tools/bin/start-pr-review-worker.sh +0 -261
  84. package/tools/bin/start-resident-issue-loop.sh +0 -499
  85. package/tools/bin/update-github-labels.sh +0 -14
  86. package/tools/bin/worker-status.sh +0 -19
  87. package/tools/bin/workflow-catalog.sh +0 -77
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- # shellcheck source=/dev/null
6
- source "${SCRIPT_DIR}/flow-config-lib.sh"
7
-
8
- FLOW_SKILL_DIR="$(resolve_flow_skill_dir "${BASH_SOURCE[0]}")"
9
- PROFILE_REGISTRY_ROOT="$(resolve_flow_profile_registry_root)"
10
- CONFIG_YAML="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
11
- # Do NOT export execution env for the current profile here — render-flow-config
12
- # is meant to render the SELECTED profile's config (via CONFIG_YAML), and exporting
13
- # the ambient profile's vars into the shell causes config_or_env to silently override
14
- # per-profile YAML with defaults from the current resident worker's own config.
15
- # Also, ambient env vars from the shell are cleared below so they don't leak into
16
- # profile-smoke or other callers.
17
- for _clean in ACP_CODING_WORKER ACP_OPENCLAW_MODEL ACP_CLAUDE_MODEL \
18
- ACP_CLAUDE_TIMEOUT_SECONDS ACP_CLAUDE_MAX_ATTEMPTS ACP_CLAUDE_RETRY_BACKOFF_SECONDS \
19
- ACP_OPENCLAW_THINKING ACP_OPENCLAW_TIMEOUT_SECONDS \
20
- CODING_WORKER; do
21
- unset "${_clean}" 2>/dev/null || true
22
- done
23
- unset _clean
24
- AVAILABLE_PROFILES="$(flow_list_profile_ids "${FLOW_SKILL_DIR}" | paste -sd, -)"
25
- INSTALLED_PROFILES="$(flow_list_installed_profile_ids | paste -sd, -)"
26
- PROFILE_ID="$(flow_resolve_adapter_id "${CONFIG_YAML}")"
27
- PROFILE_SELECTION_MODE="$(flow_profile_selection_mode "${FLOW_SKILL_DIR}")"
28
- PROFILE_SELECTION_HINT="$(flow_profile_selection_hint "${FLOW_SKILL_DIR}")"
29
- PROFILE_NOTES="$(flow_resolve_profile_notes_file "${CONFIG_YAML}")"
30
-
31
- config_or_env() {
32
- local env_names="${1:?env names required}"
33
- local config_key="${2:-}"
34
- local env_name=""
35
- local value=""
36
-
37
- for env_name in ${env_names}; do
38
- value="${!env_name:-}"
39
- if [[ -n "${value}" ]]; then
40
- printf '%s\n' "${value}"
41
- return 0
42
- fi
43
- done
44
-
45
- if [[ -n "${config_key}" && -f "${CONFIG_YAML}" ]]; then
46
- flow_config_get "${CONFIG_YAML}" "${config_key}"
47
- return 0
48
- fi
49
-
50
- printf '\n'
51
- }
52
-
53
- printf 'FLOW_SKILL_DIR=%s\n' "${FLOW_SKILL_DIR}"
54
- printf 'PROFILE_REGISTRY_ROOT=%s\n' "${PROFILE_REGISTRY_ROOT}"
55
- printf 'CONFIG_YAML=%s\n' "${CONFIG_YAML}"
56
- printf 'PROFILE_ID=%s\n' "${PROFILE_ID}"
57
- printf 'PROFILE_SELECTION_MODE=%s\n' "${PROFILE_SELECTION_MODE}"
58
- if [[ -n "${PROFILE_SELECTION_HINT}" ]]; then
59
- printf 'PROFILE_SELECTION_HINT=%s\n' "${PROFILE_SELECTION_HINT}"
60
- fi
61
- printf 'AVAILABLE_PROFILES=%s\n' "${AVAILABLE_PROFILES}"
62
- printf 'INSTALLED_PROFILES=%s\n' "${INSTALLED_PROFILES}"
63
- printf 'PROFILE_NOTES=%s\n' "${PROFILE_NOTES}"
64
- if [[ -f "${PROFILE_NOTES}" ]]; then
65
- printf 'PROFILE_NOTES_EXISTS=yes\n'
66
- else
67
- printf 'PROFILE_NOTES_EXISTS=no\n'
68
- fi
69
- printf 'EFFECTIVE_REPO_ROOT=%s\n' "$(config_or_env 'ACP_REPO_ROOT' repo.root)"
70
- printf 'EFFECTIVE_AGENT_REPO_ROOT=%s\n' "$(config_or_env 'ACP_AGENT_REPO_ROOT' runtime.agent_repo_root)"
71
- printf 'EFFECTIVE_WORKTREE_ROOT=%s\n' "$(config_or_env 'ACP_WORKTREE_ROOT' runtime.worktree_root)"
72
- printf 'EFFECTIVE_RUNS_ROOT=%s\n' "$(config_or_env 'ACP_RUNS_ROOT' runtime.runs_root)"
73
- printf 'EFFECTIVE_STATE_ROOT=%s\n' "$(config_or_env 'ACP_STATE_ROOT' runtime.state_root)"
74
- printf 'EFFECTIVE_RETAINED_REPO_ROOT=%s\n' "$(config_or_env 'ACP_RETAINED_REPO_ROOT' runtime.retained_repo_root)"
75
- printf 'EFFECTIVE_VSCODE_WORKSPACE_FILE=%s\n' "$(config_or_env 'ACP_VSCODE_WORKSPACE_FILE' runtime.vscode_workspace_file)"
76
- printf 'EFFECTIVE_CODING_WORKER=%s\n' "$(config_or_env 'ACP_CODING_WORKER' execution.coding_worker)"
77
- printf 'EFFECTIVE_PROVIDER_QUOTA_COOLDOWNS=%s\n' "$(config_or_env 'ACP_PROVIDER_QUOTA_COOLDOWNS' execution.provider_quota.cooldowns)"
78
- printf 'EFFECTIVE_PROVIDER_POOL_ORDER=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_ORDER' execution.provider_pool_order)"
79
- printf 'EFFECTIVE_PROVIDER_POOL_NAME=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_POOL_NAME')"
80
- printf 'EFFECTIVE_PROVIDER_POOL_BACKEND=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_BACKEND')"
81
- printf 'EFFECTIVE_PROVIDER_POOL_MODEL=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_MODEL')"
82
- printf 'EFFECTIVE_PROVIDER_POOL_KEY=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_KEY')"
83
- printf 'EFFECTIVE_PROVIDER_POOLS_EXHAUSTED=%s\n' "$(config_or_env 'ACP_PROVIDER_POOLS_EXHAUSTED')"
84
- printf 'EFFECTIVE_PROVIDER_POOL_SELECTION_REASON=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_SELECTION_REASON')"
85
- printf 'EFFECTIVE_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH')"
86
- printf 'EFFECTIVE_PROVIDER_POOL_NEXT_ATTEMPT_AT=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT')"
87
- printf 'EFFECTIVE_PROVIDER_POOL_LAST_REASON=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_LAST_REASON')"
88
- printf 'EFFECTIVE_CODEX_PROFILE_SAFE=%s\n' "$(config_or_env 'ACP_CODEX_PROFILE_SAFE' execution.safe_profile)"
89
- printf 'EFFECTIVE_CODEX_PROFILE_BYPASS=%s\n' "$(config_or_env 'ACP_CODEX_PROFILE_BYPASS' execution.bypass_profile)"
90
- printf 'EFFECTIVE_CLAUDE_MODEL=%s\n' "$(config_or_env 'ACP_CLAUDE_MODEL' execution.claude.model)"
91
- printf 'EFFECTIVE_CLAUDE_PERMISSION_MODE=%s\n' "$(config_or_env 'ACP_CLAUDE_PERMISSION_MODE' execution.claude.permission_mode)"
92
- printf 'EFFECTIVE_CLAUDE_EFFORT=%s\n' "$(config_or_env 'ACP_CLAUDE_EFFORT' execution.claude.effort)"
93
- printf 'EFFECTIVE_CLAUDE_TIMEOUT_SECONDS=%s\n' "$(config_or_env 'ACP_CLAUDE_TIMEOUT_SECONDS' execution.claude.timeout_seconds)"
94
- printf 'EFFECTIVE_CLAUDE_MAX_ATTEMPTS=%s\n' "$(config_or_env 'ACP_CLAUDE_MAX_ATTEMPTS' execution.claude.max_attempts)"
95
- printf 'EFFECTIVE_CLAUDE_RETRY_BACKOFF_SECONDS=%s\n' "$(config_or_env 'ACP_CLAUDE_RETRY_BACKOFF_SECONDS' execution.claude.retry_backoff_seconds)"
96
- printf 'EFFECTIVE_OPENCLAW_MODEL=%s\n' "$(config_or_env 'ACP_OPENCLAW_MODEL' execution.openclaw.model)"
97
- printf 'EFFECTIVE_OPENCLAW_THINKING=%s\n' "$(config_or_env 'ACP_OPENCLAW_THINKING' execution.openclaw.thinking)"
98
- printf 'EFFECTIVE_OPENCLAW_TIMEOUT_SECONDS=%s\n' "$(config_or_env 'ACP_OPENCLAW_TIMEOUT_SECONDS' execution.openclaw.timeout_seconds)"
@@ -1,448 +0,0 @@
1
- #!/usr/bin/env bash
2
- # resident-issue-controller-lib.sh — controller_* functions for the resident
3
- # issue loop. Sourced by start-resident-issue-loop.sh to keep the main script
4
- # focused on the top-level loop logic.
5
-
6
- controller_unregister_pending_issue() {
7
- local issue_id="${1:-${ISSUE_ID:-}}"
8
- [[ -n "${issue_id}" ]] || return 0
9
- rm -f "$(issue_pending_file "${issue_id}")"
10
- }
11
-
12
- controller_register_pending_issue() {
13
- [[ -n "${ISSUE_ID:-}" ]] || return 0
14
- printf '%s\n' "$$" >"$(issue_pending_file "${ISSUE_ID}")"
15
- }
16
-
17
- controller_refresh_execution_context() {
18
- unset \
19
- ACP_CODING_WORKER \
20
- ACP_CODEX_PROFILE_SAFE F_LOSNING_CODEX_PROFILE_SAFE \
21
- ACP_CODEX_PROFILE_BYPASS F_LOSNING_CODEX_PROFILE_BYPASS \
22
- ACP_CLAUDE_MODEL F_LOSNING_CLAUDE_MODEL \
23
- ACP_CLAUDE_PERMISSION_MODE F_LOSNING_CLAUDE_PERMISSION_MODE \
24
- ACP_CLAUDE_EFFORT F_LOSNING_CLAUDE_EFFORT \
25
- ACP_CLAUDE_TIMEOUT_SECONDS F_LOSNING_CLAUDE_TIMEOUT_SECONDS \
26
- ACP_CLAUDE_MAX_ATTEMPTS F_LOSNING_CLAUDE_MAX_ATTEMPTS \
27
- ACP_CLAUDE_RETRY_BACKOFF_SECONDS F_LOSNING_CLAUDE_RETRY_BACKOFF_SECONDS \
28
- ACP_OPENCLAW_MODEL F_LOSNING_OPENCLAW_MODEL \
29
- ACP_OPENCLAW_THINKING F_LOSNING_OPENCLAW_THINKING \
30
- ACP_OPENCLAW_TIMEOUT_SECONDS F_LOSNING_OPENCLAW_TIMEOUT_SECONDS \
31
- ACP_OPENCLAW_STALL_SECONDS F_LOSNING_OPENCLAW_STALL_SECONDS \
32
- ACP_ACTIVE_PROVIDER_POOL_NAME F_LOSNING_ACTIVE_PROVIDER_POOL_NAME \
33
- ACP_ACTIVE_PROVIDER_BACKEND F_LOSNING_ACTIVE_PROVIDER_BACKEND \
34
- ACP_ACTIVE_PROVIDER_MODEL F_LOSNING_ACTIVE_PROVIDER_MODEL \
35
- ACP_ACTIVE_PROVIDER_KEY F_LOSNING_ACTIVE_PROVIDER_KEY \
36
- ACP_PROVIDER_POOLS_EXHAUSTED F_LOSNING_PROVIDER_POOLS_EXHAUSTED \
37
- ACP_PROVIDER_POOL_SELECTION_REASON F_LOSNING_PROVIDER_POOL_SELECTION_REASON \
38
- ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH \
39
- ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_AT \
40
- ACP_PROVIDER_POOL_LAST_REASON F_LOSNING_PROVIDER_POOL_LAST_REASON
41
- flow_export_execution_env "${CONFIG_YAML}"
42
- flow_export_project_env_aliases
43
- CODING_WORKER="${ACP_CODING_WORKER:-codex}"
44
- controller_capture_active_provider_context
45
- }
46
-
47
- controller_refresh_issue_lane_context() {
48
- local is_scheduled="${1:-no}"
49
- local schedule_interval_seconds="${2:-0}"
50
-
51
- if [[ "${is_scheduled}" == "yes" ]]; then
52
- ACTIVE_RESIDENT_LANE_KIND="scheduled"
53
- ACTIVE_RESIDENT_LANE_VALUE="${schedule_interval_seconds}"
54
- else
55
- ACTIVE_RESIDENT_LANE_KIND="recurring"
56
- ACTIVE_RESIDENT_LANE_VALUE="general"
57
- fi
58
-
59
- ACTIVE_RESIDENT_WORKER_KEY="$(flow_resident_issue_lane_key "${CODING_WORKER}" "${MODE}" "${ACTIVE_RESIDENT_LANE_KIND}" "${ACTIVE_RESIDENT_LANE_VALUE}")"
60
- ACTIVE_RESIDENT_META_FILE="$(flow_resident_issue_lane_meta_file "${CONFIG_YAML}" "${ACTIVE_RESIDENT_WORKER_KEY}")"
61
- }
62
-
63
- controller_live_lane_peer() {
64
- [[ -n "${ACTIVE_RESIDENT_WORKER_KEY}" ]] || return 1
65
- flow_resident_live_issue_controller_for_key "${CONFIG_YAML}" "${ACTIVE_RESIDENT_WORKER_KEY}" "$$" || return 1
66
- }
67
-
68
- controller_yield_to_live_lane_peer() {
69
- local live_controller=""
70
- local controller_issue_id=""
71
- local controller_state=""
72
-
73
- live_controller="$(controller_live_lane_peer || true)"
74
- [[ -n "${live_controller}" ]] || return 1
75
-
76
- controller_issue_id="$(awk -F= '/^ISSUE_ID=/{print $2; exit}' <<<"${live_controller}")"
77
- controller_state="$(awk -F= '/^CONTROLLER_STATE=/{print $2; exit}' <<<"${live_controller}")"
78
-
79
- if [[ -n "${controller_issue_id}" && "${controller_issue_id}" != "${ISSUE_ID}" ]]; then
80
- flow_resident_issue_enqueue "${CONFIG_YAML}" "${ISSUE_ID}" "resident-live-lane" >/dev/null || true
81
- CONTROLLER_REASON="live-lane-controller-${controller_issue_id}-${controller_state:-running}"
82
- else
83
- CONTROLLER_REASON="duplicate-live-lane-controller"
84
- fi
85
-
86
- return 0
87
- }
88
-
89
- controller_capture_active_provider_context() {
90
- ACTIVE_PROVIDER_POOL_NAME="${ACP_ACTIVE_PROVIDER_POOL_NAME:-${F_LOSNING_ACTIVE_PROVIDER_POOL_NAME:-}}"
91
- ACTIVE_PROVIDER_BACKEND="${ACP_ACTIVE_PROVIDER_BACKEND:-${F_LOSNING_ACTIVE_PROVIDER_BACKEND:-${CODING_WORKER:-}}}"
92
- ACTIVE_PROVIDER_MODEL="${ACP_ACTIVE_PROVIDER_MODEL:-${F_LOSNING_ACTIVE_PROVIDER_MODEL:-}}"
93
- ACTIVE_PROVIDER_KEY="${ACP_ACTIVE_PROVIDER_KEY:-${F_LOSNING_ACTIVE_PROVIDER_KEY:-}}"
94
- ACTIVE_PROVIDER_SELECTION_REASON="${ACP_PROVIDER_POOL_SELECTION_REASON:-${F_LOSNING_PROVIDER_POOL_SELECTION_REASON:-}}"
95
- ACTIVE_PROVIDER_NEXT_ATTEMPT_EPOCH="${ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH:-${F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH:-}}"
96
- ACTIVE_PROVIDER_NEXT_ATTEMPT_AT="${ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT:-${F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_AT:-}}"
97
- ACTIVE_PROVIDER_LAST_REASON="${ACP_PROVIDER_POOL_LAST_REASON:-${F_LOSNING_PROVIDER_POOL_LAST_REASON:-}}"
98
-
99
- if [[ -z "${ACTIVE_PROVIDER_MODEL}" ]]; then
100
- case "${ACTIVE_PROVIDER_BACKEND}" in
101
- openclaw)
102
- ACTIVE_PROVIDER_MODEL="${ACP_OPENCLAW_MODEL:-${F_LOSNING_OPENCLAW_MODEL:-}}"
103
- ;;
104
- claude)
105
- ACTIVE_PROVIDER_MODEL="${ACP_CLAUDE_MODEL:-${F_LOSNING_CLAUDE_MODEL:-}}"
106
- ;;
107
- codex)
108
- ACTIVE_PROVIDER_MODEL="${ACP_CODEX_PROFILE_SAFE:-${F_LOSNING_CODEX_PROFILE_SAFE:-}}"
109
- ;;
110
- opencode)
111
- ACTIVE_PROVIDER_MODEL="${ACP_OPENCODE_MODEL:-${F_LOSNING_OPENCODE_MODEL:-}}"
112
- ;;
113
- kilo)
114
- ACTIVE_PROVIDER_MODEL="${ACP_KILO_MODEL:-${F_LOSNING_KILO_MODEL:-}}"
115
- ;;
116
- esac
117
- fi
118
-
119
- if [[ -z "${ACTIVE_PROVIDER_KEY}" && -n "${ACTIVE_PROVIDER_BACKEND}" && -n "${ACTIVE_PROVIDER_MODEL}" ]]; then
120
- ACTIVE_PROVIDER_KEY="$(flow_sanitize_provider_key "${ACTIVE_PROVIDER_BACKEND}-${ACTIVE_PROVIDER_MODEL}")"
121
- fi
122
- }
123
-
124
- controller_set_recorded_provider_from_active() {
125
- LAST_RECORDED_PROVIDER_POOL_NAME="${ACTIVE_PROVIDER_POOL_NAME}"
126
- LAST_RECORDED_PROVIDER_BACKEND="${ACTIVE_PROVIDER_BACKEND}"
127
- LAST_RECORDED_PROVIDER_MODEL="${ACTIVE_PROVIDER_MODEL}"
128
- LAST_RECORDED_PROVIDER_KEY="${ACTIVE_PROVIDER_KEY}"
129
- }
130
-
131
- controller_mark_provider_launched() {
132
- LAST_LAUNCHED_PROVIDER_POOL_NAME="${ACTIVE_PROVIDER_POOL_NAME}"
133
- LAST_LAUNCHED_PROVIDER_BACKEND="${ACTIVE_PROVIDER_BACKEND}"
134
- LAST_LAUNCHED_PROVIDER_MODEL="${ACTIVE_PROVIDER_MODEL}"
135
- LAST_LAUNCHED_PROVIDER_KEY="${ACTIVE_PROVIDER_KEY}"
136
-
137
- if [[ -z "${LAST_RECORDED_PROVIDER_KEY}" ]]; then
138
- controller_set_recorded_provider_from_active
139
- fi
140
- }
141
-
142
- controller_track_provider_selection() {
143
- local reason="${1:-provider-selection}"
144
- local now_at=""
145
-
146
- [[ -n "${ACTIVE_PROVIDER_KEY}" ]] || return 0
147
-
148
- if [[ -z "${LAST_RECORDED_PROVIDER_KEY}" ]]; then
149
- controller_set_recorded_provider_from_active
150
- return 0
151
- fi
152
-
153
- if [[ "${ACTIVE_PROVIDER_KEY}" == "${LAST_RECORDED_PROVIDER_KEY}" ]]; then
154
- return 0
155
- fi
156
-
157
- now_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
158
- PROVIDER_SWITCH_COUNT=$((PROVIDER_SWITCH_COUNT + 1))
159
- LAST_PROVIDER_SWITCH_AT="${now_at}"
160
- LAST_PROVIDER_SWITCH_REASON="${reason}"
161
- LAST_PROVIDER_FROM_POOL_NAME="${LAST_RECORDED_PROVIDER_POOL_NAME}"
162
- LAST_PROVIDER_FROM_BACKEND="${LAST_RECORDED_PROVIDER_BACKEND}"
163
- LAST_PROVIDER_FROM_MODEL="${LAST_RECORDED_PROVIDER_MODEL}"
164
- LAST_PROVIDER_FROM_KEY="${LAST_RECORDED_PROVIDER_KEY}"
165
- LAST_PROVIDER_TO_POOL_NAME="${ACTIVE_PROVIDER_POOL_NAME}"
166
- LAST_PROVIDER_TO_BACKEND="${ACTIVE_PROVIDER_BACKEND}"
167
- LAST_PROVIDER_TO_MODEL="${ACTIVE_PROVIDER_MODEL}"
168
- LAST_PROVIDER_TO_KEY="${ACTIVE_PROVIDER_KEY}"
169
-
170
- if [[ "${reason}" == "provider-failover" ]]; then
171
- PROVIDER_FAILOVER_COUNT=$((PROVIDER_FAILOVER_COUNT + 1))
172
- LAST_PROVIDER_FAILOVER_AT="${now_at}"
173
- fi
174
-
175
- controller_set_recorded_provider_from_active
176
- }
177
-
178
- controller_adopt_issue() {
179
- local next_issue_id="${1:?issue id required}"
180
- local previous_issue_id="${ISSUE_ID:-}"
181
- local previous_controller_file="${CONTROLLER_FILE:-}"
182
-
183
- if [[ -n "${previous_issue_id}" && "${previous_issue_id}" != "${next_issue_id}" ]]; then
184
- controller_unregister_pending_issue "${previous_issue_id}"
185
- if [[ -n "${previous_controller_file}" && -f "${previous_controller_file}" ]]; then
186
- rm -f "${previous_controller_file}"
187
- fi
188
- fi
189
-
190
- ISSUE_ID="${next_issue_id}"
191
- SESSION="${ISSUE_SESSION_PREFIX}${ISSUE_ID}"
192
- CONTROLLER_FILE="$(flow_resident_issue_controller_file "${CONFIG_YAML}" "${ISSUE_ID}")"
193
- RESIDENT_META_FILE="$(flow_resident_issue_meta_file "${CONFIG_YAML}" "${ISSUE_ID}")"
194
- CONTROLLER_LOOP_COUNT="0"
195
- NEXT_WAKE_EPOCH=""
196
- NEXT_WAKE_AT=""
197
- IDLE_WAIT_STARTED_EPOCH=""
198
- ACTIVE_RESIDENT_WORKER_KEY=""
199
- ACTIVE_RESIDENT_META_FILE=""
200
- ACTIVE_RESIDENT_LANE_KIND=""
201
- ACTIVE_RESIDENT_LANE_VALUE=""
202
- }
203
-
204
- controller_mark_issue_running() {
205
- local is_heavy="no"
206
-
207
- if declare -F heartbeat_issue_is_heavy >/dev/null 2>&1; then
208
- is_heavy="$(heartbeat_issue_is_heavy "${ISSUE_ID}" 2>/dev/null || printf 'no\n')"
209
- fi
210
-
211
- if declare -F heartbeat_mark_issue_running >/dev/null 2>&1; then
212
- heartbeat_mark_issue_running "${ISSUE_ID}" "${is_heavy}" >/dev/null 2>&1 || true
213
- fi
214
- }
215
-
216
- controller_rollback_issue_launch() {
217
- if declare -F heartbeat_issue_launch_failed >/dev/null 2>&1; then
218
- heartbeat_issue_launch_failed "${ISSUE_ID}" >/dev/null 2>&1 || true
219
- fi
220
- }
221
-
222
- controller_adopt_next_recurring_issue() {
223
- local next_issue_id=""
224
- local claim_out=""
225
- local claim_file=""
226
-
227
- claim_out="$(flow_resident_issue_claim_next "${CONFIG_YAML}" "${SESSION}" "${ISSUE_ID}" || true)"
228
- next_issue_id="$(awk -F= '/^ISSUE_ID=/{print $2}' <<<"${claim_out}")"
229
- claim_file="$(awk -F= '/^CLAIM_FILE=/{print $2}' <<<"${claim_out}")"
230
- if [[ -z "${next_issue_id}" ]]; then
231
- next_issue_id="$(select_next_recurring_issue_id || true)"
232
- fi
233
- [[ -n "${next_issue_id}" ]] || return 1
234
-
235
- controller_adopt_issue "${next_issue_id}"
236
- flow_resident_issue_release_claim "${claim_file}"
237
- CONTROLLER_REASON="adopted-next-recurring-issue"
238
- controller_write_state "adopting-issue" ""
239
- return 0
240
- }
241
-
242
- controller_wait_for_leased_issue() {
243
- local idle_timeout="${IDLE_TIMEOUT_SECONDS:-0}"
244
- local now_epoch=""
245
-
246
- case "${idle_timeout}" in
247
- ''|*[!0-9]*) idle_timeout="0" ;;
248
- esac
249
-
250
- if [[ "${idle_timeout}" -le 0 ]]; then
251
- return 1
252
- fi
253
-
254
- if [[ -z "${IDLE_WAIT_STARTED_EPOCH}" ]]; then
255
- IDLE_WAIT_STARTED_EPOCH="$(date +%s)"
256
- fi
257
-
258
- while true; do
259
- if controller_adopt_next_recurring_issue; then
260
- return 0
261
- fi
262
-
263
- now_epoch="$(date +%s)"
264
- if (( now_epoch - IDLE_WAIT_STARTED_EPOCH >= idle_timeout )); then
265
- CONTROLLER_REASON="idle-timeout"
266
- return 1
267
- fi
268
-
269
- controller_write_state "idle" ""
270
- sleep "${POLL_SECONDS}"
271
- done
272
- }
273
-
274
- controller_write_state() {
275
- local state="${1:?state required}"
276
- local reason="${2:-${CONTROLLER_REASON}}"
277
-
278
- CONTROLLER_STATE="${state}"
279
- CONTROLLER_REASON="${reason}"
280
- flow_resident_write_metadata "${CONTROLLER_FILE}" \
281
- "ISSUE_ID=${ISSUE_ID}" \
282
- "SESSION=${SESSION}" \
283
- "CONTROLLER_PID=$$" \
284
- "CONTROLLER_MODE=${MODE}" \
285
- "CONTROLLER_LOOP_COUNT=${CONTROLLER_LOOP_COUNT}" \
286
- "CONTROLLER_STATE=${CONTROLLER_STATE}" \
287
- "CONTROLLER_REASON=${CONTROLLER_REASON}" \
288
- "ACTIVE_RESIDENT_WORKER_KEY=${ACTIVE_RESIDENT_WORKER_KEY}" \
289
- "ACTIVE_RESIDENT_LANE_KIND=${ACTIVE_RESIDENT_LANE_KIND}" \
290
- "ACTIVE_RESIDENT_LANE_VALUE=${ACTIVE_RESIDENT_LANE_VALUE}" \
291
- "ACTIVE_PROVIDER_POOL_NAME=${ACTIVE_PROVIDER_POOL_NAME}" \
292
- "ACTIVE_PROVIDER_BACKEND=${ACTIVE_PROVIDER_BACKEND}" \
293
- "ACTIVE_PROVIDER_MODEL=${ACTIVE_PROVIDER_MODEL}" \
294
- "ACTIVE_PROVIDER_KEY=${ACTIVE_PROVIDER_KEY}" \
295
- "ACTIVE_PROVIDER_SELECTION_REASON=${ACTIVE_PROVIDER_SELECTION_REASON}" \
296
- "ACTIVE_PROVIDER_NEXT_ATTEMPT_EPOCH=${ACTIVE_PROVIDER_NEXT_ATTEMPT_EPOCH}" \
297
- "ACTIVE_PROVIDER_NEXT_ATTEMPT_AT=${ACTIVE_PROVIDER_NEXT_ATTEMPT_AT}" \
298
- "ACTIVE_PROVIDER_LAST_REASON=${ACTIVE_PROVIDER_LAST_REASON}" \
299
- "LAST_LAUNCHED_PROVIDER_POOL_NAME=${LAST_LAUNCHED_PROVIDER_POOL_NAME}" \
300
- "LAST_LAUNCHED_PROVIDER_BACKEND=${LAST_LAUNCHED_PROVIDER_BACKEND}" \
301
- "LAST_LAUNCHED_PROVIDER_MODEL=${LAST_LAUNCHED_PROVIDER_MODEL}" \
302
- "LAST_LAUNCHED_PROVIDER_KEY=${LAST_LAUNCHED_PROVIDER_KEY}" \
303
- "PROVIDER_SWITCH_COUNT=${PROVIDER_SWITCH_COUNT}" \
304
- "PROVIDER_FAILOVER_COUNT=${PROVIDER_FAILOVER_COUNT}" \
305
- "LAST_PROVIDER_SWITCH_AT=${LAST_PROVIDER_SWITCH_AT}" \
306
- "LAST_PROVIDER_SWITCH_REASON=${LAST_PROVIDER_SWITCH_REASON}" \
307
- "LAST_PROVIDER_FROM_POOL_NAME=${LAST_PROVIDER_FROM_POOL_NAME}" \
308
- "LAST_PROVIDER_FROM_BACKEND=${LAST_PROVIDER_FROM_BACKEND}" \
309
- "LAST_PROVIDER_FROM_MODEL=${LAST_PROVIDER_FROM_MODEL}" \
310
- "LAST_PROVIDER_FROM_KEY=${LAST_PROVIDER_FROM_KEY}" \
311
- "LAST_PROVIDER_TO_POOL_NAME=${LAST_PROVIDER_TO_POOL_NAME}" \
312
- "LAST_PROVIDER_TO_BACKEND=${LAST_PROVIDER_TO_BACKEND}" \
313
- "LAST_PROVIDER_TO_MODEL=${LAST_PROVIDER_TO_MODEL}" \
314
- "LAST_PROVIDER_TO_KEY=${LAST_PROVIDER_TO_KEY}" \
315
- "LAST_PROVIDER_FAILOVER_AT=${LAST_PROVIDER_FAILOVER_AT}" \
316
- "PROVIDER_WAIT_COUNT=${PROVIDER_WAIT_COUNT}" \
317
- "PROVIDER_WAIT_TOTAL_SECONDS=${PROVIDER_WAIT_TOTAL_SECONDS}" \
318
- "PROVIDER_LAST_WAIT_SECONDS=${PROVIDER_LAST_WAIT_SECONDS}" \
319
- "PROVIDER_LAST_WAIT_STARTED_AT=${PROVIDER_LAST_WAIT_STARTED_AT}" \
320
- "PROVIDER_LAST_WAIT_COMPLETED_AT=${PROVIDER_LAST_WAIT_COMPLETED_AT}" \
321
- "NEXT_WAKE_EPOCH=${NEXT_WAKE_EPOCH}" \
322
- "NEXT_WAKE_AT=${NEXT_WAKE_AT}" \
323
- "UPDATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
324
-
325
- if [[ "${CONTROLLER_STATE}" == "stopped" ]]; then
326
- controller_unregister_pending_issue "${ISSUE_ID}"
327
- elif flow_resident_issue_controller_counts_as_pending "${CONTROLLER_STATE}"; then
328
- controller_register_pending_issue
329
- else
330
- controller_unregister_pending_issue "${ISSUE_ID}"
331
- fi
332
- }
333
-
334
- controller_last_failure_reason() {
335
- local metadata_file="${ACTIVE_RESIDENT_META_FILE:-${RESIDENT_META_FILE:-}}"
336
- [[ -n "${metadata_file}" && -f "${metadata_file}" ]] || return 1
337
- awk -F= '/^LAST_FAILURE_REASON=/{print $2; exit}' "${metadata_file}" 2>/dev/null | tr -d '"' || true
338
- }
339
-
340
- controller_provider_state() {
341
- local provider_state_script="${FLOW_TOOLS_DIR}/provider-cooldown-state.sh"
342
- local provider_state=""
343
-
344
- if [[ ! -x "${provider_state_script}" ]]; then
345
- printf 'READY=yes\n'
346
- return 0
347
- fi
348
-
349
- provider_state="$(
350
- env \
351
- -u ACP_CODING_WORKER \
352
- -u ACP_CODEX_PROFILE_SAFE -u F_LOSNING_CODEX_PROFILE_SAFE \
353
- -u ACP_CODEX_PROFILE_BYPASS -u F_LOSNING_CODEX_PROFILE_BYPASS \
354
- -u ACP_CLAUDE_MODEL -u F_LOSNING_CLAUDE_MODEL \
355
- -u ACP_CLAUDE_PERMISSION_MODE -u F_LOSNING_CLAUDE_PERMISSION_MODE \
356
- -u ACP_CLAUDE_EFFORT -u F_LOSNING_CLAUDE_EFFORT \
357
- -u ACP_CLAUDE_TIMEOUT_SECONDS -u F_LOSNING_CLAUDE_TIMEOUT_SECONDS \
358
- -u ACP_CLAUDE_MAX_ATTEMPTS -u F_LOSNING_CLAUDE_MAX_ATTEMPTS \
359
- -u ACP_CLAUDE_RETRY_BACKOFF_SECONDS -u F_LOSNING_CLAUDE_RETRY_BACKOFF_SECONDS \
360
- -u ACP_OPENCLAW_MODEL -u F_LOSNING_OPENCLAW_MODEL \
361
- -u ACP_OPENCLAW_THINKING -u F_LOSNING_OPENCLAW_THINKING \
362
- -u ACP_OPENCLAW_TIMEOUT_SECONDS -u F_LOSNING_OPENCLAW_TIMEOUT_SECONDS \
363
- -u ACP_ACTIVE_PROVIDER_POOL_NAME -u F_LOSNING_ACTIVE_PROVIDER_POOL_NAME \
364
- -u ACP_ACTIVE_PROVIDER_BACKEND -u F_LOSNING_ACTIVE_PROVIDER_BACKEND \
365
- -u ACP_ACTIVE_PROVIDER_MODEL -u F_LOSNING_ACTIVE_PROVIDER_MODEL \
366
- -u ACP_ACTIVE_PROVIDER_KEY -u F_LOSNING_ACTIVE_PROVIDER_KEY \
367
- -u ACP_PROVIDER_POOLS_EXHAUSTED -u F_LOSNING_PROVIDER_POOLS_EXHAUSTED \
368
- -u ACP_PROVIDER_POOL_SELECTION_REASON -u F_LOSNING_PROVIDER_POOL_SELECTION_REASON \
369
- -u ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH -u F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH \
370
- -u ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT -u F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_AT \
371
- -u ACP_PROVIDER_POOL_LAST_REASON -u F_LOSNING_PROVIDER_POOL_LAST_REASON \
372
- "${provider_state_script}" get 2>/dev/null || true
373
- )"
374
- if [[ -z "${provider_state}" ]]; then
375
- printf 'READY=yes\n'
376
- return 0
377
- fi
378
-
379
- printf '%s\n' "${provider_state}"
380
- }
381
-
382
- controller_wait_for_provider_capacity() {
383
- local provider_state=""
384
- local provider_ready=""
385
- local provider_next_epoch=""
386
- local provider_next_at=""
387
- local now_epoch=""
388
- local remaining=""
389
- local sleep_seconds=""
390
- local wait_started_epoch=""
391
- local wait_completed_epoch=""
392
-
393
- PROVIDER_WAITED="no"
394
-
395
- while true; do
396
- provider_state="$(controller_provider_state)"
397
- provider_ready="$(flow_kv_get "${provider_state}" "READY")"
398
- if [[ "${provider_ready}" == "yes" ]]; then
399
- if [[ -n "${wait_started_epoch}" ]]; then
400
- wait_completed_epoch="$(date +%s)"
401
- if (( wait_completed_epoch >= wait_started_epoch )); then
402
- PROVIDER_LAST_WAIT_SECONDS=$((wait_completed_epoch - wait_started_epoch))
403
- PROVIDER_WAIT_TOTAL_SECONDS=$((PROVIDER_WAIT_TOTAL_SECONDS + PROVIDER_LAST_WAIT_SECONDS))
404
- PROVIDER_LAST_WAIT_COMPLETED_AT="$(flow_format_epoch_utc "${wait_completed_epoch}")"
405
- fi
406
- fi
407
- NEXT_WAKE_EPOCH=""
408
- NEXT_WAKE_AT=""
409
- return 0
410
- fi
411
-
412
- provider_next_epoch="$(flow_kv_get "${provider_state}" "NEXT_ATTEMPT_EPOCH")"
413
- provider_next_at="$(flow_kv_get "${provider_state}" "NEXT_ATTEMPT_AT")"
414
- if ! [[ "${provider_next_epoch}" =~ ^[0-9]+$ ]] || [[ "${provider_next_epoch}" == "0" ]]; then
415
- return 1
416
- fi
417
-
418
- if [[ -z "${wait_started_epoch}" ]]; then
419
- wait_started_epoch="$(date +%s)"
420
- PROVIDER_WAIT_COUNT=$((PROVIDER_WAIT_COUNT + 1))
421
- PROVIDER_LAST_WAIT_STARTED_AT="$(flow_format_epoch_utc "${wait_started_epoch}")"
422
- fi
423
-
424
- PROVIDER_WAITED="yes"
425
- NEXT_WAKE_EPOCH="${provider_next_epoch}"
426
- NEXT_WAKE_AT="${provider_next_at}"
427
- CONTROLLER_REASON="provider-cooldown"
428
- controller_write_state "waiting-provider" ""
429
-
430
- now_epoch="$(date +%s)"
431
- remaining=$((provider_next_epoch - now_epoch))
432
- sleep_seconds="${POLL_SECONDS}"
433
- if ! [[ "${sleep_seconds}" =~ ^[1-9][0-9]*$ ]]; then
434
- sleep_seconds="60"
435
- fi
436
- if (( remaining > 0 && remaining < sleep_seconds )); then
437
- sleep_seconds="${remaining}"
438
- fi
439
- if (( sleep_seconds <= 0 )); then
440
- sleep_seconds="1"
441
- fi
442
- sleep "${sleep_seconds}"
443
- done
444
- }
445
-
446
- controller_cleanup() {
447
- controller_write_state "stopped" "${CONTROLLER_REASON:-stopped}"
448
- }
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- # shellcheck source=/dev/null
6
- source "${SCRIPT_DIR}/flow-shell-lib.sh"
7
- # shellcheck source=/dev/null
8
- source "${SCRIPT_DIR}/flow-config-lib.sh"
9
-
10
- KIND="${1:?usage: retry-state.sh issue|pr ID get|schedule|clear [reason]}"
11
- ITEM_ID="${2:?usage: retry-state.sh issue|pr ID get|schedule|clear [reason]}"
12
- ACTION="${3:?usage: retry-state.sh issue|pr ID get|schedule|clear [reason]}"
13
- REASON="${4:-}"
14
-
15
- CONFIG_YAML="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
16
- AGENT_ROOT="$(flow_resolve_agent_root "${CONFIG_YAML}")"
17
- STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
18
- COOLDOWNS="$(flow_resolve_retry_cooldowns "${CONFIG_YAML}")"
19
- FLOW_SKILL_DIR="$(resolve_flow_skill_dir "${BASH_SOURCE[0]}")"
20
- FLOW_TOOLS_DIR="${FLOW_SKILL_DIR}/tools/bin"
21
-
22
- ACP_AGENT_ROOT="$AGENT_ROOT" \
23
- ACP_STATE_ROOT="$STATE_ROOT" \
24
- ACP_RETRY_COOLDOWNS="$COOLDOWNS" \
25
- exec bash "${FLOW_TOOLS_DIR}/agent-project-retry-state" \
26
- --state-root "$STATE_ROOT" \
27
- --kind "$KIND" \
28
- --item-id "$ITEM_ID" \
29
- --action "$ACTION" \
30
- --reason "$REASON" \
31
- --cooldowns "$COOLDOWNS"