agent-control-plane 0.1.16 → 0.3.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.
- package/README.md +93 -14
- package/bin/pr-risk.sh +28 -6
- package/hooks/heartbeat-hooks.sh +62 -22
- package/npm/bin/agent-control-plane.js +360 -10
- package/package.json +6 -3
- package/references/architecture.md +8 -0
- package/references/control-plane-map.md +6 -2
- package/references/release-checklist.md +0 -2
- package/tools/bin/agent-github-update-labels +6 -1
- package/tools/bin/agent-project-catch-up-issue-pr-links +118 -0
- package/tools/bin/agent-project-catch-up-merged-prs +78 -21
- package/tools/bin/agent-project-catch-up-scheduled-issue-retries +123 -0
- package/tools/bin/agent-project-cleanup-session +132 -4
- package/tools/bin/agent-project-heartbeat-loop +116 -1461
- package/tools/bin/agent-project-reconcile-issue-session +90 -117
- package/tools/bin/agent-project-reconcile-pr-session +76 -111
- package/tools/bin/agent-project-run-claude-session +12 -2
- package/tools/bin/agent-project-run-codex-resilient +86 -9
- package/tools/bin/agent-project-run-codex-session +16 -5
- package/tools/bin/agent-project-run-kilo-session +356 -14
- package/tools/bin/agent-project-run-ollama-session +658 -0
- package/tools/bin/agent-project-run-openclaw-session +37 -25
- package/tools/bin/agent-project-run-opencode-session +364 -14
- package/tools/bin/agent-project-run-pi-session +479 -0
- package/tools/bin/agent-project-worker-status +11 -8
- package/tools/bin/cleanup-worktree.sh +6 -1
- package/tools/bin/flow-config-lib.sh +196 -3
- package/tools/bin/flow-resident-worker-lib.sh +120 -2
- package/tools/bin/flow-shell-lib.sh +29 -2
- package/tools/bin/heartbeat-loop-cache-lib.sh +164 -0
- package/tools/bin/heartbeat-loop-counting-lib.sh +306 -0
- package/tools/bin/heartbeat-loop-pr-strategy-lib.sh +199 -0
- package/tools/bin/heartbeat-loop-scheduling-lib.sh +506 -0
- package/tools/bin/heartbeat-loop-worker-lib.sh +319 -0
- package/tools/bin/heartbeat-recovery-preflight.sh +13 -1
- package/tools/bin/heartbeat-safe-auto.sh +119 -20
- package/tools/bin/install-project-launchd.sh +19 -2
- package/tools/bin/prepare-worktree.sh +4 -4
- package/tools/bin/profile-activate.sh +2 -2
- package/tools/bin/profile-adopt.sh +2 -2
- package/tools/bin/project-init.sh +1 -1
- package/tools/bin/project-launchd-bootstrap.sh +11 -8
- package/tools/bin/project-runtimectl.sh +90 -7
- package/tools/bin/provider-cooldown-state.sh +14 -14
- package/tools/bin/reconcile-bootstrap-lib.sh +113 -0
- package/tools/bin/render-flow-config.sh +30 -33
- package/tools/bin/resident-issue-controller-lib.sh +448 -0
- package/tools/bin/resident-issue-queue-status.py +35 -0
- package/tools/bin/run-codex-task.sh +53 -4
- package/tools/bin/scaffold-profile.sh +18 -3
- package/tools/bin/start-issue-worker.sh +1 -1
- package/tools/bin/start-pr-fix-worker.sh +30 -0
- package/tools/bin/start-pr-review-worker.sh +31 -0
- package/tools/bin/start-resident-issue-loop.sh +27 -438
- package/tools/bin/sync-agent-repo.sh +2 -2
- package/tools/bin/sync-dependency-baseline.sh +3 -3
- package/tools/bin/sync-shared-agent-home.sh +4 -1
- package/tools/dashboard/app.js +7 -0
- package/tools/dashboard/dashboard_snapshot.py +13 -29
- package/tools/templates/pr-fix-template.md +3 -7
- package/tools/templates/pr-merge-repair-template.md +3 -7
- package/tools/templates/pr-review-template.md +2 -1
- package/SKILL.md +0 -149
|
@@ -133,6 +133,34 @@ PR_LINKED_ISSUE_ID="$(jq -r '.linkedIssueId // ""' <<<"$RISK_JSON")"
|
|
|
133
133
|
PR_FILES_TEXT="$(jq -r '.files[] | "- " + .' <<<"$RISK_JSON")"
|
|
134
134
|
PR_REPO_ROOT="$(flow_resolve_repo_root "${CONFIG_YAML}")"
|
|
135
135
|
PR_DEPENDENCY_SOURCE_ROOT="${ACP_DEPENDENCY_SOURCE_ROOT:-${F_LOSNING_DEPENDENCY_SOURCE_ROOT:-$PR_REPO_ROOT}}"
|
|
136
|
+
render_pr_context_reads_text() {
|
|
137
|
+
local repo_root="${1:?repo root required}"
|
|
138
|
+
local -a candidate_paths=(
|
|
139
|
+
"${repo_root}/AGENTS.md"
|
|
140
|
+
"${repo_root}/openspec/AGENT_RULES.md"
|
|
141
|
+
"${repo_root}/openspec/AGENTS.md"
|
|
142
|
+
"${repo_root}/openspec/project.md"
|
|
143
|
+
"${repo_root}/openspec/CONVENTIONS.md"
|
|
144
|
+
"${repo_root}/docs/TESTING_AND_SEED_POLICY.md"
|
|
145
|
+
)
|
|
146
|
+
local -a existing_paths=()
|
|
147
|
+
local candidate_path=""
|
|
148
|
+
|
|
149
|
+
for candidate_path in "${candidate_paths[@]}"; do
|
|
150
|
+
if [[ -f "${candidate_path}" ]]; then
|
|
151
|
+
existing_paths+=("${candidate_path}")
|
|
152
|
+
fi
|
|
153
|
+
done
|
|
154
|
+
|
|
155
|
+
if [[ "${#existing_paths[@]}" -eq 0 ]]; then
|
|
156
|
+
printf '%s\n' '- No repo-specific context files were found under the expected AGENTS/OpenSpec/testing-doc locations; rely on the current diff and nearby source.'
|
|
157
|
+
return 0
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
printf '%s\n' "${existing_paths[@]}" | sed 's/^/- `/' | sed 's/$/`/'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
PR_CONTEXT_READS_TEXT="$(render_pr_context_reads_text "${PR_REPO_ROOT}")"
|
|
136
164
|
PR_CHECK_FAILURES_TEXT="$(jq -r '(.checkFailures + .pendingChecks)[]? | "- " + .' <<<"$RISK_JSON")"
|
|
137
165
|
if [[ -z "$PR_CHECK_FAILURES_TEXT" ]]; then
|
|
138
166
|
PR_CHECK_FAILURES_TEXT="- none reported"
|
|
@@ -293,6 +321,7 @@ PR_MERGE_STATE_STATUS="$PR_MERGE_STATE_STATUS" \
|
|
|
293
321
|
PR_MERGEABLE_STATUS="$PR_MERGEABLE_STATUS" \
|
|
294
322
|
PR_CHECKS_TEXT="$PR_CHECKS_TEXT" \
|
|
295
323
|
PR_FILES_TEXT="$PR_FILES_TEXT" \
|
|
324
|
+
PR_CONTEXT_READS_TEXT="$PR_CONTEXT_READS_TEXT" \
|
|
296
325
|
PR_CHECK_FAILURES_TEXT="$PR_CHECK_FAILURES_TEXT" \
|
|
297
326
|
PR_MISSING_REASONS_TEXT="$PR_MISSING_REASONS_TEXT" \
|
|
298
327
|
PR_REVIEW_FINDINGS_TEXT="$PR_REVIEW_FINDINGS_TEXT" \
|
|
@@ -403,6 +432,7 @@ const replacements = {
|
|
|
403
432
|
'{PR_MERGEABLE_STATUS}': process.env.PR_MERGEABLE_STATUS || '',
|
|
404
433
|
'{PR_CHECKS_TEXT}': process.env.PR_CHECKS_TEXT || '',
|
|
405
434
|
'{PR_FILES_TEXT}': process.env.PR_FILES_TEXT || '',
|
|
435
|
+
'{PR_CONTEXT_READS_TEXT}': process.env.PR_CONTEXT_READS_TEXT || '',
|
|
406
436
|
'{PR_CHECK_FAILURES_TEXT}': process.env.PR_CHECK_FAILURES_TEXT || '',
|
|
407
437
|
'{PR_MISSING_REASONS_TEXT}': process.env.PR_MISSING_REASONS_TEXT || '',
|
|
408
438
|
'{PR_REVIEW_FINDINGS_TEXT}': process.env.PR_REVIEW_FINDINGS_TEXT || '',
|
|
@@ -123,6 +123,35 @@ PR_FILES_TEXT="$(jq -r '.files[] | "- " + .' <<<"$RISK_JSON")"
|
|
|
123
123
|
PR_REPO_ROOT="$(flow_resolve_repo_root "${CONFIG_YAML}")"
|
|
124
124
|
PR_DEPENDENCY_SOURCE_ROOT="${ACP_DEPENDENCY_SOURCE_ROOT:-${F_LOSNING_DEPENDENCY_SOURCE_ROOT:-$PR_REPO_ROOT}}"
|
|
125
125
|
|
|
126
|
+
render_pr_context_reads_text() {
|
|
127
|
+
local repo_root="${1:?repo root required}"
|
|
128
|
+
local -a candidate_paths=(
|
|
129
|
+
"${repo_root}/AGENTS.md"
|
|
130
|
+
"${repo_root}/openspec/AGENT_RULES.md"
|
|
131
|
+
"${repo_root}/openspec/AGENTS.md"
|
|
132
|
+
"${repo_root}/openspec/project.md"
|
|
133
|
+
"${repo_root}/openspec/CONVENTIONS.md"
|
|
134
|
+
"${repo_root}/docs/TESTING_AND_SEED_POLICY.md"
|
|
135
|
+
)
|
|
136
|
+
local -a existing_paths=()
|
|
137
|
+
local candidate_path=""
|
|
138
|
+
|
|
139
|
+
for candidate_path in "${candidate_paths[@]}"; do
|
|
140
|
+
if [[ -f "${candidate_path}" ]]; then
|
|
141
|
+
existing_paths+=("${candidate_path}")
|
|
142
|
+
fi
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
if [[ "${#existing_paths[@]}" -eq 0 ]]; then
|
|
146
|
+
printf '%s\n' '- No repo-specific context files were found under the expected AGENTS/OpenSpec/testing-doc locations; rely on the current diff and nearby source.'
|
|
147
|
+
return 0
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
printf '%s\n' "${existing_paths[@]}" | sed 's/^/- `/' | sed 's/$/`/'
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
PR_CONTEXT_READS_TEXT="$(render_pr_context_reads_text "${PR_REPO_ROOT}")"
|
|
154
|
+
|
|
126
155
|
case "$PR_AGENT_LANE" in
|
|
127
156
|
double-check-1)
|
|
128
157
|
PR_REVIEW_STAGE_TEXT="Independent agent double-check 1 of 2. A clean pass should advance this PR to the second review pass, not merge it yet."
|
|
@@ -157,6 +186,7 @@ PR_CHECKS_BYPASSED="$PR_CHECKS_BYPASSED" \
|
|
|
157
186
|
PR_MERGE_STATE_STATUS="$PR_MERGE_STATE_STATUS" \
|
|
158
187
|
PR_CHECKS_TEXT="$PR_CHECKS_TEXT" \
|
|
159
188
|
PR_FILES_TEXT="$PR_FILES_TEXT" \
|
|
189
|
+
PR_CONTEXT_READS_TEXT="$PR_CONTEXT_READS_TEXT" \
|
|
160
190
|
PR_REPO_ROOT="$PR_REPO_ROOT" \
|
|
161
191
|
PR_DEPENDENCY_SOURCE_ROOT="$PR_DEPENDENCY_SOURCE_ROOT" \
|
|
162
192
|
REPO_SLUG="$REPO_SLUG" \
|
|
@@ -183,6 +213,7 @@ const replacements = {
|
|
|
183
213
|
'{PR_MERGE_STATE_STATUS}': process.env.PR_MERGE_STATE_STATUS || '',
|
|
184
214
|
'{PR_CHECKS_TEXT}': process.env.PR_CHECKS_TEXT || '',
|
|
185
215
|
'{PR_FILES_TEXT}': process.env.PR_FILES_TEXT || '',
|
|
216
|
+
'{PR_CONTEXT_READS_TEXT}': process.env.PR_CONTEXT_READS_TEXT || '',
|
|
186
217
|
'{REPO_ROOT}': process.env.PR_REPO_ROOT || '',
|
|
187
218
|
'{DEPENDENCY_SOURCE_ROOT}': process.env.PR_DEPENDENCY_SOURCE_ROOT || '',
|
|
188
219
|
};
|
|
@@ -29,7 +29,7 @@ PENDING_LAUNCH_DIR="${ACP_PENDING_LAUNCH_DIR:-${F_LOSNING_PENDING_LAUNCH_DIR:-${
|
|
|
29
29
|
SCHEDULED_STATE_DIR="${STATE_ROOT}/scheduled-issues"
|
|
30
30
|
CONTROLLER_FILE="$(flow_resident_issue_controller_file "${CONFIG_YAML}" "${ISSUE_ID}")"
|
|
31
31
|
RESIDENT_META_FILE="$(flow_resident_issue_meta_file "${CONFIG_YAML}" "${ISSUE_ID}")"
|
|
32
|
-
CODING_WORKER="${ACP_CODING_WORKER
|
|
32
|
+
CODING_WORKER="${ACP_CODING_WORKER:-codex}"
|
|
33
33
|
MAX_IMMEDIATE_CYCLES="$(flow_resident_issue_controller_max_immediate_cycles "${CONFIG_YAML}")"
|
|
34
34
|
POLL_SECONDS="$(flow_resident_issue_controller_poll_seconds "${CONFIG_YAML}")"
|
|
35
35
|
IDLE_TIMEOUT_SECONDS="$(flow_resident_issue_controller_idle_timeout_seconds "${CONFIG_YAML}")"
|
|
@@ -159,16 +159,32 @@ issue_pending_file() {
|
|
|
159
159
|
printf '%s/issue-%s.pid\n' "${PENDING_LAUNCH_DIR}" "${issue_id}"
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
162
|
+
RESIDENT_CONTROLLER_LIB=""
|
|
163
|
+
for _rcl_candidate in \
|
|
164
|
+
"${SCRIPT_DIR}/resident-issue-controller-lib.sh" \
|
|
165
|
+
"${AGENT_CONTROL_PLANE_ROOT:-}/tools/bin/resident-issue-controller-lib.sh" \
|
|
166
|
+
"${ACP_ROOT:-}/tools/bin/resident-issue-controller-lib.sh" \
|
|
167
|
+
"${SHARED_AGENT_HOME:-}/tools/bin/resident-issue-controller-lib.sh"; do
|
|
168
|
+
if [[ -n "${_rcl_candidate}" && -f "${_rcl_candidate}" ]]; then
|
|
169
|
+
RESIDENT_CONTROLLER_LIB="${_rcl_candidate}"
|
|
170
|
+
break
|
|
171
|
+
fi
|
|
172
|
+
done
|
|
173
|
+
if [[ -n "${SHARED_AGENT_HOME:-}" && -z "${RESIDENT_CONTROLLER_LIB}" ]]; then
|
|
174
|
+
for _rcl_skill in "${AGENT_CONTROL_PLANE_SKILL_NAME:-agent-control-plane}" "${AGENT_CONTROL_PLANE_COMPAT_ALIAS:-}"; do
|
|
175
|
+
[[ -n "${_rcl_skill}" ]] || continue
|
|
176
|
+
_rcl_candidate="${SHARED_AGENT_HOME}/skills/openclaw/${_rcl_skill}/tools/bin/resident-issue-controller-lib.sh"
|
|
177
|
+
if [[ -f "${_rcl_candidate}" ]]; then
|
|
178
|
+
RESIDENT_CONTROLLER_LIB="${_rcl_candidate}"
|
|
179
|
+
break
|
|
180
|
+
fi
|
|
181
|
+
done
|
|
182
|
+
fi
|
|
183
|
+
if [[ -z "${RESIDENT_CONTROLLER_LIB}" ]]; then
|
|
184
|
+
echo "unable to locate resident-issue-controller-lib.sh" >&2
|
|
185
|
+
exit 1
|
|
186
|
+
fi
|
|
187
|
+
source "${RESIDENT_CONTROLLER_LIB}"
|
|
172
188
|
|
|
173
189
|
issue_id_is_recurring() {
|
|
174
190
|
local issue_id="${1:?issue id required}"
|
|
@@ -190,161 +206,6 @@ issue_id_is_scheduled() {
|
|
|
190
206
|
issue_json_is_scheduled "$(issue_json_for "${issue_id}" 2>/dev/null || printf '{}\n')"
|
|
191
207
|
}
|
|
192
208
|
|
|
193
|
-
controller_refresh_execution_context() {
|
|
194
|
-
unset \
|
|
195
|
-
ACP_CODING_WORKER F_LOSNING_CODING_WORKER \
|
|
196
|
-
ACP_CODEX_PROFILE_SAFE F_LOSNING_CODEX_PROFILE_SAFE \
|
|
197
|
-
ACP_CODEX_PROFILE_BYPASS F_LOSNING_CODEX_PROFILE_BYPASS \
|
|
198
|
-
ACP_CLAUDE_MODEL F_LOSNING_CLAUDE_MODEL \
|
|
199
|
-
ACP_CLAUDE_PERMISSION_MODE F_LOSNING_CLAUDE_PERMISSION_MODE \
|
|
200
|
-
ACP_CLAUDE_EFFORT F_LOSNING_CLAUDE_EFFORT \
|
|
201
|
-
ACP_CLAUDE_TIMEOUT_SECONDS F_LOSNING_CLAUDE_TIMEOUT_SECONDS \
|
|
202
|
-
ACP_CLAUDE_MAX_ATTEMPTS F_LOSNING_CLAUDE_MAX_ATTEMPTS \
|
|
203
|
-
ACP_CLAUDE_RETRY_BACKOFF_SECONDS F_LOSNING_CLAUDE_RETRY_BACKOFF_SECONDS \
|
|
204
|
-
ACP_OPENCLAW_MODEL F_LOSNING_OPENCLAW_MODEL \
|
|
205
|
-
ACP_OPENCLAW_THINKING F_LOSNING_OPENCLAW_THINKING \
|
|
206
|
-
ACP_OPENCLAW_TIMEOUT_SECONDS F_LOSNING_OPENCLAW_TIMEOUT_SECONDS \
|
|
207
|
-
ACP_OPENCLAW_STALL_SECONDS F_LOSNING_OPENCLAW_STALL_SECONDS \
|
|
208
|
-
ACP_ACTIVE_PROVIDER_POOL_NAME F_LOSNING_ACTIVE_PROVIDER_POOL_NAME \
|
|
209
|
-
ACP_ACTIVE_PROVIDER_BACKEND F_LOSNING_ACTIVE_PROVIDER_BACKEND \
|
|
210
|
-
ACP_ACTIVE_PROVIDER_MODEL F_LOSNING_ACTIVE_PROVIDER_MODEL \
|
|
211
|
-
ACP_ACTIVE_PROVIDER_KEY F_LOSNING_ACTIVE_PROVIDER_KEY \
|
|
212
|
-
ACP_PROVIDER_POOLS_EXHAUSTED F_LOSNING_PROVIDER_POOLS_EXHAUSTED \
|
|
213
|
-
ACP_PROVIDER_POOL_SELECTION_REASON F_LOSNING_PROVIDER_POOL_SELECTION_REASON \
|
|
214
|
-
ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH \
|
|
215
|
-
ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_AT \
|
|
216
|
-
ACP_PROVIDER_POOL_LAST_REASON F_LOSNING_PROVIDER_POOL_LAST_REASON
|
|
217
|
-
flow_export_execution_env "${CONFIG_YAML}"
|
|
218
|
-
flow_export_project_env_aliases
|
|
219
|
-
CODING_WORKER="${ACP_CODING_WORKER:-${F_LOSNING_CODING_WORKER:-codex}}"
|
|
220
|
-
controller_capture_active_provider_context
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
controller_refresh_issue_lane_context() {
|
|
224
|
-
local is_scheduled="${1:-no}"
|
|
225
|
-
local schedule_interval_seconds="${2:-0}"
|
|
226
|
-
|
|
227
|
-
if [[ "${is_scheduled}" == "yes" ]]; then
|
|
228
|
-
ACTIVE_RESIDENT_LANE_KIND="scheduled"
|
|
229
|
-
ACTIVE_RESIDENT_LANE_VALUE="${schedule_interval_seconds}"
|
|
230
|
-
else
|
|
231
|
-
ACTIVE_RESIDENT_LANE_KIND="recurring"
|
|
232
|
-
ACTIVE_RESIDENT_LANE_VALUE="general"
|
|
233
|
-
fi
|
|
234
|
-
|
|
235
|
-
ACTIVE_RESIDENT_WORKER_KEY="$(flow_resident_issue_lane_key "${CODING_WORKER}" "${MODE}" "${ACTIVE_RESIDENT_LANE_KIND}" "${ACTIVE_RESIDENT_LANE_VALUE}")"
|
|
236
|
-
ACTIVE_RESIDENT_META_FILE="$(flow_resident_issue_lane_meta_file "${CONFIG_YAML}" "${ACTIVE_RESIDENT_WORKER_KEY}")"
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
controller_live_lane_peer() {
|
|
240
|
-
[[ -n "${ACTIVE_RESIDENT_WORKER_KEY}" ]] || return 1
|
|
241
|
-
flow_resident_live_issue_controller_for_key "${CONFIG_YAML}" "${ACTIVE_RESIDENT_WORKER_KEY}" "$$" || return 1
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
controller_yield_to_live_lane_peer() {
|
|
245
|
-
local live_controller=""
|
|
246
|
-
local controller_issue_id=""
|
|
247
|
-
local controller_state=""
|
|
248
|
-
|
|
249
|
-
live_controller="$(controller_live_lane_peer || true)"
|
|
250
|
-
[[ -n "${live_controller}" ]] || return 1
|
|
251
|
-
|
|
252
|
-
controller_issue_id="$(awk -F= '/^ISSUE_ID=/{print $2; exit}' <<<"${live_controller}")"
|
|
253
|
-
controller_state="$(awk -F= '/^CONTROLLER_STATE=/{print $2; exit}' <<<"${live_controller}")"
|
|
254
|
-
|
|
255
|
-
if [[ -n "${controller_issue_id}" && "${controller_issue_id}" != "${ISSUE_ID}" ]]; then
|
|
256
|
-
flow_resident_issue_enqueue "${CONFIG_YAML}" "${ISSUE_ID}" "resident-live-lane" >/dev/null || true
|
|
257
|
-
CONTROLLER_REASON="live-lane-controller-${controller_issue_id}-${controller_state:-running}"
|
|
258
|
-
else
|
|
259
|
-
CONTROLLER_REASON="duplicate-live-lane-controller"
|
|
260
|
-
fi
|
|
261
|
-
|
|
262
|
-
return 0
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
controller_capture_active_provider_context() {
|
|
266
|
-
ACTIVE_PROVIDER_POOL_NAME="${ACP_ACTIVE_PROVIDER_POOL_NAME:-${F_LOSNING_ACTIVE_PROVIDER_POOL_NAME:-}}"
|
|
267
|
-
ACTIVE_PROVIDER_BACKEND="${ACP_ACTIVE_PROVIDER_BACKEND:-${F_LOSNING_ACTIVE_PROVIDER_BACKEND:-${CODING_WORKER:-}}}"
|
|
268
|
-
ACTIVE_PROVIDER_MODEL="${ACP_ACTIVE_PROVIDER_MODEL:-${F_LOSNING_ACTIVE_PROVIDER_MODEL:-}}"
|
|
269
|
-
ACTIVE_PROVIDER_KEY="${ACP_ACTIVE_PROVIDER_KEY:-${F_LOSNING_ACTIVE_PROVIDER_KEY:-}}"
|
|
270
|
-
ACTIVE_PROVIDER_SELECTION_REASON="${ACP_PROVIDER_POOL_SELECTION_REASON:-${F_LOSNING_PROVIDER_POOL_SELECTION_REASON:-}}"
|
|
271
|
-
ACTIVE_PROVIDER_NEXT_ATTEMPT_EPOCH="${ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH:-${F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH:-}}"
|
|
272
|
-
ACTIVE_PROVIDER_NEXT_ATTEMPT_AT="${ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT:-${F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_AT:-}}"
|
|
273
|
-
ACTIVE_PROVIDER_LAST_REASON="${ACP_PROVIDER_POOL_LAST_REASON:-${F_LOSNING_PROVIDER_POOL_LAST_REASON:-}}"
|
|
274
|
-
|
|
275
|
-
if [[ -z "${ACTIVE_PROVIDER_MODEL}" ]]; then
|
|
276
|
-
case "${ACTIVE_PROVIDER_BACKEND}" in
|
|
277
|
-
openclaw)
|
|
278
|
-
ACTIVE_PROVIDER_MODEL="${ACP_OPENCLAW_MODEL:-${F_LOSNING_OPENCLAW_MODEL:-}}"
|
|
279
|
-
;;
|
|
280
|
-
claude)
|
|
281
|
-
ACTIVE_PROVIDER_MODEL="${ACP_CLAUDE_MODEL:-${F_LOSNING_CLAUDE_MODEL:-}}"
|
|
282
|
-
;;
|
|
283
|
-
codex)
|
|
284
|
-
ACTIVE_PROVIDER_MODEL="${ACP_CODEX_PROFILE_SAFE:-${F_LOSNING_CODEX_PROFILE_SAFE:-}}"
|
|
285
|
-
;;
|
|
286
|
-
esac
|
|
287
|
-
fi
|
|
288
|
-
|
|
289
|
-
if [[ -z "${ACTIVE_PROVIDER_KEY}" && -n "${ACTIVE_PROVIDER_BACKEND}" && -n "${ACTIVE_PROVIDER_MODEL}" ]]; then
|
|
290
|
-
ACTIVE_PROVIDER_KEY="$(flow_sanitize_provider_key "${ACTIVE_PROVIDER_BACKEND}-${ACTIVE_PROVIDER_MODEL}")"
|
|
291
|
-
fi
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
controller_set_recorded_provider_from_active() {
|
|
295
|
-
LAST_RECORDED_PROVIDER_POOL_NAME="${ACTIVE_PROVIDER_POOL_NAME}"
|
|
296
|
-
LAST_RECORDED_PROVIDER_BACKEND="${ACTIVE_PROVIDER_BACKEND}"
|
|
297
|
-
LAST_RECORDED_PROVIDER_MODEL="${ACTIVE_PROVIDER_MODEL}"
|
|
298
|
-
LAST_RECORDED_PROVIDER_KEY="${ACTIVE_PROVIDER_KEY}"
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
controller_mark_provider_launched() {
|
|
302
|
-
LAST_LAUNCHED_PROVIDER_POOL_NAME="${ACTIVE_PROVIDER_POOL_NAME}"
|
|
303
|
-
LAST_LAUNCHED_PROVIDER_BACKEND="${ACTIVE_PROVIDER_BACKEND}"
|
|
304
|
-
LAST_LAUNCHED_PROVIDER_MODEL="${ACTIVE_PROVIDER_MODEL}"
|
|
305
|
-
LAST_LAUNCHED_PROVIDER_KEY="${ACTIVE_PROVIDER_KEY}"
|
|
306
|
-
|
|
307
|
-
if [[ -z "${LAST_RECORDED_PROVIDER_KEY}" ]]; then
|
|
308
|
-
controller_set_recorded_provider_from_active
|
|
309
|
-
fi
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
controller_track_provider_selection() {
|
|
313
|
-
local reason="${1:-provider-selection}"
|
|
314
|
-
local now_at=""
|
|
315
|
-
|
|
316
|
-
[[ -n "${ACTIVE_PROVIDER_KEY}" ]] || return 0
|
|
317
|
-
|
|
318
|
-
if [[ -z "${LAST_RECORDED_PROVIDER_KEY}" ]]; then
|
|
319
|
-
controller_set_recorded_provider_from_active
|
|
320
|
-
return 0
|
|
321
|
-
fi
|
|
322
|
-
|
|
323
|
-
if [[ "${ACTIVE_PROVIDER_KEY}" == "${LAST_RECORDED_PROVIDER_KEY}" ]]; then
|
|
324
|
-
return 0
|
|
325
|
-
fi
|
|
326
|
-
|
|
327
|
-
now_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
328
|
-
PROVIDER_SWITCH_COUNT=$((PROVIDER_SWITCH_COUNT + 1))
|
|
329
|
-
LAST_PROVIDER_SWITCH_AT="${now_at}"
|
|
330
|
-
LAST_PROVIDER_SWITCH_REASON="${reason}"
|
|
331
|
-
LAST_PROVIDER_FROM_POOL_NAME="${LAST_RECORDED_PROVIDER_POOL_NAME}"
|
|
332
|
-
LAST_PROVIDER_FROM_BACKEND="${LAST_RECORDED_PROVIDER_BACKEND}"
|
|
333
|
-
LAST_PROVIDER_FROM_MODEL="${LAST_RECORDED_PROVIDER_MODEL}"
|
|
334
|
-
LAST_PROVIDER_FROM_KEY="${LAST_RECORDED_PROVIDER_KEY}"
|
|
335
|
-
LAST_PROVIDER_TO_POOL_NAME="${ACTIVE_PROVIDER_POOL_NAME}"
|
|
336
|
-
LAST_PROVIDER_TO_BACKEND="${ACTIVE_PROVIDER_BACKEND}"
|
|
337
|
-
LAST_PROVIDER_TO_MODEL="${ACTIVE_PROVIDER_MODEL}"
|
|
338
|
-
LAST_PROVIDER_TO_KEY="${ACTIVE_PROVIDER_KEY}"
|
|
339
|
-
|
|
340
|
-
if [[ "${reason}" == "provider-failover" ]]; then
|
|
341
|
-
PROVIDER_FAILOVER_COUNT=$((PROVIDER_FAILOVER_COUNT + 1))
|
|
342
|
-
LAST_PROVIDER_FAILOVER_AT="${now_at}"
|
|
343
|
-
fi
|
|
344
|
-
|
|
345
|
-
controller_set_recorded_provider_from_active
|
|
346
|
-
}
|
|
347
|
-
|
|
348
209
|
select_next_recurring_issue_id() {
|
|
349
210
|
local candidate_id=""
|
|
350
211
|
|
|
@@ -371,274 +232,6 @@ select_next_recurring_issue_id() {
|
|
|
371
232
|
return 1
|
|
372
233
|
}
|
|
373
234
|
|
|
374
|
-
controller_adopt_issue() {
|
|
375
|
-
local next_issue_id="${1:?issue id required}"
|
|
376
|
-
local previous_issue_id="${ISSUE_ID:-}"
|
|
377
|
-
local previous_controller_file="${CONTROLLER_FILE:-}"
|
|
378
|
-
|
|
379
|
-
if [[ -n "${previous_issue_id}" && "${previous_issue_id}" != "${next_issue_id}" ]]; then
|
|
380
|
-
controller_unregister_pending_issue "${previous_issue_id}"
|
|
381
|
-
if [[ -n "${previous_controller_file}" && -f "${previous_controller_file}" ]]; then
|
|
382
|
-
rm -f "${previous_controller_file}"
|
|
383
|
-
fi
|
|
384
|
-
fi
|
|
385
|
-
|
|
386
|
-
ISSUE_ID="${next_issue_id}"
|
|
387
|
-
SESSION="${ISSUE_SESSION_PREFIX}${ISSUE_ID}"
|
|
388
|
-
CONTROLLER_FILE="$(flow_resident_issue_controller_file "${CONFIG_YAML}" "${ISSUE_ID}")"
|
|
389
|
-
RESIDENT_META_FILE="$(flow_resident_issue_meta_file "${CONFIG_YAML}" "${ISSUE_ID}")"
|
|
390
|
-
CONTROLLER_LOOP_COUNT="0"
|
|
391
|
-
NEXT_WAKE_EPOCH=""
|
|
392
|
-
NEXT_WAKE_AT=""
|
|
393
|
-
IDLE_WAIT_STARTED_EPOCH=""
|
|
394
|
-
ACTIVE_RESIDENT_WORKER_KEY=""
|
|
395
|
-
ACTIVE_RESIDENT_META_FILE=""
|
|
396
|
-
ACTIVE_RESIDENT_LANE_KIND=""
|
|
397
|
-
ACTIVE_RESIDENT_LANE_VALUE=""
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
controller_mark_issue_running() {
|
|
401
|
-
local is_heavy="no"
|
|
402
|
-
|
|
403
|
-
if declare -F heartbeat_issue_is_heavy >/dev/null 2>&1; then
|
|
404
|
-
is_heavy="$(heartbeat_issue_is_heavy "${ISSUE_ID}" 2>/dev/null || printf 'no\n')"
|
|
405
|
-
fi
|
|
406
|
-
|
|
407
|
-
if declare -F heartbeat_mark_issue_running >/dev/null 2>&1; then
|
|
408
|
-
heartbeat_mark_issue_running "${ISSUE_ID}" "${is_heavy}" >/dev/null 2>&1 || true
|
|
409
|
-
fi
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
controller_rollback_issue_launch() {
|
|
413
|
-
if declare -F heartbeat_issue_launch_failed >/dev/null 2>&1; then
|
|
414
|
-
heartbeat_issue_launch_failed "${ISSUE_ID}" >/dev/null 2>&1 || true
|
|
415
|
-
fi
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
controller_adopt_next_recurring_issue() {
|
|
419
|
-
local next_issue_id=""
|
|
420
|
-
local claim_out=""
|
|
421
|
-
local claim_file=""
|
|
422
|
-
|
|
423
|
-
claim_out="$(flow_resident_issue_claim_next "${CONFIG_YAML}" "${SESSION}" "${ISSUE_ID}" || true)"
|
|
424
|
-
next_issue_id="$(awk -F= '/^ISSUE_ID=/{print $2}' <<<"${claim_out}")"
|
|
425
|
-
claim_file="$(awk -F= '/^CLAIM_FILE=/{print $2}' <<<"${claim_out}")"
|
|
426
|
-
if [[ -z "${next_issue_id}" ]]; then
|
|
427
|
-
next_issue_id="$(select_next_recurring_issue_id || true)"
|
|
428
|
-
fi
|
|
429
|
-
[[ -n "${next_issue_id}" ]] || return 1
|
|
430
|
-
|
|
431
|
-
controller_adopt_issue "${next_issue_id}"
|
|
432
|
-
flow_resident_issue_release_claim "${claim_file}"
|
|
433
|
-
CONTROLLER_REASON="adopted-next-recurring-issue"
|
|
434
|
-
controller_write_state "adopting-issue" ""
|
|
435
|
-
return 0
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
controller_wait_for_leased_issue() {
|
|
439
|
-
local idle_timeout="${IDLE_TIMEOUT_SECONDS:-0}"
|
|
440
|
-
local now_epoch=""
|
|
441
|
-
|
|
442
|
-
case "${idle_timeout}" in
|
|
443
|
-
''|*[!0-9]*) idle_timeout="0" ;;
|
|
444
|
-
esac
|
|
445
|
-
|
|
446
|
-
if [[ "${idle_timeout}" -le 0 ]]; then
|
|
447
|
-
return 1
|
|
448
|
-
fi
|
|
449
|
-
|
|
450
|
-
if [[ -z "${IDLE_WAIT_STARTED_EPOCH}" ]]; then
|
|
451
|
-
IDLE_WAIT_STARTED_EPOCH="$(date +%s)"
|
|
452
|
-
fi
|
|
453
|
-
|
|
454
|
-
while true; do
|
|
455
|
-
if controller_adopt_next_recurring_issue; then
|
|
456
|
-
return 0
|
|
457
|
-
fi
|
|
458
|
-
|
|
459
|
-
now_epoch="$(date +%s)"
|
|
460
|
-
if (( now_epoch - IDLE_WAIT_STARTED_EPOCH >= idle_timeout )); then
|
|
461
|
-
CONTROLLER_REASON="idle-timeout"
|
|
462
|
-
return 1
|
|
463
|
-
fi
|
|
464
|
-
|
|
465
|
-
controller_write_state "idle" ""
|
|
466
|
-
sleep "${POLL_SECONDS}"
|
|
467
|
-
done
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
controller_write_state() {
|
|
471
|
-
local state="${1:?state required}"
|
|
472
|
-
local reason="${2:-${CONTROLLER_REASON}}"
|
|
473
|
-
|
|
474
|
-
CONTROLLER_STATE="${state}"
|
|
475
|
-
CONTROLLER_REASON="${reason}"
|
|
476
|
-
flow_resident_write_metadata "${CONTROLLER_FILE}" \
|
|
477
|
-
"ISSUE_ID=${ISSUE_ID}" \
|
|
478
|
-
"SESSION=${SESSION}" \
|
|
479
|
-
"CONTROLLER_PID=$$" \
|
|
480
|
-
"CONTROLLER_MODE=${MODE}" \
|
|
481
|
-
"CONTROLLER_LOOP_COUNT=${CONTROLLER_LOOP_COUNT}" \
|
|
482
|
-
"CONTROLLER_STATE=${CONTROLLER_STATE}" \
|
|
483
|
-
"CONTROLLER_REASON=${CONTROLLER_REASON}" \
|
|
484
|
-
"ACTIVE_RESIDENT_WORKER_KEY=${ACTIVE_RESIDENT_WORKER_KEY}" \
|
|
485
|
-
"ACTIVE_RESIDENT_LANE_KIND=${ACTIVE_RESIDENT_LANE_KIND}" \
|
|
486
|
-
"ACTIVE_RESIDENT_LANE_VALUE=${ACTIVE_RESIDENT_LANE_VALUE}" \
|
|
487
|
-
"ACTIVE_PROVIDER_POOL_NAME=${ACTIVE_PROVIDER_POOL_NAME}" \
|
|
488
|
-
"ACTIVE_PROVIDER_BACKEND=${ACTIVE_PROVIDER_BACKEND}" \
|
|
489
|
-
"ACTIVE_PROVIDER_MODEL=${ACTIVE_PROVIDER_MODEL}" \
|
|
490
|
-
"ACTIVE_PROVIDER_KEY=${ACTIVE_PROVIDER_KEY}" \
|
|
491
|
-
"ACTIVE_PROVIDER_SELECTION_REASON=${ACTIVE_PROVIDER_SELECTION_REASON}" \
|
|
492
|
-
"ACTIVE_PROVIDER_NEXT_ATTEMPT_EPOCH=${ACTIVE_PROVIDER_NEXT_ATTEMPT_EPOCH}" \
|
|
493
|
-
"ACTIVE_PROVIDER_NEXT_ATTEMPT_AT=${ACTIVE_PROVIDER_NEXT_ATTEMPT_AT}" \
|
|
494
|
-
"ACTIVE_PROVIDER_LAST_REASON=${ACTIVE_PROVIDER_LAST_REASON}" \
|
|
495
|
-
"LAST_LAUNCHED_PROVIDER_POOL_NAME=${LAST_LAUNCHED_PROVIDER_POOL_NAME}" \
|
|
496
|
-
"LAST_LAUNCHED_PROVIDER_BACKEND=${LAST_LAUNCHED_PROVIDER_BACKEND}" \
|
|
497
|
-
"LAST_LAUNCHED_PROVIDER_MODEL=${LAST_LAUNCHED_PROVIDER_MODEL}" \
|
|
498
|
-
"LAST_LAUNCHED_PROVIDER_KEY=${LAST_LAUNCHED_PROVIDER_KEY}" \
|
|
499
|
-
"PROVIDER_SWITCH_COUNT=${PROVIDER_SWITCH_COUNT}" \
|
|
500
|
-
"PROVIDER_FAILOVER_COUNT=${PROVIDER_FAILOVER_COUNT}" \
|
|
501
|
-
"LAST_PROVIDER_SWITCH_AT=${LAST_PROVIDER_SWITCH_AT}" \
|
|
502
|
-
"LAST_PROVIDER_SWITCH_REASON=${LAST_PROVIDER_SWITCH_REASON}" \
|
|
503
|
-
"LAST_PROVIDER_FROM_POOL_NAME=${LAST_PROVIDER_FROM_POOL_NAME}" \
|
|
504
|
-
"LAST_PROVIDER_FROM_BACKEND=${LAST_PROVIDER_FROM_BACKEND}" \
|
|
505
|
-
"LAST_PROVIDER_FROM_MODEL=${LAST_PROVIDER_FROM_MODEL}" \
|
|
506
|
-
"LAST_PROVIDER_FROM_KEY=${LAST_PROVIDER_FROM_KEY}" \
|
|
507
|
-
"LAST_PROVIDER_TO_POOL_NAME=${LAST_PROVIDER_TO_POOL_NAME}" \
|
|
508
|
-
"LAST_PROVIDER_TO_BACKEND=${LAST_PROVIDER_TO_BACKEND}" \
|
|
509
|
-
"LAST_PROVIDER_TO_MODEL=${LAST_PROVIDER_TO_MODEL}" \
|
|
510
|
-
"LAST_PROVIDER_TO_KEY=${LAST_PROVIDER_TO_KEY}" \
|
|
511
|
-
"LAST_PROVIDER_FAILOVER_AT=${LAST_PROVIDER_FAILOVER_AT}" \
|
|
512
|
-
"PROVIDER_WAIT_COUNT=${PROVIDER_WAIT_COUNT}" \
|
|
513
|
-
"PROVIDER_WAIT_TOTAL_SECONDS=${PROVIDER_WAIT_TOTAL_SECONDS}" \
|
|
514
|
-
"PROVIDER_LAST_WAIT_SECONDS=${PROVIDER_LAST_WAIT_SECONDS}" \
|
|
515
|
-
"PROVIDER_LAST_WAIT_STARTED_AT=${PROVIDER_LAST_WAIT_STARTED_AT}" \
|
|
516
|
-
"PROVIDER_LAST_WAIT_COMPLETED_AT=${PROVIDER_LAST_WAIT_COMPLETED_AT}" \
|
|
517
|
-
"NEXT_WAKE_EPOCH=${NEXT_WAKE_EPOCH}" \
|
|
518
|
-
"NEXT_WAKE_AT=${NEXT_WAKE_AT}" \
|
|
519
|
-
"UPDATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
520
|
-
|
|
521
|
-
if [[ "${CONTROLLER_STATE}" == "stopped" ]]; then
|
|
522
|
-
controller_unregister_pending_issue "${ISSUE_ID}"
|
|
523
|
-
elif flow_resident_issue_controller_counts_as_pending "${CONTROLLER_STATE}"; then
|
|
524
|
-
controller_register_pending_issue
|
|
525
|
-
else
|
|
526
|
-
controller_unregister_pending_issue "${ISSUE_ID}"
|
|
527
|
-
fi
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
controller_last_failure_reason() {
|
|
531
|
-
local metadata_file="${ACTIVE_RESIDENT_META_FILE:-${RESIDENT_META_FILE:-}}"
|
|
532
|
-
[[ -n "${metadata_file}" && -f "${metadata_file}" ]] || return 1
|
|
533
|
-
awk -F= '/^LAST_FAILURE_REASON=/{print $2; exit}' "${metadata_file}" 2>/dev/null | tr -d '"' || true
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
controller_provider_state() {
|
|
537
|
-
local provider_state_script="${FLOW_TOOLS_DIR}/provider-cooldown-state.sh"
|
|
538
|
-
local provider_state=""
|
|
539
|
-
|
|
540
|
-
if [[ ! -x "${provider_state_script}" ]]; then
|
|
541
|
-
printf 'READY=yes\n'
|
|
542
|
-
return 0
|
|
543
|
-
fi
|
|
544
|
-
|
|
545
|
-
provider_state="$(
|
|
546
|
-
env \
|
|
547
|
-
-u ACP_CODING_WORKER -u F_LOSNING_CODING_WORKER \
|
|
548
|
-
-u ACP_CODEX_PROFILE_SAFE -u F_LOSNING_CODEX_PROFILE_SAFE \
|
|
549
|
-
-u ACP_CODEX_PROFILE_BYPASS -u F_LOSNING_CODEX_PROFILE_BYPASS \
|
|
550
|
-
-u ACP_CLAUDE_MODEL -u F_LOSNING_CLAUDE_MODEL \
|
|
551
|
-
-u ACP_CLAUDE_PERMISSION_MODE -u F_LOSNING_CLAUDE_PERMISSION_MODE \
|
|
552
|
-
-u ACP_CLAUDE_EFFORT -u F_LOSNING_CLAUDE_EFFORT \
|
|
553
|
-
-u ACP_CLAUDE_TIMEOUT_SECONDS -u F_LOSNING_CLAUDE_TIMEOUT_SECONDS \
|
|
554
|
-
-u ACP_CLAUDE_MAX_ATTEMPTS -u F_LOSNING_CLAUDE_MAX_ATTEMPTS \
|
|
555
|
-
-u ACP_CLAUDE_RETRY_BACKOFF_SECONDS -u F_LOSNING_CLAUDE_RETRY_BACKOFF_SECONDS \
|
|
556
|
-
-u ACP_OPENCLAW_MODEL -u F_LOSNING_OPENCLAW_MODEL \
|
|
557
|
-
-u ACP_OPENCLAW_THINKING -u F_LOSNING_OPENCLAW_THINKING \
|
|
558
|
-
-u ACP_OPENCLAW_TIMEOUT_SECONDS -u F_LOSNING_OPENCLAW_TIMEOUT_SECONDS \
|
|
559
|
-
-u ACP_ACTIVE_PROVIDER_POOL_NAME -u F_LOSNING_ACTIVE_PROVIDER_POOL_NAME \
|
|
560
|
-
-u ACP_ACTIVE_PROVIDER_BACKEND -u F_LOSNING_ACTIVE_PROVIDER_BACKEND \
|
|
561
|
-
-u ACP_ACTIVE_PROVIDER_MODEL -u F_LOSNING_ACTIVE_PROVIDER_MODEL \
|
|
562
|
-
-u ACP_ACTIVE_PROVIDER_KEY -u F_LOSNING_ACTIVE_PROVIDER_KEY \
|
|
563
|
-
-u ACP_PROVIDER_POOLS_EXHAUSTED -u F_LOSNING_PROVIDER_POOLS_EXHAUSTED \
|
|
564
|
-
-u ACP_PROVIDER_POOL_SELECTION_REASON -u F_LOSNING_PROVIDER_POOL_SELECTION_REASON \
|
|
565
|
-
-u ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH -u F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH \
|
|
566
|
-
-u ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT -u F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_AT \
|
|
567
|
-
-u ACP_PROVIDER_POOL_LAST_REASON -u F_LOSNING_PROVIDER_POOL_LAST_REASON \
|
|
568
|
-
"${provider_state_script}" get 2>/dev/null || true
|
|
569
|
-
)"
|
|
570
|
-
if [[ -z "${provider_state}" ]]; then
|
|
571
|
-
printf 'READY=yes\n'
|
|
572
|
-
return 0
|
|
573
|
-
fi
|
|
574
|
-
|
|
575
|
-
printf '%s\n' "${provider_state}"
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
controller_wait_for_provider_capacity() {
|
|
579
|
-
local provider_state=""
|
|
580
|
-
local provider_ready=""
|
|
581
|
-
local provider_next_epoch=""
|
|
582
|
-
local provider_next_at=""
|
|
583
|
-
local now_epoch=""
|
|
584
|
-
local remaining=""
|
|
585
|
-
local sleep_seconds=""
|
|
586
|
-
local wait_started_epoch=""
|
|
587
|
-
local wait_completed_epoch=""
|
|
588
|
-
|
|
589
|
-
PROVIDER_WAITED="no"
|
|
590
|
-
|
|
591
|
-
while true; do
|
|
592
|
-
provider_state="$(controller_provider_state)"
|
|
593
|
-
provider_ready="$(flow_kv_get "${provider_state}" "READY")"
|
|
594
|
-
if [[ "${provider_ready}" == "yes" ]]; then
|
|
595
|
-
if [[ -n "${wait_started_epoch}" ]]; then
|
|
596
|
-
wait_completed_epoch="$(date +%s)"
|
|
597
|
-
if (( wait_completed_epoch >= wait_started_epoch )); then
|
|
598
|
-
PROVIDER_LAST_WAIT_SECONDS=$((wait_completed_epoch - wait_started_epoch))
|
|
599
|
-
PROVIDER_WAIT_TOTAL_SECONDS=$((PROVIDER_WAIT_TOTAL_SECONDS + PROVIDER_LAST_WAIT_SECONDS))
|
|
600
|
-
PROVIDER_LAST_WAIT_COMPLETED_AT="$(date -u -r "${wait_completed_epoch}" +"%Y-%m-%dT%H:%M:%SZ")"
|
|
601
|
-
fi
|
|
602
|
-
fi
|
|
603
|
-
NEXT_WAKE_EPOCH=""
|
|
604
|
-
NEXT_WAKE_AT=""
|
|
605
|
-
return 0
|
|
606
|
-
fi
|
|
607
|
-
|
|
608
|
-
provider_next_epoch="$(flow_kv_get "${provider_state}" "NEXT_ATTEMPT_EPOCH")"
|
|
609
|
-
provider_next_at="$(flow_kv_get "${provider_state}" "NEXT_ATTEMPT_AT")"
|
|
610
|
-
if ! [[ "${provider_next_epoch}" =~ ^[0-9]+$ ]] || [[ "${provider_next_epoch}" == "0" ]]; then
|
|
611
|
-
return 1
|
|
612
|
-
fi
|
|
613
|
-
|
|
614
|
-
if [[ -z "${wait_started_epoch}" ]]; then
|
|
615
|
-
wait_started_epoch="$(date +%s)"
|
|
616
|
-
PROVIDER_WAIT_COUNT=$((PROVIDER_WAIT_COUNT + 1))
|
|
617
|
-
PROVIDER_LAST_WAIT_STARTED_AT="$(date -u -r "${wait_started_epoch}" +"%Y-%m-%dT%H:%M:%SZ")"
|
|
618
|
-
fi
|
|
619
|
-
|
|
620
|
-
PROVIDER_WAITED="yes"
|
|
621
|
-
NEXT_WAKE_EPOCH="${provider_next_epoch}"
|
|
622
|
-
NEXT_WAKE_AT="${provider_next_at}"
|
|
623
|
-
CONTROLLER_REASON="provider-cooldown"
|
|
624
|
-
controller_write_state "waiting-provider" ""
|
|
625
|
-
|
|
626
|
-
now_epoch="$(date +%s)"
|
|
627
|
-
remaining=$((provider_next_epoch - now_epoch))
|
|
628
|
-
sleep_seconds="${POLL_SECONDS}"
|
|
629
|
-
if ! [[ "${sleep_seconds}" =~ ^[1-9][0-9]*$ ]]; then
|
|
630
|
-
sleep_seconds="60"
|
|
631
|
-
fi
|
|
632
|
-
if (( remaining > 0 && remaining < sleep_seconds )); then
|
|
633
|
-
sleep_seconds="${remaining}"
|
|
634
|
-
fi
|
|
635
|
-
if (( sleep_seconds <= 0 )); then
|
|
636
|
-
sleep_seconds="1"
|
|
637
|
-
fi
|
|
638
|
-
sleep "${sleep_seconds}"
|
|
639
|
-
done
|
|
640
|
-
}
|
|
641
|
-
|
|
642
235
|
record_scheduled_next_due() {
|
|
643
236
|
local interval_seconds="${1:-0}"
|
|
644
237
|
local state_file now_epoch next_due_epoch
|
|
@@ -662,10 +255,6 @@ UPDATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
662
255
|
EOF
|
|
663
256
|
}
|
|
664
257
|
|
|
665
|
-
controller_cleanup() {
|
|
666
|
-
controller_write_state "stopped" "${CONTROLLER_REASON:-stopped}"
|
|
667
|
-
}
|
|
668
|
-
|
|
669
258
|
trap 'CONTROLLER_REASON="${CONTROLLER_REASON:-terminated}"; controller_cleanup' EXIT
|
|
670
259
|
trap 'CONTROLLER_REASON="interrupted"; exit 0' INT TERM
|
|
671
260
|
|
|
@@ -6,11 +6,11 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
6
6
|
source "${SCRIPT_DIR}/flow-config-lib.sh"
|
|
7
7
|
|
|
8
8
|
CONFIG_YAML="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
|
|
9
|
-
SOURCE_REPO_ROOT="${ACP_SOURCE_REPO_ROOT:-$
|
|
9
|
+
SOURCE_REPO_ROOT="${ACP_SOURCE_REPO_ROOT:-$(flow_resolve_retained_repo_root "${CONFIG_YAML}")}"
|
|
10
10
|
CANONICAL_REPO_ROOT="$(flow_resolve_repo_root "${CONFIG_YAML}")"
|
|
11
11
|
AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
|
|
12
12
|
DEFAULT_BRANCH="$(flow_resolve_default_branch "${CONFIG_YAML}")"
|
|
13
|
-
REMOTE_NAME="${ACP_REMOTE_NAME
|
|
13
|
+
REMOTE_NAME="${ACP_REMOTE_NAME:-origin}"
|
|
14
14
|
FLOW_SKILL_DIR="$(resolve_flow_skill_dir "${BASH_SOURCE[0]}")"
|
|
15
15
|
FLOW_TOOLS_DIR="${FLOW_SKILL_DIR}/tools/bin"
|
|
16
16
|
|
|
@@ -13,9 +13,9 @@ STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
|
|
|
13
13
|
LOCK_DIR="${STATE_ROOT}/dependency-baseline.lock"
|
|
14
14
|
PID_FILE="${LOCK_DIR}/pid"
|
|
15
15
|
HASH_FILE="${STATE_ROOT}/dependency-baseline.sha256"
|
|
16
|
-
PACKAGE_MANAGER_BIN="${ACP_PACKAGE_MANAGER_BIN
|
|
17
|
-
WORKSPACE_BUILD_PACKAGES_RAW="${ACP_WORKSPACE_BUILD_PACKAGES
|
|
18
|
-
WORKSPACE_BUILD_ARTIFACTS_RAW="${ACP_WORKSPACE_BUILD_ARTIFACTS
|
|
16
|
+
PACKAGE_MANAGER_BIN="${ACP_PACKAGE_MANAGER_BIN:-pnpm}"
|
|
17
|
+
WORKSPACE_BUILD_PACKAGES_RAW="${ACP_WORKSPACE_BUILD_PACKAGES:-}"
|
|
18
|
+
WORKSPACE_BUILD_ARTIFACTS_RAW="${ACP_WORKSPACE_BUILD_ARTIFACTS:-}"
|
|
19
19
|
declare -a WORKSPACE_BUILD_PACKAGES=()
|
|
20
20
|
declare -a WORKSPACE_BUILD_ARTIFACTS=()
|
|
21
21
|
|
|
@@ -207,7 +207,10 @@ sync_tree_rsync() {
|
|
|
207
207
|
local target_dir="${2:?target dir required}"
|
|
208
208
|
[[ -d "${source_dir}" ]] || return 0
|
|
209
209
|
mkdir -p "${target_dir}"
|
|
210
|
-
rsync -a --delete --exclude='.git/' "${source_dir}/" "${target_dir}/"
|
|
210
|
+
if rsync -a --delete --exclude='.git/' "${source_dir}/" "${target_dir}/"; then
|
|
211
|
+
return 0
|
|
212
|
+
fi
|
|
213
|
+
sync_tree_copy_mode "${source_dir}" "${target_dir}"
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
reset_runtime_skill_targets() {
|