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
@@ -0,0 +1,981 @@
1
+ flow_github_pr_merge() {
2
+ local repo_slug="${1:?repo slug required}"
3
+ local pr_number="${2:?pr number required}"
4
+ local merge_method="${3:-squash}"
5
+ local delete_branch="${4:-no}"
6
+ local pr_json=""
7
+ local head_ref=""
8
+ local encoded_ref=""
9
+
10
+ if flow_using_gitea; then
11
+ printf '%s' "$(
12
+ MERGE_METHOD="${merge_method}" DELETE_BRANCH="${delete_branch}" python3 - <<'PY'
13
+ import json
14
+ import os
15
+
16
+ method = os.environ.get("MERGE_METHOD", "squash")
17
+ delete_branch = os.environ.get("DELETE_BRANCH", "no") == "yes"
18
+ method_map = {"merge": "merge", "squash": "squash", "rebase": "rebase"}
19
+ print(json.dumps({
20
+ "Do": method_map.get(method, "squash"),
21
+ "delete_branch_after_merge": delete_branch,
22
+ }))
23
+ PY
24
+ )" | flow_github_api_repo "${repo_slug}" "pulls/${pr_number}/merge" --method POST --input - >/dev/null
25
+ return $?
26
+ fi
27
+
28
+ if gh pr merge "${pr_number}" -R "${repo_slug}" "--${merge_method}" $([[ "${delete_branch}" == "yes" ]] && printf '%s' '--delete-branch') --admin >/dev/null 2>&1; then
29
+ return 0
30
+ fi
31
+
32
+ printf '{"merge_method":"%s"}' "${merge_method}" \
33
+ | flow_github_api_repo "${repo_slug}" "pulls/${pr_number}/merge" --method PUT --input - >/dev/null
34
+
35
+ if [[ "${delete_branch}" == "yes" ]]; then
36
+ pr_json="$(flow_github_pr_view_json "${repo_slug}" "${pr_number}" 2>/dev/null || printf '{}\n')"
37
+ head_ref="$(jq -r '.headRefName // ""' <<<"${pr_json}")"
38
+ if [[ -n "${head_ref}" ]]; then
39
+ encoded_ref="$(flow_github_urlencode "heads/${head_ref}")"
40
+ flow_github_api_repo "${repo_slug}" "git/refs/${encoded_ref}" --method DELETE >/dev/null 2>&1 || true
41
+ fi
42
+ fi
43
+ }
44
+
45
+ flow_config_get() {
46
+ local config_file="${1:?config file required}"
47
+ local target_path="${2:?target path required}"
48
+
49
+ python3 - "$config_file" "$target_path" <<'PY'
50
+ import sys
51
+
52
+ config_file = sys.argv[1]
53
+ target_path = sys.argv[2]
54
+
55
+ stack = []
56
+ found = False
57
+
58
+ with open(config_file, "r", encoding="utf-8") as fh:
59
+ for raw_line in fh:
60
+ stripped = raw_line.strip()
61
+ if not stripped or stripped.startswith("#") or stripped.startswith("- "):
62
+ continue
63
+ if ":" not in raw_line:
64
+ continue
65
+
66
+ indent = len(raw_line) - len(raw_line.lstrip())
67
+ key, value = stripped.split(":", 1)
68
+ key = key.strip()
69
+ value = value.strip().strip("\"'")
70
+
71
+ while stack and indent <= stack[-1][0]:
72
+ stack.pop()
73
+
74
+ stack.append((indent, key))
75
+ current_path = ".".join(part for _, part in stack)
76
+
77
+ if current_path == target_path and value:
78
+ print(value)
79
+ found = True
80
+ break
81
+
82
+ if not found:
83
+ print("")
84
+ PY
85
+ }
86
+
87
+ flow_kv_get() {
88
+ local payload="${1:-}"
89
+ local key="${2:?key required}"
90
+
91
+ awk -F= -v key="${key}" '$1 == key { print substr($0, length(key) + 2); exit }' <<<"${payload}"
92
+ }
93
+
94
+ flow_env_or_config() {
95
+ local config_file="${1:?config file required}"
96
+ local env_names="${2:?env names required}"
97
+ local config_key="${3:?config key required}"
98
+ local default_value="${4:-}"
99
+ local env_name=""
100
+ local value=""
101
+
102
+ for env_name in ${env_names}; do
103
+ value="${!env_name:-}"
104
+ if [[ -n "${value}" ]]; then
105
+ printf '%s\n' "${value}"
106
+ return 0
107
+ fi
108
+ done
109
+
110
+ if [[ -f "${config_file}" ]]; then
111
+ value="$(flow_config_get "${config_file}" "${config_key}")"
112
+ if [[ -n "${value}" ]]; then
113
+ printf '%s\n' "${value}"
114
+ return 0
115
+ fi
116
+ fi
117
+
118
+ printf '%s\n' "${default_value}"
119
+ }
120
+
121
+ flow_resolve_adapter_id() {
122
+ local config_file="${1:-}"
123
+ local default_value=""
124
+ if [[ -z "${config_file}" ]]; then
125
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
126
+ fi
127
+ default_value="$(flow_default_profile_id)"
128
+ flow_env_or_config "${config_file}" "ACP_PROJECT_ID AGENT_PROJECT_ID" "id" "${default_value}"
129
+ }
130
+
131
+ flow_resolve_profile_notes_file() {
132
+ local config_file="${1:-}"
133
+ local config_dir=""
134
+
135
+ if [[ -z "${config_file}" ]]; then
136
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
137
+ fi
138
+
139
+ config_dir="$(cd "$(dirname "${config_file}")" 2>/dev/null && pwd -P || dirname "${config_file}")"
140
+ printf '%s/README.md
141
+ ' "${config_dir}"
142
+ }
143
+
144
+ flow_default_issue_session_prefix() {
145
+ local config_file="${1:-}"
146
+ local adapter_id=""
147
+
148
+ if [[ -z "${config_file}" ]]; then
149
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
150
+ fi
151
+
152
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
153
+ printf '%s-issue-\n' "${adapter_id}"
154
+ }
155
+
156
+ flow_default_pr_session_prefix() {
157
+ local config_file="${1:-}"
158
+ local adapter_id=""
159
+
160
+ if [[ -z "${config_file}" ]]; then
161
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
162
+ fi
163
+
164
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
165
+ printf '%s-pr-\n' "${adapter_id}"
166
+ }
167
+
168
+ flow_default_issue_branch_prefix() {
169
+ local config_file="${1:-}"
170
+ local adapter_id=""
171
+
172
+ if [[ -z "${config_file}" ]]; then
173
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
174
+ fi
175
+
176
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
177
+ printf 'agent/%s/issue\n' "${adapter_id}"
178
+ }
179
+
180
+ flow_default_pr_worktree_branch_prefix() {
181
+ local config_file="${1:-}"
182
+ local adapter_id=""
183
+
184
+ if [[ -z "${config_file}" ]]; then
185
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
186
+ fi
187
+
188
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
189
+ printf 'agent/%s/pr\n' "${adapter_id}"
190
+ }
191
+
192
+ flow_default_managed_pr_branch_globs() {
193
+ local config_file="${1:-}"
194
+ local adapter_id=""
195
+
196
+ if [[ -z "${config_file}" ]]; then
197
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
198
+ fi
199
+
200
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
201
+ printf 'agent/%s/* codex/* openclaw/*\n' "${adapter_id}"
202
+ }
203
+
204
+ flow_default_agent_root() {
205
+ local config_file="${1:-}"
206
+ local adapter_id=""
207
+ local platform_home="${AGENT_PLATFORM_HOME:-${HOME}/.agent-runtime}"
208
+
209
+ if [[ -z "${config_file}" ]]; then
210
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
211
+ fi
212
+
213
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
214
+ printf '%s/projects/%s
215
+ ' "${platform_home}" "${adapter_id}"
216
+ }
217
+
218
+ flow_default_repo_slug() {
219
+ local config_file="${1:-}"
220
+ local adapter_id=""
221
+
222
+ if [[ -z "${config_file}" ]]; then
223
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
224
+ fi
225
+
226
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
227
+ printf 'example/%s
228
+ ' "${adapter_id}"
229
+ }
230
+
231
+ flow_default_repo_id() {
232
+ printf '\n'
233
+ }
234
+
235
+ flow_default_repo_root() {
236
+ local config_file="${1:-}"
237
+ local agent_root=""
238
+
239
+ if [[ -z "${config_file}" ]]; then
240
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
241
+ fi
242
+
243
+ agent_root="$(flow_default_agent_root "${config_file}")"
244
+ printf '%s/repo
245
+ ' "${agent_root}"
246
+ }
247
+
248
+ flow_default_worktree_root() {
249
+ local config_file="${1:-}"
250
+ local agent_root=""
251
+
252
+ if [[ -z "${config_file}" ]]; then
253
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
254
+ fi
255
+
256
+ agent_root="$(flow_default_agent_root "${config_file}")"
257
+ printf '%s/worktrees
258
+ ' "${agent_root}"
259
+ }
260
+
261
+ flow_default_retained_repo_root() {
262
+ local config_file="${1:-}"
263
+ local agent_root=""
264
+
265
+ if [[ -z "${config_file}" ]]; then
266
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
267
+ fi
268
+
269
+ agent_root="$(flow_default_agent_root "${config_file}")"
270
+ printf '%s/retained
271
+ ' "${agent_root}"
272
+ }
273
+
274
+ flow_default_vscode_workspace_file() {
275
+ local config_file="${1:-}"
276
+ local agent_root=""
277
+
278
+ if [[ -z "${config_file}" ]]; then
279
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
280
+ fi
281
+
282
+ agent_root="$(flow_default_agent_root "${config_file}")"
283
+ printf '%s/workspace.code-workspace
284
+ ' "${agent_root}"
285
+ }
286
+ flow_resolve_repo_slug() {
287
+ local config_file="${1:-}"
288
+ local default_value=""
289
+ if [[ -z "${config_file}" ]]; then
290
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
291
+ fi
292
+ default_value="$(flow_default_repo_slug "${config_file}")"
293
+ flow_env_or_config "${config_file}" "ACP_REPO_SLUG F_LOSNING_REPO_SLUG" "repo.slug" "${default_value}"
294
+ }
295
+
296
+ flow_resolve_repo_id() {
297
+ local config_file="${1:-}"
298
+ local default_value=""
299
+ if [[ -z "${config_file}" ]]; then
300
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
301
+ fi
302
+ default_value="$(flow_default_repo_id)"
303
+ flow_env_or_config "${config_file}" "ACP_REPO_ID F_LOSNING_REPO_ID ACP_GITHUB_REPOSITORY_ID F_LOSNING_GITHUB_REPOSITORY_ID" "repo.id" "${default_value}"
304
+ }
305
+
306
+ flow_resolve_default_branch() {
307
+ local config_file="${1:-}"
308
+ if [[ -z "${config_file}" ]]; then
309
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
310
+ fi
311
+ flow_env_or_config "${config_file}" "ACP_DEFAULT_BRANCH F_LOSNING_DEFAULT_BRANCH" "repo.default_branch" "main"
312
+ }
313
+
314
+ flow_resolve_project_label() {
315
+ local config_file="${1:-}"
316
+ local repo_slug=""
317
+ local adapter_id=""
318
+ local label=""
319
+
320
+ if [[ -z "${config_file}" ]]; then
321
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
322
+ fi
323
+
324
+ repo_slug="$(flow_resolve_repo_slug "${config_file}")"
325
+ adapter_id="$(flow_resolve_adapter_id "${config_file}")"
326
+ label="${repo_slug##*/}"
327
+ if [[ -n "${label}" ]]; then
328
+ printf '%s\n' "${label}"
329
+ else
330
+ printf '%s\n' "${adapter_id}"
331
+ fi
332
+ }
333
+
334
+ flow_resolve_repo_root() {
335
+ local config_file="${1:-}"
336
+ local default_value=""
337
+ if [[ -z "${config_file}" ]]; then
338
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
339
+ fi
340
+ default_value="$(flow_default_repo_root "${config_file}")"
341
+ flow_env_or_config "${config_file}" "ACP_REPO_ROOT F_LOSNING_REPO_ROOT" "repo.root" "${default_value}"
342
+ }
343
+
344
+ flow_resolve_agent_root() {
345
+ local config_file="${1:-}"
346
+ local default_value=""
347
+
348
+ if [[ -z "${config_file}" ]]; then
349
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
350
+ fi
351
+
352
+ default_value="$(flow_default_agent_root "${config_file}")"
353
+ flow_env_or_config "${config_file}" "ACP_AGENT_ROOT F_LOSNING_AGENT_ROOT" "runtime.orchestrator_agent_root" "${default_value}"
354
+ }
355
+
356
+ flow_resolve_agent_repo_root() {
357
+ local config_file="${1:-}"
358
+ local default_value=""
359
+
360
+ if [[ -z "${config_file}" ]]; then
361
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
362
+ fi
363
+
364
+ default_value="$(flow_resolve_repo_root "${config_file}")"
365
+ flow_env_or_config "${config_file}" "ACP_AGENT_REPO_ROOT F_LOSNING_AGENT_REPO_ROOT" "runtime.agent_repo_root" "${default_value}"
366
+ }
367
+
368
+ flow_resolve_worktree_root() {
369
+ local config_file="${1:-}"
370
+ local default_value=""
371
+ if [[ -z "${config_file}" ]]; then
372
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
373
+ fi
374
+ default_value="$(flow_default_worktree_root "${config_file}")"
375
+ flow_env_or_config "${config_file}" "ACP_WORKTREE_ROOT F_LOSNING_WORKTREE_ROOT" "runtime.worktree_root" "${default_value}"
376
+ }
377
+
378
+ flow_resolve_runs_root() {
379
+ local config_file="${1:-}"
380
+ local default_value=""
381
+ local explicit_root="${ACP_RUNS_ROOT:-${F_LOSNING_RUNS_ROOT:-}}"
382
+ local umbrella_root="${ACP_AGENT_ROOT:-${F_LOSNING_AGENT_ROOT:-}}"
383
+
384
+ if [[ -z "${config_file}" ]]; then
385
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
386
+ fi
387
+
388
+ if [[ -n "${explicit_root}" ]]; then
389
+ printf '%s\n' "${explicit_root}"
390
+ return 0
391
+ fi
392
+
393
+ default_value="$(flow_resolve_agent_root "${config_file}")/runs"
394
+ if [[ -n "${umbrella_root}" ]]; then
395
+ printf '%s\n' "${default_value}"
396
+ return 0
397
+ fi
398
+
399
+ flow_env_or_config "${config_file}" "ACP_RUNS_ROOT F_LOSNING_RUNS_ROOT" "runtime.runs_root" "${default_value}"
400
+ }
401
+
402
+ flow_resolve_state_root() {
403
+ local config_file="${1:-}"
404
+ local default_value=""
405
+ local explicit_root="${ACP_STATE_ROOT:-${F_LOSNING_STATE_ROOT:-}}"
406
+ local umbrella_root="${ACP_AGENT_ROOT:-${F_LOSNING_AGENT_ROOT:-}}"
407
+
408
+ if [[ -z "${config_file}" ]]; then
409
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
410
+ fi
411
+
412
+ if [[ -n "${explicit_root}" ]]; then
413
+ printf '%s\n' "${explicit_root}"
414
+ return 0
415
+ fi
416
+
417
+ default_value="$(flow_resolve_agent_root "${config_file}")/state"
418
+ if [[ -n "${umbrella_root}" ]]; then
419
+ printf '%s\n' "${default_value}"
420
+ return 0
421
+ fi
422
+
423
+ flow_env_or_config "${config_file}" "ACP_STATE_ROOT F_LOSNING_STATE_ROOT" "runtime.state_root" "${default_value}"
424
+ }
425
+
426
+ flow_resolve_history_root() {
427
+ local config_file="${1:-}"
428
+ local default_value=""
429
+ local explicit_root="${ACP_HISTORY_ROOT:-${F_LOSNING_HISTORY_ROOT:-}}"
430
+ local umbrella_root="${ACP_AGENT_ROOT:-${F_LOSNING_AGENT_ROOT:-}}"
431
+
432
+ if [[ -z "${config_file}" ]]; then
433
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
434
+ fi
435
+
436
+ if [[ -n "${explicit_root}" ]]; then
437
+ printf '%s\n' "${explicit_root}"
438
+ return 0
439
+ fi
440
+
441
+ default_value="$(flow_resolve_agent_root "${config_file}")/history"
442
+ if [[ -n "${umbrella_root}" ]]; then
443
+ printf '%s\n' "${default_value}"
444
+ return 0
445
+ fi
446
+
447
+ flow_env_or_config "${config_file}" "ACP_HISTORY_ROOT F_LOSNING_HISTORY_ROOT" "runtime.history_root" "${default_value}"
448
+ }
449
+
450
+ flow_resolve_retained_repo_root() {
451
+ local config_file="${1:-}"
452
+ local default_value=""
453
+ if [[ -z "${config_file}" ]]; then
454
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
455
+ fi
456
+ default_value="$(flow_default_retained_repo_root "${config_file}")"
457
+ flow_env_or_config "${config_file}" "ACP_RETAINED_REPO_ROOT F_LOSNING_RETAINED_REPO_ROOT" "runtime.retained_repo_root" "${default_value}"
458
+ }
459
+
460
+ flow_resolve_source_repo_root() {
461
+ local config_file="${1:-}"
462
+ local default_value=""
463
+ if [[ -z "${config_file}" ]]; then
464
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
465
+ fi
466
+ default_value="$(flow_resolve_retained_repo_root "${config_file}")"
467
+ flow_env_or_config "${config_file}" "ACP_SOURCE_REPO_ROOT F_LOSNING_SOURCE_REPO_ROOT" "runtime.source_repo_root" "${default_value}"
468
+ }
469
+
470
+ flow_resolve_vscode_workspace_file() {
471
+ local config_file="${1:-}"
472
+ local default_value=""
473
+ if [[ -z "${config_file}" ]]; then
474
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
475
+ fi
476
+ default_value="$(flow_default_vscode_workspace_file "${config_file}")"
477
+ flow_env_or_config "${config_file}" "ACP_VSCODE_WORKSPACE_FILE F_LOSNING_VSCODE_WORKSPACE_FILE" "runtime.vscode_workspace_file" "${default_value}"
478
+ }
479
+
480
+ flow_resolve_web_playwright_command() {
481
+ local config_file="${1:-}"
482
+ if [[ -z "${config_file}" ]]; then
483
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
484
+ fi
485
+ flow_env_or_config "${config_file}" "ACP_WEB_PLAYWRIGHT_COMMAND F_LOSNING_WEB_PLAYWRIGHT_COMMAND" "execution.verification.web_playwright_command" "pnpm exec playwright test"
486
+ }
487
+
488
+ flow_resolve_codex_quota_bin() {
489
+ local flow_root="${1:-}"
490
+ local shared_home=""
491
+ local explicit_bin="${ACP_CODEX_QUOTA_BIN:-${F_LOSNING_CODEX_QUOTA_BIN:-}}"
492
+ local candidate=""
493
+
494
+ if [[ -n "${explicit_bin}" ]]; then
495
+ printf '%s\n' "${explicit_bin}"
496
+ return 0
497
+ fi
498
+
499
+ if [[ -z "${flow_root}" ]]; then
500
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
501
+ fi
502
+ shared_home="${SHARED_AGENT_HOME:-$(resolve_shared_agent_home "${flow_root}")}"
503
+
504
+ for candidate in \
505
+ "${flow_root}/tools/bin/codex-quota" \
506
+ "${shared_home}/tools/bin/codex-quota"; do
507
+ if [[ -x "${candidate}" ]]; then
508
+ printf '%s\n' "${candidate}"
509
+ return 0
510
+ fi
511
+ done
512
+
513
+ candidate="$(command -v codex-quota 2>/dev/null || true)"
514
+ if [[ -n "${candidate}" ]]; then
515
+ printf '%s\n' "${candidate}"
516
+ return 0
517
+ fi
518
+
519
+ printf '%s\n' "${flow_root}/tools/bin/codex-quota"
520
+ }
521
+
522
+ flow_resolve_codex_quota_manager_script() {
523
+ local flow_root="${1:-}"
524
+ local shared_home=""
525
+ local explicit_script="${ACP_CODEX_QUOTA_MANAGER_SCRIPT:-${F_LOSNING_CODEX_QUOTA_MANAGER_SCRIPT:-}}"
526
+ local candidate=""
527
+
528
+ if [[ -n "${explicit_script}" ]]; then
529
+ printf '%s\n' "${explicit_script}"
530
+ return 0
531
+ fi
532
+
533
+ if [[ -z "${flow_root}" ]]; then
534
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
535
+ fi
536
+ shared_home="${SHARED_AGENT_HOME:-$(resolve_shared_agent_home "${flow_root}")}"
537
+
538
+ for candidate in \
539
+ "${flow_root}/tools/vendor/codex-quota-manager/scripts/auto-switch.sh" \
540
+ "${shared_home}/tools/vendor/codex-quota-manager/scripts/auto-switch.sh" \
541
+ "${shared_home}/skills/openclaw/codex-quota-manager/scripts/auto-switch.sh"; do
542
+ if [[ -x "${candidate}" ]]; then
543
+ printf '%s\n' "${candidate}"
544
+ return 0
545
+ fi
546
+ done
547
+
548
+ printf '%s\n' "${flow_root}/tools/vendor/codex-quota-manager/scripts/auto-switch.sh"
549
+ }
550
+
551
+ flow_resolve_template_file() {
552
+ local template_name="${1:?template name required}"
553
+ local workspace_dir="${2:-}"
554
+ local config_file="${3:-}"
555
+ local flow_root=""
556
+ local profile_id=""
557
+ local config_dir=""
558
+ local template_dir=""
559
+ local candidate=""
560
+ local workspace_real=""
561
+ local canonical_tools_real=""
562
+
563
+ if [[ -z "${config_file}" ]]; then
564
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
565
+ fi
566
+
567
+ flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[0]}")"
568
+ config_dir="$(cd "$(dirname "${config_file}")" 2>/dev/null && pwd -P || dirname "${config_file}")"
569
+
570
+ for template_dir in \
571
+ "${AGENT_CONTROL_PLANE_TEMPLATE_DIR:-}" \
572
+ "${ACP_TEMPLATE_DIR:-}" \
573
+ "${F_LOSNING_TEMPLATE_DIR:-}"; do
574
+ if [[ -n "${template_dir}" && -f "${template_dir}/${template_name}" ]]; then
575
+ printf '%s\n' "${template_dir}/${template_name}"
576
+ return 0
577
+ fi
578
+ done
579
+
580
+ if [[ -n "${workspace_dir}" && -f "${workspace_dir}/templates/${template_name}" ]]; then
581
+ workspace_real="$(cd "${workspace_dir}" && pwd -P)"
582
+ canonical_tools_real="$(cd "${flow_root}/tools" && pwd -P)"
583
+ if [[ "${workspace_real}" != "${canonical_tools_real}" ]]; then
584
+ printf '%s\n' "${workspace_dir}/templates/${template_name}"
585
+ return 0
586
+ fi
587
+ fi
588
+
589
+ candidate="${config_dir}/templates/${template_name}"
590
+ if [[ -f "${candidate}" ]]; then
591
+ printf '%s\n' "${candidate}"
592
+ return 0
593
+ fi
594
+
595
+ if [[ -n "${workspace_dir}" && -f "${workspace_dir}/templates/${template_name}" ]]; then
596
+ printf '%s\n' "${workspace_dir}/templates/${template_name}"
597
+ return 0
598
+ fi
599
+
600
+ printf '%s\n' "${flow_root}/tools/templates/${template_name}"
601
+ }
602
+
603
+ flow_resolve_retry_cooldowns() {
604
+ local config_file="${1:-}"
605
+ if [[ -z "${config_file}" ]]; then
606
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
607
+ fi
608
+ flow_env_or_config "${config_file}" "ACP_RETRY_COOLDOWNS F_LOSNING_RETRY_COOLDOWNS" "execution.retry.cooldowns" "300,900,1800,3600"
609
+ }
610
+
611
+ flow_resolve_provider_quota_cooldowns() {
612
+ local config_file="${1:-}"
613
+ if [[ -z "${config_file}" ]]; then
614
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
615
+ fi
616
+ flow_env_or_config "${config_file}" "ACP_PROVIDER_QUOTA_COOLDOWNS F_LOSNING_PROVIDER_QUOTA_COOLDOWNS" "execution.provider_quota.cooldowns" "300,900,1800,3600"
617
+ }
618
+
619
+ flow_resolve_provider_pool_order() {
620
+ local config_file="${1:-}"
621
+ if [[ -z "${config_file}" ]]; then
622
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
623
+ fi
624
+ flow_env_or_config "${config_file}" "ACP_PROVIDER_POOL_ORDER F_LOSNING_PROVIDER_POOL_ORDER" "execution.provider_pool_order" ""
625
+ }
626
+
627
+ flow_provider_pool_names() {
628
+ local config_file="${1:-}"
629
+ local order=""
630
+ local pool_name=""
631
+
632
+ if [[ -z "${config_file}" ]]; then
633
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
634
+ fi
635
+
636
+ order="$(flow_resolve_provider_pool_order "${config_file}")"
637
+ for pool_name in ${order}; do
638
+ [[ -n "${pool_name}" ]] || continue
639
+ printf '%s\n' "${pool_name}"
640
+ done
641
+ }
642
+
643
+ flow_provider_pools_enabled() {
644
+ local config_file="${1:-}"
645
+ [[ -n "$(flow_resolve_provider_pool_order "${config_file}")" ]]
646
+ }
647
+
648
+ flow_provider_pool_value() {
649
+ local config_file="${1:?config file required}"
650
+ local pool_name="${2:?pool name required}"
651
+ local relative_path="${3:?relative path required}"
652
+
653
+ flow_config_get "${config_file}" "execution.provider_pools.${pool_name}.${relative_path}"
654
+ }
655
+
656
+ flow_provider_pool_backend() {
657
+ local config_file="${1:?config file required}"
658
+ local pool_name="${2:?pool name required}"
659
+
660
+ flow_provider_pool_value "${config_file}" "${pool_name}" "coding_worker"
661
+ }
662
+
663
+ flow_provider_pool_safe_profile() {
664
+ local config_file="${1:?config file required}"
665
+ local pool_name="${2:?pool name required}"
666
+
667
+ flow_provider_pool_value "${config_file}" "${pool_name}" "safe_profile"
668
+ }
669
+
670
+ flow_provider_pool_bypass_profile() {
671
+ local config_file="${1:?config file required}"
672
+ local pool_name="${2:?pool name required}"
673
+
674
+ flow_provider_pool_value "${config_file}" "${pool_name}" "bypass_profile"
675
+ }
676
+
677
+ flow_provider_pool_claude_model() {
678
+ local config_file="${1:?config file required}"
679
+ local pool_name="${2:?pool name required}"
680
+
681
+ flow_provider_pool_value "${config_file}" "${pool_name}" "claude.model"
682
+ }
683
+
684
+ flow_provider_pool_claude_permission_mode() {
685
+ local config_file="${1:?config file required}"
686
+ local pool_name="${2:?pool name required}"
687
+
688
+ flow_provider_pool_value "${config_file}" "${pool_name}" "claude.permission_mode"
689
+ }
690
+
691
+ flow_provider_pool_claude_effort() {
692
+ local config_file="${1:?config file required}"
693
+ local pool_name="${2:?pool name required}"
694
+
695
+ flow_provider_pool_value "${config_file}" "${pool_name}" "claude.effort"
696
+ }
697
+
698
+ flow_provider_pool_claude_timeout_seconds() {
699
+ local config_file="${1:?config file required}"
700
+ local pool_name="${2:?pool name required}"
701
+
702
+ flow_provider_pool_value "${config_file}" "${pool_name}" "claude.timeout_seconds"
703
+ }
704
+
705
+ flow_provider_pool_claude_max_attempts() {
706
+ local config_file="${1:?config file required}"
707
+ local pool_name="${2:?pool name required}"
708
+
709
+ flow_provider_pool_value "${config_file}" "${pool_name}" "claude.max_attempts"
710
+ }
711
+
712
+ flow_provider_pool_claude_retry_backoff_seconds() {
713
+ local config_file="${1:?config file required}"
714
+ local pool_name="${2:?pool name required}"
715
+
716
+ flow_provider_pool_value "${config_file}" "${pool_name}" "claude.retry_backoff_seconds"
717
+ }
718
+
719
+ flow_provider_pool_openclaw_model() {
720
+ local config_file="${1:?config file required}"
721
+ local pool_name="${2:?pool name required}"
722
+
723
+ flow_provider_pool_value "${config_file}" "${pool_name}" "openclaw.model"
724
+ }
725
+
726
+ flow_provider_pool_openclaw_thinking() {
727
+ local config_file="${1:?config file required}"
728
+ local pool_name="${2:?pool name required}"
729
+
730
+ flow_provider_pool_value "${config_file}" "${pool_name}" "openclaw.thinking"
731
+ }
732
+
733
+ flow_provider_pool_openclaw_timeout_seconds() {
734
+ local config_file="${1:?config file required}"
735
+ local pool_name="${2:?pool name required}"
736
+
737
+ flow_provider_pool_value "${config_file}" "${pool_name}" "openclaw.timeout_seconds"
738
+ }
739
+
740
+ flow_provider_pool_ollama_model() {
741
+ local config_file="${1:?config file required}"
742
+ local pool_name="${2:?pool name required}"
743
+
744
+ flow_provider_pool_value "${config_file}" "${pool_name}" "ollama.model"
745
+ }
746
+
747
+ flow_provider_pool_ollama_base_url() {
748
+ local config_file="${1:?config file required}"
749
+ local pool_name="${2:?pool name required}"
750
+
751
+ flow_provider_pool_value "${config_file}" "${pool_name}" "ollama.base_url"
752
+ }
753
+
754
+ flow_provider_pool_ollama_timeout_seconds() {
755
+ local config_file="${1:?config file required}"
756
+ local pool_name="${2:?pool name required}"
757
+
758
+ flow_provider_pool_value "${config_file}" "${pool_name}" "ollama.timeout_seconds"
759
+ }
760
+
761
+ flow_provider_pool_pi_model() {
762
+ local config_file="${1:?config file required}"
763
+ local pool_name="${2:?pool name required}"
764
+
765
+ flow_provider_pool_value "${config_file}" "${pool_name}" "pi.model"
766
+ }
767
+
768
+ flow_provider_pool_pi_thinking() {
769
+ local config_file="${1:?config file required}"
770
+ local pool_name="${2:?pool name required}"
771
+
772
+ flow_provider_pool_value "${config_file}" "${pool_name}" "pi.thinking"
773
+ }
774
+
775
+ flow_provider_pool_pi_timeout_seconds() {
776
+ local config_file="${1:?config file required}"
777
+ local pool_name="${2:?pool name required}"
778
+
779
+ flow_provider_pool_value "${config_file}" "${pool_name}" "pi.timeout_seconds"
780
+ }
781
+
782
+ flow_provider_pool_opencode_model() {
783
+ local config_file="${1:?config file required}"
784
+ local pool_name="${2:?pool name required}"
785
+
786
+ flow_provider_pool_value "${config_file}" "${pool_name}" "opencode.model"
787
+ }
788
+
789
+ flow_provider_pool_opencode_timeout_seconds() {
790
+ local config_file="${1:?config file required}"
791
+ local pool_name="${2:?pool name required}"
792
+
793
+ flow_provider_pool_value "${config_file}" "${pool_name}" "opencode.timeout_seconds"
794
+ }
795
+
796
+ flow_provider_pool_kilo_model() {
797
+ local config_file="${1:?config file required}"
798
+ local pool_name="${2:?pool name required}"
799
+
800
+ flow_provider_pool_value "${config_file}" "${pool_name}" "kilo.model"
801
+ }
802
+
803
+ flow_provider_pool_kilo_timeout_seconds() {
804
+ local config_file="${1:?config file required}"
805
+ local pool_name="${2:?pool name required}"
806
+
807
+ flow_provider_pool_value "${config_file}" "${pool_name}" "kilo.timeout_seconds"
808
+ }
809
+
810
+ flow_sanitize_provider_key() {
811
+ local raw_key="${1:?raw key required}"
812
+
813
+ printf '%s' "${raw_key}" \
814
+ | tr '[:upper:]' '[:lower:]' \
815
+ | sed -E 's/[^a-z0-9._-]+/-/g; s/^-+//; s/-+$//; s/-\+/-/g'
816
+ }
817
+
818
+ flow_provider_pool_model_identity() {
819
+ local config_file="${1:?config file required}"
820
+ local pool_name="${2:?pool name required}"
821
+ local backend=""
822
+
823
+ backend="$(flow_provider_pool_backend "${config_file}" "${pool_name}")"
824
+ case "${backend}" in
825
+ codex)
826
+ flow_provider_pool_safe_profile "${config_file}" "${pool_name}"
827
+ ;;
828
+ claude)
829
+ flow_provider_pool_claude_model "${config_file}" "${pool_name}"
830
+ ;;
831
+ openclaw)
832
+ flow_provider_pool_openclaw_model "${config_file}" "${pool_name}"
833
+ ;;
834
+ ollama)
835
+ flow_provider_pool_ollama_model "${config_file}" "${pool_name}"
836
+ ;;
837
+ pi)
838
+ flow_provider_pool_pi_model "${config_file}" "${pool_name}"
839
+ ;;
840
+ opencode)
841
+ flow_provider_pool_opencode_model "${config_file}" "${pool_name}"
842
+ ;;
843
+ kilo)
844
+ flow_provider_pool_kilo_model "${config_file}" "${pool_name}"
845
+ ;;
846
+ *)
847
+ printf '\n'
848
+ ;;
849
+ esac
850
+ }
851
+
852
+ # Check health of a specific provider pool
853
+ # Returns: 0 = healthy, 1 = unhealthy
854
+ flow_provider_pool_health_check() {
855
+ local config_file="${1:?config file required}"
856
+ local pool_name="${2:?pool name required}"
857
+ local backend=""
858
+ local adapter_script=""
859
+ local SCRIPT_DIR=""
860
+
861
+ backend="$(flow_provider_pool_backend "${config_file}" "${pool_name}")"
862
+ if [[ -z "${backend}" ]]; then
863
+ echo "ERROR: No backend found for pool ${pool_name}"
864
+ return 1
865
+ fi
866
+
867
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
868
+ adapter_script="${SCRIPT_DIR}/${backend}-adapter.sh"
869
+
870
+ if [[ ! -f "${adapter_script}" ]]; then
871
+ echo "WARN: Adapter script not found: ${adapter_script}"
872
+ return 1
873
+ fi
874
+
875
+ # Source adapter and run health check
876
+ source "${SCRIPT_DIR}/adapter-interface.sh"
877
+ source "${SCRIPT_DIR}/adapter-capabilities.sh"
878
+ source "${adapter_script}"
879
+
880
+ adapter_health_check
881
+ return $?
882
+ }
883
+
884
+ # Get healthy pools in priority order
885
+ # Returns list of pool names that are healthy
886
+ flow_healthy_pools() {
887
+ local config_file="${1:-}"
888
+ local pool_name=""
889
+ local healthy_pools=""
890
+
891
+ if [[ -z "${config_file}" ]]; then
892
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
893
+ fi
894
+
895
+ for pool_name in $(flow_provider_pool_names "${config_file}"); do
896
+ if flow_provider_pool_health_check "${config_file}" "${pool_name}" >/dev/null 2>&1; then
897
+ healthy_pools="${healthy_pools} ${pool_name}"
898
+ fi
899
+ done
900
+
901
+ printf '%s\n' "${healthy_pools}"
902
+ }
903
+
904
+ # Enhanced provider pool selection with failover
905
+ # Returns: environment variables for the best available provider pool
906
+ flow_selected_provider_pool_env() {
907
+ local config_file="${1:-}"
908
+ local pool_name=""
909
+ local selected_pool=""
910
+ local backend=""
911
+ local health_output=""
912
+
913
+ if [[ -z "${config_file}" ]]; then
914
+ config_file="$(resolve_flow_config_yaml "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
915
+ fi
916
+
917
+ # Try pools in order, pick first healthy one
918
+ for pool_name in $(flow_provider_pool_names "${config_file}"); do
919
+ health_output="$(flow_provider_pool_health_check "${config_file}" "${pool_name}" 2>&1)"
920
+ if [[ $? -eq 0 ]]; then
921
+ selected_pool="${pool_name}"
922
+ echo "INFO: Selected healthy provider pool: ${selected_pool}"
923
+ echo "${health_output}"
924
+ break
925
+ else
926
+ echo "WARN: Provider pool '${pool_name}' is unhealthy, trying next: ${health_output}"
927
+ fi
928
+ done
929
+
930
+ if [[ -z "${selected_pool}" ]]; then
931
+ echo "ERROR: No healthy provider pools available!"
932
+ return 1
933
+ fi
934
+
935
+ # Export environment for selected pool
936
+ backend="$(flow_provider_pool_backend "${config_file}" "${selected_pool}")"
937
+ echo "INFO: Active provider backend: ${backend}"
938
+ echo "ACP_PROVIDER_POOL_NAME=${selected_pool}"
939
+ echo "ACP_PROVIDER_POOL_BACKEND=${backend}"
940
+
941
+ # Export backend-specific settings
942
+ case "${backend}" in
943
+ claude)
944
+ echo "ACP_CLAUDE_MODEL=$(flow_provider_pool_claude_model "${config_file}" "${selected_pool}")"
945
+ echo "ACP_CLAUDE_PERMISSION_MODE=$(flow_provider_pool_claude_permission_mode "${config_file}" "${selected_pool}")"
946
+ echo "ACP_CLAUDE_EFFORT=$(flow_provider_pool_claude_effort "${config_file}" "${selected_pool}")"
947
+ echo "ACP_CLAUDE_TIMEOUT_SECONDS=$(flow_provider_pool_claude_timeout_seconds "${config_file}" "${selected_pool}")"
948
+ echo "ACP_CLAUDE_MAX_ATTEMPTS=$(flow_provider_pool_claude_max_attempts "${config_file}" "${selected_pool}")"
949
+ echo "ACP_CLAUDE_RETRY_BACKOFF_SECONDS=$(flow_provider_pool_claude_retry_backoff_seconds "${config_file}" "${selected_pool}")"
950
+ ;;
951
+ openclaw)
952
+ echo "ACP_OPENCLAW_MODEL=$(flow_provider_pool_openclaw_model "${config_file}" "${selected_pool}")"
953
+ echo "ACP_OPENCLAW_THINKING=$(flow_provider_pool_openclaw_thinking "${config_file}" "${selected_pool}")"
954
+ echo "ACP_OPENCLAW_TIMEOUT_SECONDS=$(flow_provider_pool_openclaw_timeout_seconds "${config_file}" "${selected_pool}")"
955
+ ;;
956
+ ollama)
957
+ echo "ACP_OLLAMA_MODEL=$(flow_provider_pool_ollama_model "${config_file}" "${selected_pool}")"
958
+ echo "ACP_OLLAMA_BASE_URL=$(flow_provider_pool_ollama_base_url "${config_file}" "${selected_pool}")"
959
+ echo "ACP_OLLAMA_TIMEOUT_SECONDS=$(flow_provider_pool_ollama_timeout_seconds "${config_file}" "${selected_pool}")"
960
+ ;;
961
+ pi)
962
+ echo "ACP_PI_MODEL=$(flow_provider_pool_pi_model "${config_file}" "${selected_pool}")"
963
+ echo "ACP_PI_THINKING=$(flow_provider_pool_pi_thinking "${config_file}" "${selected_pool}")"
964
+ echo "ACP_PI_TIMEOUT_SECONDS=$(flow_provider_pool_pi_timeout_seconds "${config_file}" "${selected_pool}")"
965
+ ;;
966
+ opencode)
967
+ echo "ACP_OPENCODE_MODEL=$(flow_provider_pool_opencode_model "${config_file}" "${selected_pool}")"
968
+ echo "ACP_OPENCODE_TIMEOUT_SECONDS=$(flow_provider_pool_opencode_timeout_seconds "${config_file}" "${selected_pool}")"
969
+ ;;
970
+ kilo)
971
+ echo "ACP_KILO_MODEL=$(flow_provider_pool_kilo_model "${config_file}" "${selected_pool}")"
972
+ echo "ACP_KILO_TIMEOUT_SECONDS=$(flow_provider_pool_kilo_timeout_seconds "${config_file}" "${selected_pool}")"
973
+ ;;
974
+ codex)
975
+ echo "INFO: Codex uses quota manager, no additional env needed"
976
+ ;;
977
+ esac
978
+
979
+ return 0
980
+ }
981
+