agent-control-plane 0.1.16 → 0.2.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 +322 -9
- package/package.json +1 -1
- 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 +77 -21
- package/tools/bin/agent-project-catch-up-scheduled-issue-retries +123 -0
- package/tools/bin/agent-project-cleanup-session +84 -0
- package/tools/bin/agent-project-heartbeat-loop +10 -3
- package/tools/bin/agent-project-reconcile-issue-session +24 -12
- package/tools/bin/agent-project-run-claude-session +2 -2
- package/tools/bin/agent-project-run-kilo-session +346 -14
- package/tools/bin/agent-project-run-ollama-session +658 -0
- package/tools/bin/agent-project-run-openclaw-session +27 -25
- package/tools/bin/agent-project-run-opencode-session +354 -14
- package/tools/bin/agent-project-run-pi-session +479 -0
- package/tools/bin/agent-project-worker-status +1 -1
- package/tools/bin/flow-config-lib.sh +116 -3
- package/tools/bin/flow-resident-worker-lib.sh +1 -1
- package/tools/bin/flow-shell-lib.sh +5 -2
- package/tools/bin/heartbeat-recovery-preflight.sh +1 -0
- package/tools/bin/heartbeat-safe-auto.sh +105 -17
- 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-runtimectl.sh +90 -7
- package/tools/bin/provider-cooldown-state.sh +14 -14
- package/tools/bin/render-flow-config.sh +30 -33
- 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 +4 -4
- 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/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
|
@@ -35,7 +35,6 @@ flow_export_compat_env_aliases() {
|
|
|
35
35
|
flow_export_env_alias_if_unset F_LOSNING_MEMORY_DIR ACP_MEMORY_DIR
|
|
36
36
|
flow_export_env_alias_if_unset F_LOSNING_RETAINED_REPO_ROOT ACP_RETAINED_REPO_ROOT
|
|
37
37
|
flow_export_env_alias_if_unset F_LOSNING_VSCODE_WORKSPACE_FILE ACP_VSCODE_WORKSPACE_FILE
|
|
38
|
-
flow_export_env_alias_if_unset F_LOSNING_CODING_WORKER ACP_CODING_WORKER
|
|
39
38
|
flow_export_env_alias_if_unset F_LOSNING_CODEX_PROFILE_SAFE ACP_CODEX_PROFILE_SAFE
|
|
40
39
|
flow_export_env_alias_if_unset F_LOSNING_CODEX_PROFILE_BYPASS ACP_CODEX_PROFILE_BYPASS
|
|
41
40
|
flow_export_env_alias_if_unset F_LOSNING_CLAUDE_MODEL ACP_CLAUDE_MODEL
|
|
@@ -84,7 +83,6 @@ flow_export_canonical_env_aliases() {
|
|
|
84
83
|
flow_export_env_alias_if_unset ACP_MEMORY_DIR F_LOSNING_MEMORY_DIR
|
|
85
84
|
flow_export_env_alias_if_unset ACP_RETAINED_REPO_ROOT F_LOSNING_RETAINED_REPO_ROOT
|
|
86
85
|
flow_export_env_alias_if_unset ACP_VSCODE_WORKSPACE_FILE F_LOSNING_VSCODE_WORKSPACE_FILE
|
|
87
|
-
flow_export_env_alias_if_unset ACP_CODING_WORKER F_LOSNING_CODING_WORKER
|
|
88
86
|
flow_export_env_alias_if_unset ACP_CODEX_PROFILE_SAFE F_LOSNING_CODEX_PROFILE_SAFE
|
|
89
87
|
flow_export_env_alias_if_unset ACP_CODEX_PROFILE_BYPASS F_LOSNING_CODEX_PROFILE_BYPASS
|
|
90
88
|
flow_export_env_alias_if_unset ACP_CLAUDE_MODEL F_LOSNING_CLAUDE_MODEL
|
|
@@ -223,6 +221,11 @@ resolve_shared_agent_home() {
|
|
|
223
221
|
flow_root="$(resolve_flow_skill_dir "${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}")"
|
|
224
222
|
fi
|
|
225
223
|
|
|
224
|
+
if flow_is_skill_root "${flow_root}"; then
|
|
225
|
+
flow_print_dir "${flow_root}"
|
|
226
|
+
return 0
|
|
227
|
+
fi
|
|
228
|
+
|
|
226
229
|
flow_print_dir "${flow_root}/../../.."
|
|
227
230
|
}
|
|
228
231
|
|
|
@@ -80,6 +80,7 @@ run_preflight_test "heartbeat-no-tmux-sessions" "${TEST_DIR}/test-heartbeat-safe
|
|
|
80
80
|
run_preflight_test "heartbeat-static-capacity-without-quota-cache" "${TEST_DIR}/test-heartbeat-safe-auto-static-capacity-without-quota-cache.sh"
|
|
81
81
|
run_preflight_test "heartbeat-openclaw-skips-codex-quota" "${TEST_DIR}/test-heartbeat-safe-auto-openclaw-skips-codex-quota.sh"
|
|
82
82
|
run_preflight_test "heartbeat-empty-schedule-label-sync" "${TEST_DIR}/test-heartbeat-sync-issue-labels-empty-schedule.sh"
|
|
83
|
+
run_preflight_test "heartbeat-open-issue-terminal-sync" "${TEST_DIR}/test-heartbeat-sync-open-agent-issues-terminal-clears-running.sh"
|
|
83
84
|
run_preflight_test "heartbeat-open-pr-terminal-sync" "${TEST_DIR}/test-heartbeat-sync-open-agent-prs-terminal-clears-running.sh"
|
|
84
85
|
run_preflight_test "heartbeat-pr-launch-dedup" "${TEST_DIR}/test-heartbeat-loop-pr-launch-dedup.sh"
|
|
85
86
|
run_preflight_test "heartbeat-auth-wait-capacity" "${TEST_DIR}/test-heartbeat-loop-auth-wait-does-not-consume-capacity.sh"
|
|
@@ -17,6 +17,8 @@ flow_export_execution_env "${CONFIG_YAML}"
|
|
|
17
17
|
AGENT_ROOT="$(flow_resolve_agent_root "${CONFIG_YAML}")"
|
|
18
18
|
RUNS_ROOT="$(flow_resolve_runs_root "${CONFIG_YAML}")"
|
|
19
19
|
STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
|
|
20
|
+
HISTORY_ROOT="$(flow_resolve_history_root "${CONFIG_YAML}")"
|
|
21
|
+
WORKTREE_ROOT="$(flow_resolve_worktree_root "${CONFIG_YAML}")"
|
|
20
22
|
MEMORY_DIR="${ACP_MEMORY_DIR:-${F_LOSNING_MEMORY_DIR:-${AGENT_CONTROL_PLANE_WORKSPACE:-$HOME/.agent-runtime/control-plane/workspace}/memory}}"
|
|
21
23
|
REPO_SLUG="$(flow_resolve_repo_slug "${CONFIG_YAML}")"
|
|
22
24
|
MAX_CONCURRENT_WORKERS="${ACP_MAX_CONCURRENT_WORKERS:-${F_LOSNING_MAX_CONCURRENT_WORKERS:-20}}"
|
|
@@ -29,7 +31,7 @@ MAX_CONCURRENT_BLOCKED_RECOVERY_ISSUE_WORKERS="${ACP_MAX_CONCURRENT_BLOCKED_RECO
|
|
|
29
31
|
BLOCKED_RECOVERY_COOLDOWN_SECONDS="${ACP_BLOCKED_RECOVERY_COOLDOWN_SECONDS:-${F_LOSNING_BLOCKED_RECOVERY_COOLDOWN_SECONDS:-900}}"
|
|
30
32
|
MAX_OPEN_AGENT_PRS_FOR_RECURRING="${ACP_MAX_OPEN_AGENT_PRS_FOR_RECURRING:-${F_LOSNING_MAX_OPEN_AGENT_PRS_FOR_RECURRING:-12}}"
|
|
31
33
|
MAX_LAUNCHES_PER_HEARTBEAT="${ACP_MAX_LAUNCHES_PER_HEARTBEAT:-${F_LOSNING_MAX_LAUNCHES_PER_HEARTBEAT:-$MAX_CONCURRENT_WORKERS}}"
|
|
32
|
-
CODING_WORKER="${ACP_CODING_WORKER
|
|
34
|
+
CODING_WORKER="${ACP_CODING_WORKER:-codex}"
|
|
33
35
|
# The catchup and shared heartbeat passes can legitimately take a few minutes
|
|
34
36
|
# once they reconcile stale sessions, sync labels, and launch multiple workers.
|
|
35
37
|
CATCHUP_TIMEOUT_SECONDS="${ACP_CATCHUP_TIMEOUT_SECONDS:-${F_LOSNING_CATCHUP_TIMEOUT_SECONDS:-180}}"
|
|
@@ -67,9 +69,12 @@ AGENT_WORKTREE_AUDIT_TIMEOUT_SECONDS="${ACP_AGENT_WORKTREE_AUDIT_TIMEOUT_SECONDS
|
|
|
67
69
|
LOCK_DIR="${STATE_ROOT}/heartbeat-loop.lock"
|
|
68
70
|
PID_FILE="${LOCK_DIR}/pid"
|
|
69
71
|
SHARED_LOOP_PID_FILE="${STATE_ROOT}/shared-heartbeat-loop.pid"
|
|
72
|
+
SHARED_LOOP_STATUS_FILE="${STATE_ROOT}/shared-heartbeat-loop.env"
|
|
70
73
|
QUOTA_LOCK_DIR="${STATE_ROOT}/quota-preflight.lock"
|
|
71
74
|
QUOTA_PID_FILE="${QUOTA_LOCK_DIR}/pid"
|
|
72
75
|
|
|
76
|
+
mkdir -p "${AGENT_ROOT}" "${RUNS_ROOT}" "${STATE_ROOT}" "${HISTORY_ROOT}" "${WORKTREE_ROOT}" "${MEMORY_DIR}"
|
|
77
|
+
|
|
73
78
|
acquire_lock() {
|
|
74
79
|
mkdir -p "${STATE_ROOT}"
|
|
75
80
|
|
|
@@ -124,6 +129,29 @@ release_quota_lock() {
|
|
|
124
129
|
rm -rf "${QUOTA_LOCK_DIR}"
|
|
125
130
|
}
|
|
126
131
|
|
|
132
|
+
write_shared_loop_status() {
|
|
133
|
+
local state="${1:-}"
|
|
134
|
+
local status="${2:-}"
|
|
135
|
+
local timestamp=""
|
|
136
|
+
local tmp_file=""
|
|
137
|
+
|
|
138
|
+
timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
139
|
+
mkdir -p "${STATE_ROOT}"
|
|
140
|
+
tmp_file="$(mktemp)"
|
|
141
|
+
if [[ -f "${SHARED_LOOP_STATUS_FILE}" ]]; then
|
|
142
|
+
grep -Ev '^(STATE|STATUS|STARTED_AT|UPDATED_AT)=' "${SHARED_LOOP_STATUS_FILE}" >"${tmp_file}" || true
|
|
143
|
+
fi
|
|
144
|
+
printf 'STATE=%s\n' "${state}" >>"${tmp_file}"
|
|
145
|
+
if [[ -n "${status}" ]]; then
|
|
146
|
+
printf 'STATUS=%s\n' "${status}" >>"${tmp_file}"
|
|
147
|
+
fi
|
|
148
|
+
if [[ "${state}" == "running" ]]; then
|
|
149
|
+
printf 'STARTED_AT=%s\n' "${timestamp}" >>"${tmp_file}"
|
|
150
|
+
fi
|
|
151
|
+
printf 'UPDATED_AT=%s\n' "${timestamp}" >>"${tmp_file}"
|
|
152
|
+
mv "${tmp_file}" "${SHARED_LOOP_STATUS_FILE}"
|
|
153
|
+
}
|
|
154
|
+
|
|
127
155
|
run_with_timeout() {
|
|
128
156
|
local timeout_seconds="${1:?timeout seconds required}"
|
|
129
157
|
shift
|
|
@@ -530,6 +558,7 @@ fi
|
|
|
530
558
|
derive_dynamic_limits
|
|
531
559
|
|
|
532
560
|
printf '[%s] shared heartbeat loop start\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
561
|
+
write_shared_loop_status "running" ""
|
|
533
562
|
if ACP_TIMEOUT_CHILD_PID_FILE="${SHARED_LOOP_PID_FILE}" \
|
|
534
563
|
F_LOSNING_TIMEOUT_CHILD_PID_FILE="${SHARED_LOOP_PID_FILE}" \
|
|
535
564
|
run_with_timeout "${HEARTBEAT_LOOP_TIMEOUT_SECONDS}" \
|
|
@@ -563,9 +592,11 @@ printf '[%s] shared heartbeat loop start\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
|
563
592
|
--heavy-running-label "E2E_ISSUE" \
|
|
564
593
|
--heavy-deferred-key "E2E_DEFERRED" \
|
|
565
594
|
--heavy-deferred-message "E2E-heavy issues remain queued until the single e2e slot is free."; then
|
|
595
|
+
write_shared_loop_status "idle" "0"
|
|
566
596
|
printf '[%s] shared heartbeat loop end status=0\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
567
597
|
else
|
|
568
598
|
loop_status=$?
|
|
599
|
+
write_shared_loop_status "idle" "${loop_status}"
|
|
569
600
|
if [[ "${loop_status}" -eq 124 ]]; then
|
|
570
601
|
printf 'HEARTBEAT_LOOP_TIMEOUT=yes\n'
|
|
571
602
|
fi
|
|
@@ -573,21 +604,78 @@ else
|
|
|
573
604
|
exit "${loop_status}"
|
|
574
605
|
fi
|
|
575
606
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
607
|
+
# ── Throttled catch-up passes ──────────────────────────────────────────────────
|
|
608
|
+
# These scripts fetch merged/closed PRs and linked issues which change rarely.
|
|
609
|
+
# Run them at most once every CATCHUP_INTERVAL_SECONDS (default 300 = 5 min)
|
|
610
|
+
# to avoid burning API quota on every heartbeat cycle.
|
|
611
|
+
CATCHUP_INTERVAL_SECONDS="${ACP_CATCHUP_INTERVAL_SECONDS:-${F_LOSNING_CATCHUP_INTERVAL_SECONDS:-300}}"
|
|
612
|
+
CATCHUP_STAMP_FILE="${STATE_ROOT}/last-catchup-timestamp"
|
|
613
|
+
_catchup_now="$(date +%s)"
|
|
614
|
+
_catchup_last="0"
|
|
615
|
+
if [[ -f "${CATCHUP_STAMP_FILE}" ]]; then
|
|
616
|
+
_catchup_last="$(cat "${CATCHUP_STAMP_FILE}" 2>/dev/null || echo 0)"
|
|
617
|
+
fi
|
|
618
|
+
_catchup_age=$(( _catchup_now - _catchup_last ))
|
|
619
|
+
|
|
620
|
+
if [[ "${_catchup_age}" -ge "${CATCHUP_INTERVAL_SECONDS}" ]]; then
|
|
621
|
+
printf '[%s] merged-pr catchup start\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
622
|
+
if run_with_timeout "${CATCHUP_TIMEOUT_SECONDS}" \
|
|
623
|
+
env \
|
|
624
|
+
ACP_RUNS_ROOT="$RUNS_ROOT" \
|
|
625
|
+
F_LOSNING_RUNS_ROOT="$RUNS_ROOT" \
|
|
626
|
+
bash "${FLOW_TOOLS_DIR}/agent-project-catch-up-merged-prs" \
|
|
627
|
+
--repo-slug "$REPO_SLUG" \
|
|
628
|
+
--state-root "$STATE_ROOT" \
|
|
629
|
+
--hook-file "$HOOK_FILE" \
|
|
630
|
+
--limit 100; then
|
|
631
|
+
printf '[%s] merged-pr catchup end status=0\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
632
|
+
else
|
|
633
|
+
catchup_status=$?
|
|
634
|
+
if [[ "${catchup_status}" -eq 124 ]]; then
|
|
635
|
+
printf 'CATCHUP_TIMEOUT=yes\n'
|
|
636
|
+
fi
|
|
637
|
+
printf '[%s] merged-pr catchup end status=%s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "${catchup_status}"
|
|
638
|
+
fi
|
|
639
|
+
|
|
640
|
+
printf '[%s] linked-pr issue catchup start\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
641
|
+
if run_with_timeout "${CATCHUP_TIMEOUT_SECONDS}" \
|
|
642
|
+
env \
|
|
643
|
+
ACP_RUNS_ROOT="$RUNS_ROOT" \
|
|
644
|
+
F_LOSNING_RUNS_ROOT="$RUNS_ROOT" \
|
|
645
|
+
bash "${FLOW_TOOLS_DIR}/agent-project-catch-up-issue-pr-links" \
|
|
646
|
+
--repo-slug "$REPO_SLUG" \
|
|
647
|
+
--state-root "$STATE_ROOT" \
|
|
648
|
+
--hook-file "$HOOK_FILE" \
|
|
649
|
+
--limit 100; then
|
|
650
|
+
printf '[%s] linked-pr issue catchup end status=0\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
651
|
+
else
|
|
652
|
+
linked_issue_catchup_status=$?
|
|
653
|
+
if [[ "${linked_issue_catchup_status}" -eq 124 ]]; then
|
|
654
|
+
printf 'LINKED_ISSUE_CATCHUP_TIMEOUT=yes\n'
|
|
655
|
+
fi
|
|
656
|
+
printf '[%s] linked-pr issue catchup end status=%s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "${linked_issue_catchup_status}"
|
|
657
|
+
fi
|
|
658
|
+
|
|
659
|
+
printf '[%s] scheduled-issue retry catchup start\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
660
|
+
if run_with_timeout "${CATCHUP_TIMEOUT_SECONDS}" \
|
|
661
|
+
env \
|
|
662
|
+
ACP_RUNS_ROOT="$RUNS_ROOT" \
|
|
663
|
+
F_LOSNING_RUNS_ROOT="$RUNS_ROOT" \
|
|
664
|
+
bash "${FLOW_TOOLS_DIR}/agent-project-catch-up-scheduled-issue-retries" \
|
|
665
|
+
--repo-slug "$REPO_SLUG" \
|
|
666
|
+
--state-root "$STATE_ROOT" \
|
|
667
|
+
--hook-file "$HOOK_FILE" \
|
|
668
|
+
--limit 100; then
|
|
669
|
+
printf '[%s] scheduled-issue retry catchup end status=0\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
670
|
+
else
|
|
671
|
+
scheduled_issue_catchup_status=$?
|
|
672
|
+
if [[ "${scheduled_issue_catchup_status}" -eq 124 ]]; then
|
|
673
|
+
printf 'SCHEDULED_ISSUE_CATCHUP_TIMEOUT=yes\n'
|
|
674
|
+
fi
|
|
675
|
+
printf '[%s] scheduled-issue retry catchup end status=%s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "${scheduled_issue_catchup_status}"
|
|
591
676
|
fi
|
|
592
|
-
|
|
677
|
+
|
|
678
|
+
printf '%s' "${_catchup_now}" >"${CATCHUP_STAMP_FILE}"
|
|
679
|
+
else
|
|
680
|
+
printf '[%s] catchup skipped (age=%ss, interval=%ss)\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "${_catchup_age}" "${CATCHUP_INTERVAL_SECONDS}"
|
|
593
681
|
fi
|
|
@@ -54,7 +54,7 @@ build_launchd_base_path() {
|
|
|
54
54
|
local tool_name=""
|
|
55
55
|
local tool_dir=""
|
|
56
56
|
|
|
57
|
-
for tool_name in node gh git python3 openclaw codex claude; do
|
|
57
|
+
for tool_name in node gh git python3 openclaw codex claude ollama pi crush kilo; do
|
|
58
58
|
tool_dir="$(resolved_tool_dir "${tool_name}" || true)"
|
|
59
59
|
append_path_dir path_value "${tool_dir}"
|
|
60
60
|
done
|
|
@@ -112,7 +112,14 @@ fi
|
|
|
112
112
|
PROFILE_ID="$(flow_resolve_adapter_id "${CONFIG_YAML}")"
|
|
113
113
|
profile_slug="$(printf '%s' "${PROFILE_ID}" | tr -c 'A-Za-z0-9._-' '-')"
|
|
114
114
|
HOME_DIR="${ACP_PROJECT_RUNTIME_HOME_DIR:-${HOME:-}}"
|
|
115
|
-
SOURCE_HOME="${ACP_PROJECT_RUNTIME_SOURCE_HOME
|
|
115
|
+
SOURCE_HOME="${ACP_PROJECT_RUNTIME_SOURCE_HOME:-}"
|
|
116
|
+
if [[ -z "${SOURCE_HOME}" ]]; then
|
|
117
|
+
if flow_is_skill_root "${FLOW_SKILL_DIR}"; then
|
|
118
|
+
SOURCE_HOME="${FLOW_SKILL_DIR}"
|
|
119
|
+
else
|
|
120
|
+
SOURCE_HOME="$(cd "${FLOW_SKILL_DIR}/../../.." && pwd)"
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
116
123
|
RUNTIME_HOME="${ACP_PROJECT_RUNTIME_RUNTIME_HOME:-${HOME_DIR}/.agent-runtime/runtime-home}"
|
|
117
124
|
WORKSPACE_DIR="${ACP_PROJECT_RUNTIME_WORKSPACE_DIR:-${HOME_DIR}/.agent-runtime/control-plane/workspace}"
|
|
118
125
|
PROFILE_REGISTRY_ROOT="${ACP_PROJECT_RUNTIME_PROFILE_REGISTRY_ROOT:-${ACP_PROFILE_REGISTRY_ROOT:-${HOME_DIR}/.agent-runtime/control-plane/profiles}}"
|
|
@@ -120,6 +127,7 @@ LAUNCH_AGENTS_DIR="${ACP_PROJECT_RUNTIME_LAUNCH_AGENTS_DIR:-${HOME_DIR}/Library/
|
|
|
120
127
|
LOG_DIR="${ACP_PROJECT_RUNTIME_LOG_DIR:-${HOME_DIR}/.agent-runtime/logs}"
|
|
121
128
|
LABEL="${label_override:-${ACP_PROJECT_RUNTIME_LAUNCHD_LABEL:-ai.agent.project.${profile_slug}}}"
|
|
122
129
|
BASE_PATH="$(build_launchd_base_path)"
|
|
130
|
+
CODING_WORKER_OVERRIDE="${ACP_PROJECT_RUNTIME_CODING_WORKER:-${ACP_CODING_WORKER:-}}"
|
|
123
131
|
SYNC_SCRIPT="${ACP_PROJECT_RUNTIME_SYNC_SCRIPT:-${FLOW_SKILL_DIR}/tools/bin/sync-shared-agent-home.sh}"
|
|
124
132
|
BOOTSTRAP_SCRIPT="${ACP_PROJECT_RUNTIME_BOOTSTRAP_SCRIPT:-${FLOW_SKILL_DIR}/tools/bin/project-launchd-bootstrap.sh}"
|
|
125
133
|
SUPERVISOR_SCRIPT="${ACP_PROJECT_RUNTIME_SUPERVISOR_SCRIPT:-${FLOW_SKILL_DIR}/tools/bin/project-runtime-supervisor.sh}"
|
|
@@ -152,6 +160,15 @@ export AGENT_PROJECT_ID='${PROFILE_ID}'
|
|
|
152
160
|
export ACP_PROJECT_RUNTIME_PATH='${BASE_PATH}'
|
|
153
161
|
export ACP_PROJECT_RUNTIME_SYNC_SCRIPT='${SYNC_SCRIPT}'
|
|
154
162
|
export ACP_PROFILE_REGISTRY_ROOT='${PROFILE_REGISTRY_ROOT}'
|
|
163
|
+
EOF
|
|
164
|
+
|
|
165
|
+
if [[ -n "${CODING_WORKER_OVERRIDE}" ]]; then
|
|
166
|
+
cat >>"${WRAPPER_PATH}" <<EOF
|
|
167
|
+
export ACP_CODING_WORKER='${CODING_WORKER_OVERRIDE}'
|
|
168
|
+
EOF
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
cat >>"${WRAPPER_PATH}" <<EOF
|
|
155
172
|
exec bash '${SUPERVISOR_SCRIPT}' --bootstrap-script '${BOOTSTRAP_SCRIPT}' --pid-file '${SUPERVISOR_PID_FILE}' --delay-seconds '${delay_seconds}' --interval-seconds '${interval_seconds}'
|
|
156
173
|
EOF
|
|
157
174
|
chmod +x "${WRAPPER_PATH}"
|
|
@@ -9,10 +9,10 @@ CONFIG_YAML="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
|
|
|
9
9
|
AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
|
|
10
10
|
CANONICAL_REPO_ROOT="$(flow_resolve_repo_root "${CONFIG_YAML}")"
|
|
11
11
|
DEFAULT_DEPENDENCY_SOURCE_ROOT="$CANONICAL_REPO_ROOT"
|
|
12
|
-
DEPENDENCY_SOURCE_ROOT="${ACP_DEPENDENCY_SOURCE_ROOT:-$
|
|
13
|
-
SYNC_DEPENDENCY_BASELINE_SCRIPT="${ACP_SYNC_DEPENDENCY_BASELINE_SCRIPT:-${
|
|
14
|
-
PACKAGE_MANAGER_BIN="${ACP_PACKAGE_MANAGER_BIN
|
|
15
|
-
LOCAL_WORKSPACE_INSTALL="${ACP_WORKTREE_LOCAL_INSTALL
|
|
12
|
+
DEPENDENCY_SOURCE_ROOT="${ACP_DEPENDENCY_SOURCE_ROOT:-$DEFAULT_DEPENDENCY_SOURCE_ROOT}"
|
|
13
|
+
SYNC_DEPENDENCY_BASELINE_SCRIPT="${ACP_SYNC_DEPENDENCY_BASELINE_SCRIPT:-${SCRIPT_DIR}/sync-dependency-baseline.sh}"
|
|
14
|
+
PACKAGE_MANAGER_BIN="${ACP_PACKAGE_MANAGER_BIN:-pnpm}"
|
|
15
|
+
LOCAL_WORKSPACE_INSTALL="${ACP_WORKTREE_LOCAL_INSTALL:-false}"
|
|
16
16
|
WORKTREE="${1:?usage: prepare-worktree.sh WORKTREE}"
|
|
17
17
|
|
|
18
18
|
realpath_safe() {
|
|
@@ -56,8 +56,8 @@ AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
|
|
|
56
56
|
WORKTREE_ROOT="$(flow_resolve_worktree_root "${CONFIG_YAML}")"
|
|
57
57
|
RUNS_ROOT="$(flow_resolve_runs_root "${CONFIG_YAML}")"
|
|
58
58
|
STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
|
|
59
|
-
CODING_WORKER="${ACP_CODING_WORKER
|
|
60
|
-
ACTIVE_PROVIDER_POOL_NAME="${ACP_ACTIVE_PROVIDER_POOL_NAME
|
|
59
|
+
CODING_WORKER="${ACP_CODING_WORKER:-codex}"
|
|
60
|
+
ACTIVE_PROVIDER_POOL_NAME="${ACP_ACTIVE_PROVIDER_POOL_NAME:-}"
|
|
61
61
|
|
|
62
62
|
if [[ "${exports_only}" == "1" ]]; then
|
|
63
63
|
printf 'export ACP_PROJECT_ID=%q
|
|
@@ -69,8 +69,8 @@ HISTORY_ROOT="$(flow_resolve_history_root "${CONFIG_YAML}")"
|
|
|
69
69
|
WORKTREE_ROOT="$(flow_resolve_worktree_root "${CONFIG_YAML}")"
|
|
70
70
|
RETAINED_REPO_ROOT="$(flow_resolve_retained_repo_root "${CONFIG_YAML}")"
|
|
71
71
|
VSCODE_WORKSPACE_FILE="$(flow_resolve_vscode_workspace_file "${CONFIG_YAML}")"
|
|
72
|
-
REMOTE_NAME="${ACP_REMOTE_NAME
|
|
73
|
-
SOURCE_REPO_ROOT="${source_repo_root_override:-${ACP_SOURCE_REPO_ROOT:-${
|
|
72
|
+
REMOTE_NAME="${ACP_REMOTE_NAME:-origin}"
|
|
73
|
+
SOURCE_REPO_ROOT="${source_repo_root_override:-${ACP_SOURCE_REPO_ROOT:-${RETAINED_REPO_ROOT}}}"
|
|
74
74
|
PROFILE_LINK="${AGENT_ROOT}/control-plane.yaml"
|
|
75
75
|
WORKSPACE_LINK="${AGENT_ROOT}/workspace.code-workspace"
|
|
76
76
|
INSTALLED_PROFILE_DIR="$(dirname "${CONFIG_YAML}")"
|
|
@@ -42,7 +42,7 @@ Common options:
|
|
|
42
42
|
--worktree-root <path> Worktree parent root
|
|
43
43
|
--retained-repo-root <path> Retained/manual checkout root
|
|
44
44
|
--vscode-workspace-file <path> VS Code workspace file
|
|
45
|
-
--coding-worker <codex|openclaw|claude>
|
|
45
|
+
--coding-worker <codex|openclaw|claude|ollama|pi|opencode|kilo>
|
|
46
46
|
--claude-model <model>
|
|
47
47
|
--claude-permission-mode <mode>
|
|
48
48
|
--claude-effort <level>
|
|
@@ -168,13 +168,15 @@ join_by_comma() {
|
|
|
168
168
|
runtime_started() {
|
|
169
169
|
local heartbeat=""
|
|
170
170
|
local shared_loop=""
|
|
171
|
-
local
|
|
171
|
+
local active_sessions=""
|
|
172
|
+
local pending_pids=""
|
|
172
173
|
|
|
173
174
|
heartbeat="$(heartbeat_pid)"
|
|
174
175
|
shared_loop="$(shared_loop_pid)"
|
|
175
|
-
|
|
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 "${
|
|
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 < <(
|
|
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}" ||
|
|
417
|
+
if [[ -n "${heartbeat}" || -n "${shared_loop}" || "${active_session_count}" != "0" ]]; then
|
|
362
418
|
runtime_status="running"
|
|
363
419
|
fi
|
|
364
|
-
if [[
|
|
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
|
|
69
|
-
printf '%s\n' "${ACP_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
|
|
74
|
-
printf '%s\n' "${ACP_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
|
|
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
|
|
92
|
-
printf '%s\n' "${ACP_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
|
|
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
|
|
125
|
-
printf '%s\n' "${ACP_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
|
|
136
|
-
printf '%s\n' "${ACP_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
|
|
147
|
-
printf '%s\n' "${ACP_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
|