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.
Files changed (63) hide show
  1. package/README.md +93 -14
  2. package/bin/pr-risk.sh +28 -6
  3. package/hooks/heartbeat-hooks.sh +62 -22
  4. package/npm/bin/agent-control-plane.js +360 -10
  5. package/package.json +6 -3
  6. package/references/architecture.md +8 -0
  7. package/references/control-plane-map.md +6 -2
  8. package/references/release-checklist.md +0 -2
  9. package/tools/bin/agent-github-update-labels +6 -1
  10. package/tools/bin/agent-project-catch-up-issue-pr-links +118 -0
  11. package/tools/bin/agent-project-catch-up-merged-prs +78 -21
  12. package/tools/bin/agent-project-catch-up-scheduled-issue-retries +123 -0
  13. package/tools/bin/agent-project-cleanup-session +132 -4
  14. package/tools/bin/agent-project-heartbeat-loop +116 -1461
  15. package/tools/bin/agent-project-reconcile-issue-session +90 -117
  16. package/tools/bin/agent-project-reconcile-pr-session +76 -111
  17. package/tools/bin/agent-project-run-claude-session +12 -2
  18. package/tools/bin/agent-project-run-codex-resilient +86 -9
  19. package/tools/bin/agent-project-run-codex-session +16 -5
  20. package/tools/bin/agent-project-run-kilo-session +356 -14
  21. package/tools/bin/agent-project-run-ollama-session +658 -0
  22. package/tools/bin/agent-project-run-openclaw-session +37 -25
  23. package/tools/bin/agent-project-run-opencode-session +364 -14
  24. package/tools/bin/agent-project-run-pi-session +479 -0
  25. package/tools/bin/agent-project-worker-status +11 -8
  26. package/tools/bin/cleanup-worktree.sh +6 -1
  27. package/tools/bin/flow-config-lib.sh +196 -3
  28. package/tools/bin/flow-resident-worker-lib.sh +120 -2
  29. package/tools/bin/flow-shell-lib.sh +29 -2
  30. package/tools/bin/heartbeat-loop-cache-lib.sh +164 -0
  31. package/tools/bin/heartbeat-loop-counting-lib.sh +306 -0
  32. package/tools/bin/heartbeat-loop-pr-strategy-lib.sh +199 -0
  33. package/tools/bin/heartbeat-loop-scheduling-lib.sh +506 -0
  34. package/tools/bin/heartbeat-loop-worker-lib.sh +319 -0
  35. package/tools/bin/heartbeat-recovery-preflight.sh +13 -1
  36. package/tools/bin/heartbeat-safe-auto.sh +119 -20
  37. package/tools/bin/install-project-launchd.sh +19 -2
  38. package/tools/bin/prepare-worktree.sh +4 -4
  39. package/tools/bin/profile-activate.sh +2 -2
  40. package/tools/bin/profile-adopt.sh +2 -2
  41. package/tools/bin/project-init.sh +1 -1
  42. package/tools/bin/project-launchd-bootstrap.sh +11 -8
  43. package/tools/bin/project-runtimectl.sh +90 -7
  44. package/tools/bin/provider-cooldown-state.sh +14 -14
  45. package/tools/bin/reconcile-bootstrap-lib.sh +113 -0
  46. package/tools/bin/render-flow-config.sh +30 -33
  47. package/tools/bin/resident-issue-controller-lib.sh +448 -0
  48. package/tools/bin/resident-issue-queue-status.py +35 -0
  49. package/tools/bin/run-codex-task.sh +53 -4
  50. package/tools/bin/scaffold-profile.sh +18 -3
  51. package/tools/bin/start-issue-worker.sh +1 -1
  52. package/tools/bin/start-pr-fix-worker.sh +30 -0
  53. package/tools/bin/start-pr-review-worker.sh +31 -0
  54. package/tools/bin/start-resident-issue-loop.sh +27 -438
  55. package/tools/bin/sync-agent-repo.sh +2 -2
  56. package/tools/bin/sync-dependency-baseline.sh +3 -3
  57. package/tools/bin/sync-shared-agent-home.sh +4 -1
  58. package/tools/dashboard/app.js +7 -0
  59. package/tools/dashboard/dashboard_snapshot.py +13 -29
  60. package/tools/templates/pr-fix-template.md +3 -7
  61. package/tools/templates/pr-merge-repair-template.md +3 -7
  62. package/tools/templates/pr-review-template.md +2 -1
  63. package/SKILL.md +0 -149
@@ -4,16 +4,9 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  FLOW_SKILL_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
6
6
  HOME_DIR="${ACP_PROJECT_RUNTIME_HOME_DIR:-${HOME:-}}"
7
- SOURCE_HOME="${ACP_PROJECT_RUNTIME_SOURCE_HOME:-}"
8
- RUNTIME_HOME="${ACP_PROJECT_RUNTIME_RUNTIME_HOME:-${HOME_DIR}/.agent-runtime/runtime-home}"
9
7
  PROFILE_REGISTRY_ROOT="${ACP_PROJECT_RUNTIME_PROFILE_REGISTRY_ROOT:-${ACP_PROFILE_REGISTRY_ROOT:-${HOME_DIR}/.agent-runtime/control-plane/profiles}}"
10
8
  PROFILE_ID="${ACP_PROJECT_RUNTIME_PROFILE_ID:-${ACP_PROJECT_ID:-${AGENT_PROJECT_ID:-}}}"
11
9
  ENV_FILE="${ACP_PROJECT_RUNTIME_ENV_FILE:-${PROFILE_REGISTRY_ROOT}/${PROFILE_ID}/runtime.env}"
12
- BASE_PATH="${ACP_PROJECT_RUNTIME_PATH:-/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin}"
13
- SYNC_SCRIPT="${ACP_PROJECT_RUNTIME_SYNC_SCRIPT:-${FLOW_SKILL_DIR}/tools/bin/sync-shared-agent-home.sh}"
14
- ENSURE_SYNC_SCRIPT="${ACP_PROJECT_RUNTIME_ENSURE_SYNC_SCRIPT:-${FLOW_SKILL_DIR}/tools/bin/ensure-runtime-sync.sh}"
15
- RUNTIME_HEARTBEAT_SCRIPT="${ACP_PROJECT_RUNTIME_HEARTBEAT_SCRIPT:-${RUNTIME_HOME}/skills/openclaw/agent-control-plane/tools/bin/heartbeat-safe-auto.sh}"
16
- ALWAYS_SYNC="${ACP_PROJECT_RUNTIME_ALWAYS_SYNC:-0}"
17
10
 
18
11
  if [[ -z "${HOME_DIR}" ]]; then
19
12
  echo "project launchd bootstrap requires HOME or ACP_PROJECT_RUNTIME_HOME_DIR" >&2
@@ -26,7 +19,6 @@ if [[ -z "${PROFILE_ID}" ]]; then
26
19
  fi
27
20
 
28
21
  export HOME="${HOME_DIR}"
29
- export PATH="${BASE_PATH}"
30
22
  export ACP_PROFILE_REGISTRY_ROOT="${PROFILE_REGISTRY_ROOT}"
31
23
  export ACP_PROJECT_ID="${PROFILE_ID}"
32
24
  export AGENT_PROJECT_ID="${PROFILE_ID}"
@@ -38,6 +30,17 @@ if [[ -f "${ENV_FILE}" ]]; then
38
30
  set +a
39
31
  fi
40
32
 
33
+ # Resolve launch paths after runtime.env overrides are loaded so launchd can
34
+ # pin the project runtime to a source checkout or alternate runtime home.
35
+ SOURCE_HOME="${ACP_PROJECT_RUNTIME_SOURCE_HOME:-}"
36
+ RUNTIME_HOME="${ACP_PROJECT_RUNTIME_RUNTIME_HOME:-${HOME_DIR}/.agent-runtime/runtime-home}"
37
+ BASE_PATH="${ACP_PROJECT_RUNTIME_PATH:-/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin}"
38
+ SYNC_SCRIPT="${ACP_PROJECT_RUNTIME_SYNC_SCRIPT:-${FLOW_SKILL_DIR}/tools/bin/sync-shared-agent-home.sh}"
39
+ ENSURE_SYNC_SCRIPT="${ACP_PROJECT_RUNTIME_ENSURE_SYNC_SCRIPT:-${FLOW_SKILL_DIR}/tools/bin/ensure-runtime-sync.sh}"
40
+ RUNTIME_HEARTBEAT_SCRIPT="${ACP_PROJECT_RUNTIME_HEARTBEAT_SCRIPT:-${RUNTIME_HOME}/skills/openclaw/agent-control-plane/tools/bin/heartbeat-safe-auto.sh}"
41
+ ALWAYS_SYNC="${ACP_PROJECT_RUNTIME_ALWAYS_SYNC:-0}"
42
+ export PATH="${BASE_PATH}"
43
+
41
44
  if [[ ! -x "${ENSURE_SYNC_SCRIPT}" && ! -x "${SYNC_SCRIPT}" ]]; then
42
45
  echo "project launchd bootstrap missing sync helper: ${ENSURE_SYNC_SCRIPT}" >&2
43
46
  exit 65
@@ -168,13 +168,15 @@ join_by_comma() {
168
168
  runtime_started() {
169
169
  local heartbeat=""
170
170
  local shared_loop=""
171
- local supervisor=""
171
+ local active_sessions=""
172
+ local pending_pids=""
172
173
 
173
174
  heartbeat="$(heartbeat_pid)"
174
175
  shared_loop="$(shared_loop_pid)"
175
- supervisor="$(supervisor_pid)"
176
+ active_sessions="$(collect_active_tmux_sessions | sort -u)"
177
+ pending_pids="$(collect_pending_launch_pids | sort -u)"
176
178
 
177
- [[ -n "${heartbeat}" || -n "${shared_loop}" || -n "${supervisor}" ]]
179
+ [[ -n "${heartbeat}" || -n "${shared_loop}" || -n "${active_sessions}" || -n "${pending_pids}" ]]
178
180
  }
179
181
 
180
182
  wait_for_runtime_start() {
@@ -197,6 +199,13 @@ sync_stamp_value() {
197
199
  | sed -e "s/^'//" -e "s/'$//"
198
200
  }
199
201
 
202
+ shared_loop_status_value() {
203
+ local key="${1:?key required}"
204
+ local file="${STATE_ROOT}/shared-heartbeat-loop.env"
205
+ [[ -f "${file}" ]] || return 1
206
+ awk -F= -v target="${key}" '$1 == target {print $2; exit}' "${file}" 2>/dev/null
207
+ }
208
+
200
209
  tmux_session_exists() {
201
210
  local session="${1:-}"
202
211
  [[ -n "${TMUX_BIN}" && -n "${session}" ]] || return 1
@@ -218,14 +227,53 @@ collect_run_sessions() {
218
227
  done < <(find "${RUNS_ROOT}" -mindepth 2 -maxdepth 2 -type f -name run.env 2>/dev/null | sort -u)
219
228
  }
220
229
 
230
+ list_tmux_sessions() {
231
+ [[ -n "${TMUX_BIN}" ]] || return 0
232
+ "${TMUX_BIN}" list-sessions -F '#S' 2>/dev/null || true
233
+ }
234
+
235
+ collect_profile_tmux_sessions() {
236
+ local issue_prefix=""
237
+ local pr_prefix=""
238
+ local session=""
239
+
240
+ issue_prefix="$(flow_resolve_issue_session_prefix "${CONFIG_YAML}")"
241
+ pr_prefix="$(flow_resolve_pr_session_prefix "${CONFIG_YAML}")"
242
+
243
+ while IFS= read -r session; do
244
+ [[ -n "${session}" ]] || continue
245
+ case "${session}" in
246
+ "${issue_prefix}"*|"${pr_prefix}"*)
247
+ printf '%s\n' "${session}"
248
+ ;;
249
+ esac
250
+ done < <(list_tmux_sessions)
251
+ }
252
+
221
253
  collect_active_tmux_sessions() {
254
+ local known_sessions=""
222
255
  local session=""
256
+ known_sessions="$(collect_run_sessions | sort -u)"
257
+
223
258
  while IFS= read -r session; do
224
259
  [[ -n "${session}" ]] || continue
225
- if tmux_session_exists "${session}"; then
260
+ if tmux_session_exists "${session}" && grep -Fxq "${session}" <<<"${known_sessions}"; then
226
261
  printf '%s\n' "${session}"
227
262
  fi
228
- done < <(collect_run_sessions)
263
+ done < <(collect_profile_tmux_sessions)
264
+ }
265
+
266
+ collect_stale_tmux_sessions() {
267
+ local known_sessions=""
268
+ local session=""
269
+ known_sessions="$(collect_run_sessions | sort -u)"
270
+
271
+ while IFS= read -r session; do
272
+ [[ -n "${session}" ]] || continue
273
+ if tmux_session_exists "${session}" && ! grep -Fxq "${session}" <<<"${known_sessions}"; then
274
+ printf '%s\n' "${session}"
275
+ fi
276
+ done < <(collect_profile_tmux_sessions)
229
277
  }
230
278
 
231
279
  collect_repo_shared_loop_pids() {
@@ -336,15 +384,21 @@ print_status() {
336
384
  local supervisor=""
337
385
  local controller_pids=""
338
386
  local active_sessions=""
387
+ local stale_sessions=""
339
388
  local pending_pids=""
340
389
  local runtime_status="stopped"
341
390
  local controller_count="0"
342
391
  local active_session_count="0"
392
+ local stale_session_count="0"
343
393
  local pending_count="0"
344
394
  local launchd_state=""
345
395
  local runtime_sync_status=""
346
396
  local runtime_sync_updated_at=""
347
397
  local runtime_sync_fingerprint=""
398
+ local shared_loop_state=""
399
+ local shared_loop_last_status=""
400
+ local shared_loop_started_at=""
401
+ local shared_loop_updated_at=""
348
402
 
349
403
  heartbeat="$(heartbeat_pid)"
350
404
  shared_loop="$(shared_loop_pid)"
@@ -352,16 +406,18 @@ print_status() {
352
406
  heartbeat_parent="$(heartbeat_parent_pid)"
353
407
  controller_pids="$(collect_controller_pids | sort -u)"
354
408
  active_sessions="$(collect_active_tmux_sessions | sort -u)"
409
+ stale_sessions="$(collect_stale_tmux_sessions | sort -u)"
355
410
  pending_pids="$(collect_pending_launch_pids | sort -u)"
356
411
 
357
412
  [[ -n "${controller_pids}" ]] && controller_count="$(printf '%s\n' "${controller_pids}" | awk 'NF {c+=1} END {print c+0}')"
358
413
  [[ -n "${active_sessions}" ]] && active_session_count="$(printf '%s\n' "${active_sessions}" | awk 'NF {c+=1} END {print c+0}')"
414
+ [[ -n "${stale_sessions}" ]] && stale_session_count="$(printf '%s\n' "${stale_sessions}" | awk 'NF {c+=1} END {print c+0}')"
359
415
  [[ -n "${pending_pids}" ]] && pending_count="$(printf '%s\n' "${pending_pids}" | awk 'NF {c+=1} END {print c+0}')"
360
416
 
361
- if [[ -n "${heartbeat}" || -n "${shared_loop}" || -n "${supervisor}" || "${controller_count}" != "0" || "${active_session_count}" != "0" ]]; then
417
+ if [[ -n "${heartbeat}" || -n "${shared_loop}" || "${active_session_count}" != "0" ]]; then
362
418
  runtime_status="running"
363
419
  fi
364
- if [[ -z "${heartbeat}" && -z "${supervisor}" && "${active_session_count}" == "0" && ( -n "${shared_loop}" || "${controller_count}" != "0" ) ]]; then
420
+ if [[ "${runtime_status}" != "running" && ( -n "${supervisor}" || "${controller_count}" != "0" || "${pending_count}" != "0" || "${stale_session_count}" != "0" ) ]]; then
365
421
  runtime_status="partial"
366
422
  fi
367
423
 
@@ -369,6 +425,10 @@ print_status() {
369
425
  runtime_sync_status="$(sync_stamp_value "SYNC_STATUS" || true)"
370
426
  runtime_sync_updated_at="$(sync_stamp_value "UPDATED_AT" || true)"
371
427
  runtime_sync_fingerprint="$(sync_stamp_value "SOURCE_FINGERPRINT" || true)"
428
+ shared_loop_state="$(shared_loop_status_value "STATE" || true)"
429
+ shared_loop_last_status="$(shared_loop_status_value "STATUS" || true)"
430
+ shared_loop_started_at="$(shared_loop_status_value "STARTED_AT" || true)"
431
+ shared_loop_updated_at="$(shared_loop_status_value "UPDATED_AT" || true)"
372
432
 
373
433
  printf 'PROFILE_ID=%s\n' "${PROFILE_ID}"
374
434
  printf 'CONFIG_YAML=%s\n' "${CONFIG_YAML}"
@@ -382,12 +442,18 @@ print_status() {
382
442
  printf 'HEARTBEAT_PID=%s\n' "${heartbeat}"
383
443
  printf 'HEARTBEAT_PARENT_PID=%s\n' "${heartbeat_parent}"
384
444
  printf 'SHARED_LOOP_PID=%s\n' "${shared_loop}"
445
+ printf 'SHARED_LOOP_STATE=%s\n' "${shared_loop_state}"
446
+ printf 'SHARED_LOOP_LAST_STATUS=%s\n' "${shared_loop_last_status}"
447
+ printf 'SHARED_LOOP_STARTED_AT=%s\n' "${shared_loop_started_at}"
448
+ printf 'SHARED_LOOP_UPDATED_AT=%s\n' "${shared_loop_updated_at}"
385
449
  printf 'SUPERVISOR_PID=%s\n' "${supervisor}"
386
450
  printf 'CONTROLLER_COUNT=%s\n' "${controller_count}"
387
451
  printf 'ACTIVE_TMUX_SESSION_COUNT=%s\n' "${active_session_count}"
452
+ printf 'STALE_TMUX_SESSION_COUNT=%s\n' "${stale_session_count}"
388
453
  printf 'PENDING_LAUNCH_COUNT=%s\n' "${pending_count}"
389
454
  printf 'CONTROLLER_PIDS=%s\n' "$(printf '%s\n' "${controller_pids}" | join_by_comma)"
390
455
  printf 'ACTIVE_TMUX_SESSIONS=%s\n' "$(printf '%s\n' "${active_sessions}" | join_by_comma)"
456
+ printf 'STALE_TMUX_SESSIONS=%s\n' "$(printf '%s\n' "${stale_sessions}" | join_by_comma)"
391
457
  printf 'PENDING_LAUNCH_PIDS=%s\n' "$(printf '%s\n' "${pending_pids}" | join_by_comma)"
392
458
  printf 'SYNC_STAMP_FILE=%s\n' "${SYNC_STAMP_FILE}"
393
459
  printf 'RUNTIME_SYNC_STATUS=%s\n' "${runtime_sync_status}"
@@ -452,12 +518,18 @@ clear_running_labels_after_stop() {
452
518
  fi
453
519
 
454
520
  issue_json="$(flow_github_issue_list_json "${REPO_SLUG}" open 100 2>/dev/null || printf '[]\n')"
521
+ if [[ "${issue_json}" == "[]" ]]; then
522
+ issue_json="$(gh issue list -R "${REPO_SLUG}" --state open --limit 100 --json number,labels 2>/dev/null || printf '[]\n')"
523
+ fi
455
524
  while IFS= read -r number; do
456
525
  [[ -n "${number}" ]] || continue
457
526
  bash "${UPDATE_LABELS_SCRIPT}" --repo-slug "${REPO_SLUG}" --number "${number}" --remove agent-running >/dev/null 2>&1 || true
458
527
  done < <(jq -r '.[] | select(any(.labels[]?; .name == "agent-running")) | .number' <<<"${issue_json}" 2>/dev/null || true)
459
528
 
460
529
  pr_json="$(flow_github_pr_list_json "${REPO_SLUG}" open 100 2>/dev/null || printf '[]\n')"
530
+ if [[ "${pr_json}" == "[]" ]]; then
531
+ pr_json="$(gh pr list -R "${REPO_SLUG}" --state open --limit 100 --json number,labels 2>/dev/null || printf '[]\n')"
532
+ fi
461
533
  while IFS= read -r number; do
462
534
  [[ -n "${number}" ]] || continue
463
535
  bash "${UPDATE_LABELS_SCRIPT}" --repo-slug "${REPO_SLUG}" --number "${number}" --remove agent-running >/dev/null 2>&1 || true
@@ -466,6 +538,7 @@ clear_running_labels_after_stop() {
466
538
 
467
539
  stop_runtime() {
468
540
  local -a tmux_sessions=()
541
+ local -a stale_tmux_sessions=()
469
542
  local -a pid_targets=()
470
543
  local session=""
471
544
  local pid=""
@@ -476,6 +549,11 @@ stop_runtime() {
476
549
  tmux_sessions+=("${session}")
477
550
  done < <(collect_active_tmux_sessions | sort -u)
478
551
 
552
+ while IFS= read -r session; do
553
+ [[ -n "${session}" ]] || continue
554
+ stale_tmux_sessions+=("${session}")
555
+ done < <(collect_stale_tmux_sessions | sort -u)
556
+
479
557
  while IFS= read -r pid; do
480
558
  [[ -n "${pid}" ]] || continue
481
559
  pid_targets+=("${pid}")
@@ -501,6 +579,9 @@ stop_runtime() {
501
579
  for session in "${tmux_sessions[@]+"${tmux_sessions[@]}"}"; do
502
580
  "${TMUX_BIN}" kill-session -t "${session}" >/dev/null 2>&1 || true
503
581
  done
582
+ for session in "${stale_tmux_sessions[@]+"${stale_tmux_sessions[@]}"}"; do
583
+ "${TMUX_BIN}" kill-session -t "${session}" >/dev/null 2>&1 || true
584
+ done
504
585
  fi
505
586
 
506
587
  terminate_pid_list TERM "${pid_targets[@]+"${pid_targets[@]}"}"
@@ -513,8 +594,10 @@ stop_runtime() {
513
594
  printf 'LAUNCHD_STOPPED=%s\n' "${launchd_stopped}"
514
595
  printf 'STOPPED_PID_COUNT=%s\n' "$(printf '%s\n' "${pid_targets[@]+"${pid_targets[@]}"}" | awk 'NF {c+=1} END {print c+0}')"
515
596
  printf 'STOPPED_TMUX_SESSION_COUNT=%s\n' "$(printf '%s\n' "${tmux_sessions[@]+"${tmux_sessions[@]}"}" | awk 'NF {c+=1} END {print c+0}')"
597
+ printf 'STOPPED_STALE_TMUX_SESSION_COUNT=%s\n' "$(printf '%s\n' "${stale_tmux_sessions[@]+"${stale_tmux_sessions[@]}"}" | awk 'NF {c+=1} END {print c+0}')"
516
598
  printf 'STOPPED_PIDS=%s\n' "$(printf '%s\n' "${pid_targets[@]+"${pid_targets[@]}"}" | join_by_comma)"
517
599
  printf 'STOPPED_TMUX_SESSIONS=%s\n' "$(printf '%s\n' "${tmux_sessions[@]+"${tmux_sessions[@]}"}" | join_by_comma)"
600
+ printf 'STOPPED_STALE_TMUX_SESSIONS=%s\n' "$(printf '%s\n' "${stale_tmux_sessions[@]+"${stale_tmux_sessions[@]}"}" | join_by_comma)"
518
601
  }
519
602
 
520
603
  start_runtime() {
@@ -65,13 +65,13 @@ resolve_backend() {
65
65
  return 0
66
66
  fi
67
67
 
68
- if [[ -n "${ACP_ACTIVE_PROVIDER_BACKEND:-${F_LOSNING_ACTIVE_PROVIDER_BACKEND:-}}" ]]; then
69
- printf '%s\n' "${ACP_ACTIVE_PROVIDER_BACKEND:-${F_LOSNING_ACTIVE_PROVIDER_BACKEND:-}}"
68
+ if [[ -n "${ACP_ACTIVE_PROVIDER_BACKEND:-}" ]]; then
69
+ printf '%s\n' "${ACP_ACTIVE_PROVIDER_BACKEND:-}"
70
70
  return 0
71
71
  fi
72
72
 
73
- if [[ -n "${ACP_CODING_WORKER:-${F_LOSNING_CODING_WORKER:-}}" ]]; then
74
- printf '%s\n' "${ACP_CODING_WORKER:-${F_LOSNING_CODING_WORKER:-}}"
73
+ if [[ -n "${ACP_CODING_WORKER:-}" ]]; then
74
+ printf '%s\n' "${ACP_CODING_WORKER:-}"
75
75
  return 0
76
76
  fi
77
77
 
@@ -79,7 +79,7 @@ resolve_backend() {
79
79
  }
80
80
 
81
81
  resolve_codex_label() {
82
- local configured_label="${ACP_ACTIVE_PROVIDER_LABEL:-${F_LOSNING_ACTIVE_PROVIDER_LABEL:-}}"
82
+ local configured_label="${ACP_ACTIVE_PROVIDER_LABEL:-}"
83
83
  local codex_quota_bin=""
84
84
  local active_label=""
85
85
 
@@ -88,8 +88,8 @@ resolve_codex_label() {
88
88
  return 0
89
89
  fi
90
90
 
91
- if [[ -n "${ACP_CODEX_QUOTA_LABEL:-${F_LOSNING_CODEX_QUOTA_LABEL:-}}" ]]; then
92
- printf '%s\n' "${ACP_CODEX_QUOTA_LABEL:-${F_LOSNING_CODEX_QUOTA_LABEL:-}}"
91
+ if [[ -n "${ACP_CODEX_QUOTA_LABEL:-}" ]]; then
92
+ printf '%s\n' "${ACP_CODEX_QUOTA_LABEL:-}"
93
93
  return 0
94
94
  fi
95
95
 
@@ -108,7 +108,7 @@ resolve_codex_label() {
108
108
  resolve_model() {
109
109
  local resolved_backend="${1:?backend required}"
110
110
  local raw_model="${2:-}"
111
- local active_provider_model="${ACP_ACTIVE_PROVIDER_MODEL:-${F_LOSNING_ACTIVE_PROVIDER_MODEL:-}}"
111
+ local active_provider_model="${ACP_ACTIVE_PROVIDER_MODEL:-}"
112
112
 
113
113
  if [[ -n "${raw_model}" ]]; then
114
114
  printf '%s\n' "${raw_model}"
@@ -121,8 +121,8 @@ resolve_model() {
121
121
  printf '%s\n' "${OPENCLAW_MODEL}"
122
122
  elif [[ -n "${active_provider_model}" ]]; then
123
123
  printf '%s\n' "${active_provider_model}"
124
- elif [[ -n "${ACP_OPENCLAW_MODEL:-${F_LOSNING_OPENCLAW_MODEL:-}}" ]]; then
125
- printf '%s\n' "${ACP_OPENCLAW_MODEL:-${F_LOSNING_OPENCLAW_MODEL:-}}"
124
+ elif [[ -n "${ACP_OPENCLAW_MODEL:-}" ]]; then
125
+ printf '%s\n' "${ACP_OPENCLAW_MODEL:-}"
126
126
  else
127
127
  flow_config_get "${CONFIG_YAML}" "execution.openclaw.model"
128
128
  fi
@@ -132,8 +132,8 @@ resolve_model() {
132
132
  printf '%s\n' "${CLAUDE_MODEL}"
133
133
  elif [[ -n "${active_provider_model}" ]]; then
134
134
  printf '%s\n' "${active_provider_model}"
135
- elif [[ -n "${ACP_CLAUDE_MODEL:-${F_LOSNING_CLAUDE_MODEL:-}}" ]]; then
136
- printf '%s\n' "${ACP_CLAUDE_MODEL:-${F_LOSNING_CLAUDE_MODEL:-}}"
135
+ elif [[ -n "${ACP_CLAUDE_MODEL:-}" ]]; then
136
+ printf '%s\n' "${ACP_CLAUDE_MODEL:-}"
137
137
  else
138
138
  flow_config_get "${CONFIG_YAML}" "execution.claude.model"
139
139
  fi
@@ -143,8 +143,8 @@ resolve_model() {
143
143
  printf '%s\n' "${CODEX_PROFILE_SAFE}"
144
144
  elif [[ -n "${active_provider_model}" ]]; then
145
145
  printf '%s\n' "${active_provider_model}"
146
- elif [[ -n "${ACP_CODEX_PROFILE_SAFE:-${F_LOSNING_CODEX_PROFILE_SAFE:-}}" ]]; then
147
- printf '%s\n' "${ACP_CODEX_PROFILE_SAFE:-${F_LOSNING_CODEX_PROFILE_SAFE:-}}"
146
+ elif [[ -n "${ACP_CODEX_PROFILE_SAFE:-}" ]]; then
147
+ printf '%s\n' "${ACP_CODEX_PROFILE_SAFE:-}"
148
148
  else
149
149
  flow_config_get "${CONFIG_YAML}" "execution.safe_profile"
150
150
  fi
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # reconcile-bootstrap-lib.sh — shared bootstrap helpers for reconcile scripts.
3
+ # Sourced by both agent-project-reconcile-pr-session and
4
+ # agent-project-reconcile-issue-session to avoid duplicating the bootstrap
5
+ # preamble.
6
+
7
+ bootstrap_flow_shell_lib() {
8
+ local candidate=""
9
+ local skill_name=""
10
+
11
+ for candidate in \
12
+ "${SCRIPT_DIR}/flow-shell-lib.sh" \
13
+ "${AGENT_CONTROL_PLANE_ROOT:-}/tools/bin/flow-shell-lib.sh" \
14
+ "${ACP_ROOT:-}/tools/bin/flow-shell-lib.sh" \
15
+ "${F_LOSNING_FLOW_ROOT:-}/tools/bin/flow-shell-lib.sh" \
16
+ "${AGENT_FLOW_SKILL_ROOT:-}/tools/bin/flow-shell-lib.sh" \
17
+ "${SHARED_AGENT_HOME:-}/tools/bin/flow-shell-lib.sh" \
18
+ "$(pwd)/tools/bin/flow-shell-lib.sh"; do
19
+ if [[ -n "${candidate}" && -f "${candidate}" ]]; then
20
+ printf '%s\n' "${candidate}"
21
+ return 0
22
+ fi
23
+ done
24
+
25
+ if [[ -n "${SHARED_AGENT_HOME:-}" ]]; then
26
+ for skill_name in "${AGENT_CONTROL_PLANE_SKILL_NAME:-agent-control-plane}" "${AGENT_CONTROL_PLANE_COMPAT_ALIAS:-}"; do
27
+ [[ -n "${skill_name}" ]] || continue
28
+ candidate="${SHARED_AGENT_HOME}/skills/openclaw/${skill_name}/tools/bin/flow-shell-lib.sh"
29
+ if [[ -f "${candidate}" ]]; then
30
+ printf '%s\n' "${candidate}"
31
+ return 0
32
+ fi
33
+ done
34
+ fi
35
+
36
+ echo "unable to locate flow-shell-lib.sh for reconcile bootstrap" >&2
37
+ return 1
38
+ }
39
+
40
+ FLOW_SHELL_LIB_PATH="$(bootstrap_flow_shell_lib)"
41
+ BOOTSTRAP_TOOLS_DIR="$(cd "$(dirname "${FLOW_SHELL_LIB_PATH}")" && pwd)"
42
+ # shellcheck source=/dev/null
43
+ source "${FLOW_SHELL_LIB_PATH}"
44
+
45
+ resolve_reconcile_tools_dir() {
46
+ local candidate_root=""
47
+ local skill_name=""
48
+
49
+ for candidate_root in \
50
+ "${AGENT_CONTROL_PLANE_ROOT:-}" \
51
+ "${ACP_ROOT:-}" \
52
+ "${F_LOSNING_FLOW_ROOT:-}" \
53
+ "${AGENT_FLOW_SKILL_ROOT:-}"; do
54
+ if [[ -n "${candidate_root}" && -d "${candidate_root}/tools/bin" ]]; then
55
+ printf '%s/tools/bin\n' "${candidate_root}"
56
+ return 0
57
+ fi
58
+ done
59
+
60
+ if [[ -n "${SHARED_AGENT_HOME:-}" ]]; then
61
+ if [[ -d "${SHARED_AGENT_HOME}/tools/bin" ]]; then
62
+ printf '%s/tools/bin\n' "${SHARED_AGENT_HOME}"
63
+ return 0
64
+ fi
65
+ for skill_name in "${AGENT_CONTROL_PLANE_SKILL_NAME:-agent-control-plane}" "${AGENT_CONTROL_PLANE_COMPAT_ALIAS:-}"; do
66
+ [[ -n "${skill_name}" ]] || continue
67
+ candidate_root="${SHARED_AGENT_HOME}/skills/openclaw/${skill_name}"
68
+ if [[ -d "${candidate_root}/tools/bin" ]]; then
69
+ printf '%s/tools/bin\n' "${candidate_root}"
70
+ return 0
71
+ fi
72
+ done
73
+ fi
74
+
75
+ if [[ -d "${SCRIPT_DIR}" ]]; then
76
+ printf '%s\n' "${SCRIPT_DIR}"
77
+ return 0
78
+ fi
79
+
80
+ printf '%s\n' "${BOOTSTRAP_TOOLS_DIR}"
81
+ }
82
+
83
+ shared_tools_dir="$(resolve_reconcile_tools_dir)"
84
+ resolve_reconcile_helper_path() {
85
+ local helper_name="${1:?helper name required}"
86
+ local candidate=""
87
+
88
+ for candidate in \
89
+ "${SCRIPT_DIR}/${helper_name}" \
90
+ "${BOOTSTRAP_TOOLS_DIR}/${helper_name}" \
91
+ "${shared_tools_dir}/${helper_name}"; do
92
+ if [[ -n "${candidate}" && -f "${candidate}" ]]; then
93
+ printf '%s\n' "${candidate}"
94
+ return 0
95
+ fi
96
+ done
97
+
98
+ echo "unable to locate ${helper_name} for reconcile bootstrap" >&2
99
+ return 1
100
+ }
101
+
102
+ FLOW_CONFIG_LIB_PATH="$(resolve_reconcile_helper_path "flow-config-lib.sh")"
103
+ # shellcheck source=/dev/null
104
+ source "${FLOW_CONFIG_LIB_PATH}"
105
+
106
+ require_transition() {
107
+ local step="${1:?step required}"
108
+ shift
109
+ if ! "$@"; then
110
+ echo "reconcile transition failed: ${step}" >&2
111
+ exit 1
112
+ fi
113
+ }
@@ -17,9 +17,6 @@ CONFIG_YAML="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
17
17
  for _clean in ACP_CODING_WORKER ACP_OPENCLAW_MODEL ACP_CLAUDE_MODEL \
18
18
  ACP_CLAUDE_TIMEOUT_SECONDS ACP_CLAUDE_MAX_ATTEMPTS ACP_CLAUDE_RETRY_BACKOFF_SECONDS \
19
19
  ACP_OPENCLAW_THINKING ACP_OPENCLAW_TIMEOUT_SECONDS \
20
- F_LOSNING_CODING_WORKER F_LOSNING_OPENCLAW_MODEL F_LOSNING_CLAUDE_MODEL \
21
- F_LOSNING_CLAUDE_TIMEOUT_SECONDS F_LOSNING_CLAUDE_MAX_ATTEMPTS F_LOSNING_CLAUDE_RETRY_BACKOFF_SECONDS \
22
- F_LOSNING_OPENCLAW_THINKING F_LOSNING_OPENCLAW_TIMEOUT_SECONDS \
23
20
  CODING_WORKER; do
24
21
  unset "${_clean}" 2>/dev/null || true
25
22
  done
@@ -69,33 +66,33 @@ if [[ -f "${PROFILE_NOTES}" ]]; then
69
66
  else
70
67
  printf 'PROFILE_NOTES_EXISTS=no\n'
71
68
  fi
72
- printf 'EFFECTIVE_REPO_ROOT=%s\n' "$(config_or_env 'ACP_REPO_ROOT F_LOSNING_REPO_ROOT' repo.root)"
73
- printf 'EFFECTIVE_AGENT_REPO_ROOT=%s\n' "$(config_or_env 'ACP_AGENT_REPO_ROOT F_LOSNING_AGENT_REPO_ROOT' runtime.agent_repo_root)"
74
- printf 'EFFECTIVE_WORKTREE_ROOT=%s\n' "$(config_or_env 'ACP_WORKTREE_ROOT F_LOSNING_WORKTREE_ROOT' runtime.worktree_root)"
75
- printf 'EFFECTIVE_RUNS_ROOT=%s\n' "$(config_or_env 'ACP_RUNS_ROOT F_LOSNING_RUNS_ROOT' runtime.runs_root)"
76
- printf 'EFFECTIVE_STATE_ROOT=%s\n' "$(config_or_env 'ACP_STATE_ROOT F_LOSNING_STATE_ROOT' runtime.state_root)"
77
- printf 'EFFECTIVE_RETAINED_REPO_ROOT=%s\n' "$(config_or_env 'ACP_RETAINED_REPO_ROOT F_LOSNING_RETAINED_REPO_ROOT' runtime.retained_repo_root)"
78
- printf 'EFFECTIVE_VSCODE_WORKSPACE_FILE=%s\n' "$(config_or_env 'ACP_VSCODE_WORKSPACE_FILE F_LOSNING_VSCODE_WORKSPACE_FILE' runtime.vscode_workspace_file)"
79
- printf 'EFFECTIVE_CODING_WORKER=%s\n' "$(config_or_env 'ACP_CODING_WORKER F_LOSNING_CODING_WORKER' execution.coding_worker)"
80
- printf 'EFFECTIVE_PROVIDER_QUOTA_COOLDOWNS=%s\n' "$(config_or_env 'ACP_PROVIDER_QUOTA_COOLDOWNS F_LOSNING_PROVIDER_QUOTA_COOLDOWNS' execution.provider_quota.cooldowns)"
81
- printf 'EFFECTIVE_PROVIDER_POOL_ORDER=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_ORDER F_LOSNING_PROVIDER_POOL_ORDER' execution.provider_pool_order)"
82
- printf 'EFFECTIVE_PROVIDER_POOL_NAME=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_POOL_NAME F_LOSNING_ACTIVE_PROVIDER_POOL_NAME')"
83
- printf 'EFFECTIVE_PROVIDER_POOL_BACKEND=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_BACKEND F_LOSNING_ACTIVE_PROVIDER_BACKEND')"
84
- printf 'EFFECTIVE_PROVIDER_POOL_MODEL=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_MODEL F_LOSNING_ACTIVE_PROVIDER_MODEL')"
85
- printf 'EFFECTIVE_PROVIDER_POOL_KEY=%s\n' "$(config_or_env 'ACP_ACTIVE_PROVIDER_KEY F_LOSNING_ACTIVE_PROVIDER_KEY')"
86
- printf 'EFFECTIVE_PROVIDER_POOLS_EXHAUSTED=%s\n' "$(config_or_env 'ACP_PROVIDER_POOLS_EXHAUSTED F_LOSNING_PROVIDER_POOLS_EXHAUSTED')"
87
- printf 'EFFECTIVE_PROVIDER_POOL_SELECTION_REASON=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_SELECTION_REASON F_LOSNING_PROVIDER_POOL_SELECTION_REASON')"
88
- printf 'EFFECTIVE_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_EPOCH')"
89
- printf 'EFFECTIVE_PROVIDER_POOL_NEXT_ATTEMPT_AT=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_NEXT_ATTEMPT_AT F_LOSNING_PROVIDER_POOL_NEXT_ATTEMPT_AT')"
90
- printf 'EFFECTIVE_PROVIDER_POOL_LAST_REASON=%s\n' "$(config_or_env 'ACP_PROVIDER_POOL_LAST_REASON F_LOSNING_PROVIDER_POOL_LAST_REASON')"
91
- printf 'EFFECTIVE_CODEX_PROFILE_SAFE=%s\n' "$(config_or_env 'ACP_CODEX_PROFILE_SAFE F_LOSNING_CODEX_PROFILE_SAFE' execution.safe_profile)"
92
- printf 'EFFECTIVE_CODEX_PROFILE_BYPASS=%s\n' "$(config_or_env 'ACP_CODEX_PROFILE_BYPASS F_LOSNING_CODEX_PROFILE_BYPASS' execution.bypass_profile)"
93
- printf 'EFFECTIVE_CLAUDE_MODEL=%s\n' "$(config_or_env 'ACP_CLAUDE_MODEL F_LOSNING_CLAUDE_MODEL' execution.claude.model)"
94
- printf 'EFFECTIVE_CLAUDE_PERMISSION_MODE=%s\n' "$(config_or_env 'ACP_CLAUDE_PERMISSION_MODE F_LOSNING_CLAUDE_PERMISSION_MODE' execution.claude.permission_mode)"
95
- printf 'EFFECTIVE_CLAUDE_EFFORT=%s\n' "$(config_or_env 'ACP_CLAUDE_EFFORT F_LOSNING_CLAUDE_EFFORT' execution.claude.effort)"
96
- printf 'EFFECTIVE_CLAUDE_TIMEOUT_SECONDS=%s\n' "$(config_or_env 'ACP_CLAUDE_TIMEOUT_SECONDS F_LOSNING_CLAUDE_TIMEOUT_SECONDS' execution.claude.timeout_seconds)"
97
- printf 'EFFECTIVE_CLAUDE_MAX_ATTEMPTS=%s\n' "$(config_or_env 'ACP_CLAUDE_MAX_ATTEMPTS F_LOSNING_CLAUDE_MAX_ATTEMPTS' execution.claude.max_attempts)"
98
- printf 'EFFECTIVE_CLAUDE_RETRY_BACKOFF_SECONDS=%s\n' "$(config_or_env 'ACP_CLAUDE_RETRY_BACKOFF_SECONDS F_LOSNING_CLAUDE_RETRY_BACKOFF_SECONDS' execution.claude.retry_backoff_seconds)"
99
- printf 'EFFECTIVE_OPENCLAW_MODEL=%s\n' "$(config_or_env 'ACP_OPENCLAW_MODEL F_LOSNING_OPENCLAW_MODEL' execution.openclaw.model)"
100
- printf 'EFFECTIVE_OPENCLAW_THINKING=%s\n' "$(config_or_env 'ACP_OPENCLAW_THINKING F_LOSNING_OPENCLAW_THINKING' execution.openclaw.thinking)"
101
- printf 'EFFECTIVE_OPENCLAW_TIMEOUT_SECONDS=%s\n' "$(config_or_env 'ACP_OPENCLAW_TIMEOUT_SECONDS F_LOSNING_OPENCLAW_TIMEOUT_SECONDS' execution.openclaw.timeout_seconds)"
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)"