agent-control-plane 0.1.16 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -14
- package/bin/pr-risk.sh +28 -6
- package/hooks/heartbeat-hooks.sh +62 -22
- package/npm/bin/agent-control-plane.js +360 -10
- package/package.json +6 -3
- package/references/architecture.md +8 -0
- package/references/control-plane-map.md +6 -2
- package/references/release-checklist.md +0 -2
- package/tools/bin/agent-github-update-labels +6 -1
- package/tools/bin/agent-project-catch-up-issue-pr-links +118 -0
- package/tools/bin/agent-project-catch-up-merged-prs +78 -21
- package/tools/bin/agent-project-catch-up-scheduled-issue-retries +123 -0
- package/tools/bin/agent-project-cleanup-session +132 -4
- package/tools/bin/agent-project-heartbeat-loop +116 -1461
- package/tools/bin/agent-project-reconcile-issue-session +90 -117
- package/tools/bin/agent-project-reconcile-pr-session +76 -111
- package/tools/bin/agent-project-run-claude-session +12 -2
- package/tools/bin/agent-project-run-codex-resilient +86 -9
- package/tools/bin/agent-project-run-codex-session +16 -5
- package/tools/bin/agent-project-run-kilo-session +356 -14
- package/tools/bin/agent-project-run-ollama-session +658 -0
- package/tools/bin/agent-project-run-openclaw-session +37 -25
- package/tools/bin/agent-project-run-opencode-session +364 -14
- package/tools/bin/agent-project-run-pi-session +479 -0
- package/tools/bin/agent-project-worker-status +11 -8
- package/tools/bin/cleanup-worktree.sh +6 -1
- package/tools/bin/flow-config-lib.sh +196 -3
- package/tools/bin/flow-resident-worker-lib.sh +120 -2
- package/tools/bin/flow-shell-lib.sh +29 -2
- package/tools/bin/heartbeat-loop-cache-lib.sh +164 -0
- package/tools/bin/heartbeat-loop-counting-lib.sh +306 -0
- package/tools/bin/heartbeat-loop-pr-strategy-lib.sh +199 -0
- package/tools/bin/heartbeat-loop-scheduling-lib.sh +506 -0
- package/tools/bin/heartbeat-loop-worker-lib.sh +319 -0
- package/tools/bin/heartbeat-recovery-preflight.sh +13 -1
- package/tools/bin/heartbeat-safe-auto.sh +119 -20
- package/tools/bin/install-project-launchd.sh +19 -2
- package/tools/bin/prepare-worktree.sh +4 -4
- package/tools/bin/profile-activate.sh +2 -2
- package/tools/bin/profile-adopt.sh +2 -2
- package/tools/bin/project-init.sh +1 -1
- package/tools/bin/project-launchd-bootstrap.sh +11 -8
- package/tools/bin/project-runtimectl.sh +90 -7
- package/tools/bin/provider-cooldown-state.sh +14 -14
- package/tools/bin/reconcile-bootstrap-lib.sh +113 -0
- package/tools/bin/render-flow-config.sh +30 -33
- package/tools/bin/resident-issue-controller-lib.sh +448 -0
- package/tools/bin/resident-issue-queue-status.py +35 -0
- package/tools/bin/run-codex-task.sh +53 -4
- package/tools/bin/scaffold-profile.sh +18 -3
- package/tools/bin/start-issue-worker.sh +1 -1
- package/tools/bin/start-pr-fix-worker.sh +30 -0
- package/tools/bin/start-pr-review-worker.sh +31 -0
- package/tools/bin/start-resident-issue-loop.sh +27 -438
- package/tools/bin/sync-agent-repo.sh +2 -2
- package/tools/bin/sync-dependency-baseline.sh +3 -3
- package/tools/bin/sync-shared-agent-home.sh +4 -1
- package/tools/dashboard/app.js +7 -0
- package/tools/dashboard/dashboard_snapshot.py +13 -29
- package/tools/templates/pr-fix-template.md +3 -7
- package/tools/templates/pr-merge-repair-template.md +3 -7
- package/tools/templates/pr-review-template.md +2 -1
- package/SKILL.md +0 -149
|
@@ -2,82 +2,33 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"${
|
|
13
|
-
|
|
14
|
-
"${F_LOSNING_FLOW_ROOT:-}/tools/bin/flow-shell-lib.sh" \
|
|
15
|
-
"${AGENT_FLOW_SKILL_ROOT:-}/tools/bin/flow-shell-lib.sh" \
|
|
16
|
-
"${SHARED_AGENT_HOME:-}/tools/bin/flow-shell-lib.sh" \
|
|
17
|
-
"$(pwd)/tools/bin/flow-shell-lib.sh"; do
|
|
18
|
-
if [[ -n "${candidate}" && -f "${candidate}" ]]; then
|
|
19
|
-
printf '%s\n' "${candidate}"
|
|
20
|
-
return 0
|
|
21
|
-
fi
|
|
22
|
-
done
|
|
23
|
-
|
|
24
|
-
if [[ -n "${SHARED_AGENT_HOME:-}" ]]; then
|
|
25
|
-
for skill_name in "${AGENT_CONTROL_PLANE_SKILL_NAME:-agent-control-plane}" "${AGENT_CONTROL_PLANE_COMPAT_ALIAS:-}"; do
|
|
26
|
-
[[ -n "${skill_name}" ]] || continue
|
|
27
|
-
candidate="${SHARED_AGENT_HOME}/skills/openclaw/${skill_name}/tools/bin/flow-shell-lib.sh"
|
|
28
|
-
if [[ -f "${candidate}" ]]; then
|
|
29
|
-
printf '%s\n' "${candidate}"
|
|
30
|
-
return 0
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
5
|
+
RECONCILE_BOOTSTRAP_LIB=""
|
|
6
|
+
for _rbl_candidate in \
|
|
7
|
+
"${SCRIPT_DIR}/reconcile-bootstrap-lib.sh" \
|
|
8
|
+
"${AGENT_CONTROL_PLANE_ROOT:-}/tools/bin/reconcile-bootstrap-lib.sh" \
|
|
9
|
+
"${ACP_ROOT:-}/tools/bin/reconcile-bootstrap-lib.sh" \
|
|
10
|
+
"${SHARED_AGENT_HOME:-}/tools/bin/reconcile-bootstrap-lib.sh"; do
|
|
11
|
+
if [[ -n "${_rbl_candidate}" && -f "${_rbl_candidate}" ]]; then
|
|
12
|
+
RECONCILE_BOOTSTRAP_LIB="${_rbl_candidate}"
|
|
13
|
+
break
|
|
33
14
|
fi
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
source "${FLOW_SHELL_LIB_PATH}"
|
|
43
|
-
|
|
44
|
-
resolve_reconcile_tools_dir() {
|
|
45
|
-
local candidate_root=""
|
|
46
|
-
local skill_name=""
|
|
47
|
-
|
|
48
|
-
for candidate_root in \
|
|
49
|
-
"${AGENT_CONTROL_PLANE_ROOT:-}" \
|
|
50
|
-
"${ACP_ROOT:-}" \
|
|
51
|
-
"${F_LOSNING_FLOW_ROOT:-}" \
|
|
52
|
-
"${AGENT_FLOW_SKILL_ROOT:-}"; do
|
|
53
|
-
if [[ -n "${candidate_root}" && -d "${candidate_root}/tools/bin" ]]; then
|
|
54
|
-
printf '%s/tools/bin\n' "${candidate_root}"
|
|
55
|
-
return 0
|
|
15
|
+
done
|
|
16
|
+
if [[ -n "${SHARED_AGENT_HOME:-}" && -z "${RECONCILE_BOOTSTRAP_LIB}" ]]; then
|
|
17
|
+
for _rbl_skill in "${AGENT_CONTROL_PLANE_SKILL_NAME:-agent-control-plane}" "${AGENT_CONTROL_PLANE_COMPAT_ALIAS:-}"; do
|
|
18
|
+
[[ -n "${_rbl_skill}" ]] || continue
|
|
19
|
+
_rbl_candidate="${SHARED_AGENT_HOME}/skills/openclaw/${_rbl_skill}/tools/bin/reconcile-bootstrap-lib.sh"
|
|
20
|
+
if [[ -f "${_rbl_candidate}" ]]; then
|
|
21
|
+
RECONCILE_BOOTSTRAP_LIB="${_rbl_candidate}"
|
|
22
|
+
break
|
|
56
23
|
fi
|
|
57
24
|
done
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
[[ -n "${skill_name}" ]] || continue
|
|
66
|
-
candidate_root="${SHARED_AGENT_HOME}/skills/openclaw/${skill_name}"
|
|
67
|
-
if [[ -d "${candidate_root}/tools/bin" ]]; then
|
|
68
|
-
printf '%s/tools/bin\n' "${candidate_root}"
|
|
69
|
-
return 0
|
|
70
|
-
fi
|
|
71
|
-
done
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
if [[ -d "${SCRIPT_DIR}" ]]; then
|
|
75
|
-
printf '%s\n' "${SCRIPT_DIR}"
|
|
76
|
-
return 0
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
printf '%s\n' "${BOOTSTRAP_TOOLS_DIR}"
|
|
80
|
-
}
|
|
25
|
+
fi
|
|
26
|
+
if [[ -z "${RECONCILE_BOOTSTRAP_LIB}" ]]; then
|
|
27
|
+
echo "unable to locate reconcile-bootstrap-lib.sh" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
# shellcheck source=/dev/null
|
|
31
|
+
source "${RECONCILE_BOOTSTRAP_LIB}"
|
|
81
32
|
|
|
82
33
|
usage() {
|
|
83
34
|
cat <<'EOF'
|
|
@@ -89,30 +40,8 @@ allowing project adapters to inject policy hooks.
|
|
|
89
40
|
EOF
|
|
90
41
|
}
|
|
91
42
|
|
|
92
|
-
shared_tools_dir="$(resolve_reconcile_tools_dir)"
|
|
93
|
-
resolve_reconcile_helper_path() {
|
|
94
|
-
local helper_name="${1:?helper name required}"
|
|
95
|
-
local candidate=""
|
|
96
|
-
|
|
97
|
-
for candidate in \
|
|
98
|
-
"${SCRIPT_DIR}/${helper_name}" \
|
|
99
|
-
"${BOOTSTRAP_TOOLS_DIR}/${helper_name}" \
|
|
100
|
-
"${shared_tools_dir}/${helper_name}"; do
|
|
101
|
-
if [[ -n "${candidate}" && -f "${candidate}" ]]; then
|
|
102
|
-
printf '%s\n' "${candidate}"
|
|
103
|
-
return 0
|
|
104
|
-
fi
|
|
105
|
-
done
|
|
106
|
-
|
|
107
|
-
echo "unable to locate ${helper_name} for reconcile bootstrap" >&2
|
|
108
|
-
return 1
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
FLOW_CONFIG_LIB_PATH="$(resolve_reconcile_helper_path "flow-config-lib.sh")"
|
|
112
43
|
FLOW_RESIDENT_WORKER_LIB_PATH="$(resolve_reconcile_helper_path "flow-resident-worker-lib.sh")"
|
|
113
44
|
# shellcheck source=/dev/null
|
|
114
|
-
source "${FLOW_CONFIG_LIB_PATH}"
|
|
115
|
-
# shellcheck source=/dev/null
|
|
116
45
|
source "${FLOW_RESIDENT_WORKER_LIB_PATH}"
|
|
117
46
|
session=""
|
|
118
47
|
repo_slug=""
|
|
@@ -640,6 +569,9 @@ try {
|
|
|
640
569
|
|
|
641
570
|
const changedFilesLower = gitChangedFiles.map((file) => file.toLowerCase());
|
|
642
571
|
const repoHasScript = (scriptName) => Boolean(packageJson?.scripts && Object.prototype.hasOwnProperty.call(packageJson.scripts, scriptName));
|
|
572
|
+
const rootTestScript = String(packageJson?.scripts?.test || '').trim();
|
|
573
|
+
const rootTestScriptUsesNodeTest = /^node\s+--test(?:\s|$)/.test(rootTestScript);
|
|
574
|
+
const rootTestScriptLooksWatchMode = /\B--watch(?:All)?(?:[=\s]|$)|(?:^|\s)vitest\s+watch(?:\s|$)/.test(rootTestScript);
|
|
643
575
|
const commandLooksRunnable = (command) => {
|
|
644
576
|
if (/^npm test(?:\s|$)?/.test(command)) return repoHasScript('test');
|
|
645
577
|
if (/^pnpm test(?:\s|$)?/.test(command)) return repoHasScript('test');
|
|
@@ -648,6 +580,14 @@ const commandLooksRunnable = (command) => {
|
|
|
648
580
|
if (/^node\s+--test(?:\s|$)/.test(command)) return true;
|
|
649
581
|
return true;
|
|
650
582
|
};
|
|
583
|
+
const rootTestFallbackCommand = () => {
|
|
584
|
+
if (!rootTestScript) return '';
|
|
585
|
+
if (rootTestScriptUsesNodeTest) return 'npm test';
|
|
586
|
+
if (!rootTestScriptLooksWatchMode) return 'npm test';
|
|
587
|
+
if (/\bjest\b/i.test(rootTestScript)) return 'npx jest --runInBand --watchAll=false';
|
|
588
|
+
if (/\bvitest\b/i.test(rootTestScript)) return 'npx vitest run';
|
|
589
|
+
return '';
|
|
590
|
+
};
|
|
651
591
|
|
|
652
592
|
if (promptFile && fs.existsSync(promptFile)) {
|
|
653
593
|
const lines = fs.readFileSync(promptFile, 'utf8').split(/\r?\n/).slice(0, 40);
|
|
@@ -678,15 +618,17 @@ if (promptFile && fs.existsSync(promptFile)) {
|
|
|
678
618
|
}
|
|
679
619
|
|
|
680
620
|
const changedTestFiles = [...new Set(gitChangedFiles.filter((file) => /\.(?:spec|test)\.[cm]?[jt]sx?$/.test(file)))];
|
|
681
|
-
|
|
682
|
-
if (/^node\s+--test(?:\s|$)/.test(rootTestScript)) {
|
|
621
|
+
if (rootTestScriptUsesNodeTest) {
|
|
683
622
|
for (const file of changedTestFiles) {
|
|
684
623
|
addCommand(`node --test ${file}`);
|
|
685
624
|
}
|
|
686
625
|
}
|
|
687
626
|
|
|
688
|
-
if (commands.length === 0
|
|
689
|
-
|
|
627
|
+
if (commands.length === 0) {
|
|
628
|
+
const fallbackCommand = rootTestFallbackCommand();
|
|
629
|
+
if (fallbackCommand) {
|
|
630
|
+
addCommand(fallbackCommand);
|
|
631
|
+
}
|
|
690
632
|
}
|
|
691
633
|
|
|
692
634
|
const filtered = commands.filter((command) => !recordedPassCommands.has(command));
|
|
@@ -1040,15 +982,6 @@ extract_recovery_worktree_from_publish_output() {
|
|
|
1040
982
|
awk -F= '/^RECOVERY_WORKTREE=/{print $2}' <<<"$publish_out" | tail -n 1
|
|
1041
983
|
}
|
|
1042
984
|
|
|
1043
|
-
require_transition() {
|
|
1044
|
-
local step="${1:?step required}"
|
|
1045
|
-
shift
|
|
1046
|
-
if ! "$@"; then
|
|
1047
|
-
echo "reconcile transition failed: ${step}" >&2
|
|
1048
|
-
exit 1
|
|
1049
|
-
fi
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
985
|
mark_reconciled() {
|
|
1053
986
|
local reconciled_at tmp_file
|
|
1054
987
|
if [[ -d "$run_dir" ]]; then
|
|
@@ -1128,6 +1061,40 @@ update_resident_issue_metadata() {
|
|
|
1128
1061
|
"LAST_WORKTREE_REUSED=${last_worktree_reused:-no}"
|
|
1129
1062
|
}
|
|
1130
1063
|
|
|
1064
|
+
cleanup_output_value() {
|
|
1065
|
+
local cleanup_output="${1:-}"
|
|
1066
|
+
local key="${2:?key required}"
|
|
1067
|
+
awk -F= -v target_key="${key}" '$1 == target_key { print substr($0, index($0, "=") + 1); exit }' <<<"${cleanup_output}"
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
warn_cleanup_issue_session() {
|
|
1071
|
+
local cleanup_output="${1:-}"
|
|
1072
|
+
local cleanup_exit="${2:-0}"
|
|
1073
|
+
local cleanup_status=""
|
|
1074
|
+
local cleanup_mode=""
|
|
1075
|
+
local cleanup_error=""
|
|
1076
|
+
|
|
1077
|
+
cleanup_status="$(cleanup_output_value "${cleanup_output}" "CLEANUP_STATUS")"
|
|
1078
|
+
if [[ -z "${cleanup_status}" ]]; then
|
|
1079
|
+
cleanup_status="${cleanup_exit}"
|
|
1080
|
+
fi
|
|
1081
|
+
[[ "${cleanup_status}" != "0" ]] || return 0
|
|
1082
|
+
|
|
1083
|
+
cleanup_mode="$(cleanup_output_value "${cleanup_output}" "CLEANUP_MODE")"
|
|
1084
|
+
cleanup_error="$(cleanup_output_value "${cleanup_output}" "CLEANUP_ERROR")"
|
|
1085
|
+
printf '[%s] issue cleanup warning session=%s status=%s mode=%s\n' \
|
|
1086
|
+
"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
|
1087
|
+
"${session}" \
|
|
1088
|
+
"${cleanup_status}" \
|
|
1089
|
+
"${cleanup_mode:-unknown}" >&2
|
|
1090
|
+
if [[ -n "${cleanup_error}" ]]; then
|
|
1091
|
+
printf '[%s] issue cleanup detail session=%s %s\n' \
|
|
1092
|
+
"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
|
1093
|
+
"${session}" \
|
|
1094
|
+
"${cleanup_error}" >&2
|
|
1095
|
+
fi
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1131
1098
|
cleanup_issue_session() {
|
|
1132
1099
|
local -a cleanup_args=(
|
|
1133
1100
|
--repo-root "$repo_root"
|
|
@@ -1143,7 +1110,14 @@ cleanup_issue_session() {
|
|
|
1143
1110
|
cleanup_args+=(--skip-worktree-cleanup)
|
|
1144
1111
|
fi
|
|
1145
1112
|
|
|
1146
|
-
|
|
1113
|
+
local cleanup_output=""
|
|
1114
|
+
local cleanup_exit="0"
|
|
1115
|
+
if cleanup_output="$("${shared_tools_dir}/agent-project-cleanup-session" "${cleanup_args[@]}" 2>&1)"; then
|
|
1116
|
+
cleanup_exit="0"
|
|
1117
|
+
else
|
|
1118
|
+
cleanup_exit="$?"
|
|
1119
|
+
fi
|
|
1120
|
+
warn_cleanup_issue_session "${cleanup_output}" "${cleanup_exit}"
|
|
1147
1121
|
}
|
|
1148
1122
|
|
|
1149
1123
|
notify_issue_reconciled() {
|
|
@@ -1382,19 +1356,18 @@ case "$status" in
|
|
|
1382
1356
|
failure_reason="$(normalize_issue_failure_reason "${failure_reason:-worker-exit-failed}")"
|
|
1383
1357
|
schedule_provider_quota_cooldown "${failure_reason}"
|
|
1384
1358
|
normalize_issue_runner_state "failed" "${LAST_EXIT_CODE:-}" "${failure_reason}"
|
|
1385
|
-
if [[ "${result_outcome:-}" == "blocked" && "${result_action:-}" == "host-comment-blocker" ]]
|
|
1386
|
-
|| [[ "${failure_reason}" == "provider-quota-limit" ]]; then
|
|
1387
|
-
if [[ -z "${result_outcome:-}" ]]; then
|
|
1388
|
-
result_outcome="blocked"
|
|
1389
|
-
fi
|
|
1390
|
-
if [[ -z "${result_action:-}" ]]; then
|
|
1391
|
-
result_action="host-comment-blocker"
|
|
1392
|
-
fi
|
|
1359
|
+
if [[ "${result_outcome:-}" == "blocked" && "${result_action:-}" == "host-comment-blocker" ]]; then
|
|
1393
1360
|
if [[ ! -s "${run_dir}/issue-comment.md" ]]; then
|
|
1394
1361
|
write_issue_comment_artifact "$(build_issue_runtime_blocker_comment "${failure_reason}")" || true
|
|
1395
1362
|
fi
|
|
1396
1363
|
post_issue_comment_if_present
|
|
1397
1364
|
issue_set_reconcile_summary "$status" "$result_outcome" "$result_action" "$failure_reason"
|
|
1365
|
+
elif [[ "${failure_reason}" == "provider-quota-limit" ]]; then
|
|
1366
|
+
if [[ ! -s "${run_dir}/issue-comment.md" ]]; then
|
|
1367
|
+
write_issue_comment_artifact "$(build_issue_runtime_blocker_comment "${failure_reason}")" || true
|
|
1368
|
+
fi
|
|
1369
|
+
post_issue_comment_if_present
|
|
1370
|
+
issue_set_reconcile_summary "$status" "" "" "$failure_reason"
|
|
1398
1371
|
else
|
|
1399
1372
|
issue_set_reconcile_summary "$status" "" "" "$failure_reason"
|
|
1400
1373
|
fi
|
|
@@ -2,82 +2,33 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"${
|
|
13
|
-
|
|
14
|
-
"${F_LOSNING_FLOW_ROOT:-}/tools/bin/flow-shell-lib.sh" \
|
|
15
|
-
"${AGENT_FLOW_SKILL_ROOT:-}/tools/bin/flow-shell-lib.sh" \
|
|
16
|
-
"${SHARED_AGENT_HOME:-}/tools/bin/flow-shell-lib.sh" \
|
|
17
|
-
"$(pwd)/tools/bin/flow-shell-lib.sh"; do
|
|
18
|
-
if [[ -n "${candidate}" && -f "${candidate}" ]]; then
|
|
19
|
-
printf '%s\n' "${candidate}"
|
|
20
|
-
return 0
|
|
21
|
-
fi
|
|
22
|
-
done
|
|
23
|
-
|
|
24
|
-
if [[ -n "${SHARED_AGENT_HOME:-}" ]]; then
|
|
25
|
-
for skill_name in "${AGENT_CONTROL_PLANE_SKILL_NAME:-agent-control-plane}" "${AGENT_CONTROL_PLANE_COMPAT_ALIAS:-}"; do
|
|
26
|
-
[[ -n "${skill_name}" ]] || continue
|
|
27
|
-
candidate="${SHARED_AGENT_HOME}/skills/openclaw/${skill_name}/tools/bin/flow-shell-lib.sh"
|
|
28
|
-
if [[ -f "${candidate}" ]]; then
|
|
29
|
-
printf '%s\n' "${candidate}"
|
|
30
|
-
return 0
|
|
31
|
-
fi
|
|
32
|
-
done
|
|
5
|
+
RECONCILE_BOOTSTRAP_LIB=""
|
|
6
|
+
for _rbl_candidate in \
|
|
7
|
+
"${SCRIPT_DIR}/reconcile-bootstrap-lib.sh" \
|
|
8
|
+
"${AGENT_CONTROL_PLANE_ROOT:-}/tools/bin/reconcile-bootstrap-lib.sh" \
|
|
9
|
+
"${ACP_ROOT:-}/tools/bin/reconcile-bootstrap-lib.sh" \
|
|
10
|
+
"${SHARED_AGENT_HOME:-}/tools/bin/reconcile-bootstrap-lib.sh"; do
|
|
11
|
+
if [[ -n "${_rbl_candidate}" && -f "${_rbl_candidate}" ]]; then
|
|
12
|
+
RECONCILE_BOOTSTRAP_LIB="${_rbl_candidate}"
|
|
13
|
+
break
|
|
33
14
|
fi
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
source "${FLOW_SHELL_LIB_PATH}"
|
|
43
|
-
|
|
44
|
-
resolve_reconcile_tools_dir() {
|
|
45
|
-
local candidate_root=""
|
|
46
|
-
local skill_name=""
|
|
47
|
-
|
|
48
|
-
for candidate_root in \
|
|
49
|
-
"${AGENT_CONTROL_PLANE_ROOT:-}" \
|
|
50
|
-
"${ACP_ROOT:-}" \
|
|
51
|
-
"${F_LOSNING_FLOW_ROOT:-}" \
|
|
52
|
-
"${AGENT_FLOW_SKILL_ROOT:-}"; do
|
|
53
|
-
if [[ -n "${candidate_root}" && -d "${candidate_root}/tools/bin" ]]; then
|
|
54
|
-
printf '%s/tools/bin\n' "${candidate_root}"
|
|
55
|
-
return 0
|
|
15
|
+
done
|
|
16
|
+
if [[ -n "${SHARED_AGENT_HOME:-}" && -z "${RECONCILE_BOOTSTRAP_LIB}" ]]; then
|
|
17
|
+
for _rbl_skill in "${AGENT_CONTROL_PLANE_SKILL_NAME:-agent-control-plane}" "${AGENT_CONTROL_PLANE_COMPAT_ALIAS:-}"; do
|
|
18
|
+
[[ -n "${_rbl_skill}" ]] || continue
|
|
19
|
+
_rbl_candidate="${SHARED_AGENT_HOME}/skills/openclaw/${_rbl_skill}/tools/bin/reconcile-bootstrap-lib.sh"
|
|
20
|
+
if [[ -f "${_rbl_candidate}" ]]; then
|
|
21
|
+
RECONCILE_BOOTSTRAP_LIB="${_rbl_candidate}"
|
|
22
|
+
break
|
|
56
23
|
fi
|
|
57
24
|
done
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
[[ -n "${skill_name}" ]] || continue
|
|
66
|
-
candidate_root="${SHARED_AGENT_HOME}/skills/openclaw/${skill_name}"
|
|
67
|
-
if [[ -d "${candidate_root}/tools/bin" ]]; then
|
|
68
|
-
printf '%s/tools/bin\n' "${candidate_root}"
|
|
69
|
-
return 0
|
|
70
|
-
fi
|
|
71
|
-
done
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
if [[ -d "${SCRIPT_DIR}" ]]; then
|
|
75
|
-
printf '%s\n' "${SCRIPT_DIR}"
|
|
76
|
-
return 0
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
printf '%s\n' "${BOOTSTRAP_TOOLS_DIR}"
|
|
80
|
-
}
|
|
25
|
+
fi
|
|
26
|
+
if [[ -z "${RECONCILE_BOOTSTRAP_LIB}" ]]; then
|
|
27
|
+
echo "unable to locate reconcile-bootstrap-lib.sh" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
# shellcheck source=/dev/null
|
|
31
|
+
source "${RECONCILE_BOOTSTRAP_LIB}"
|
|
81
32
|
|
|
82
33
|
usage() {
|
|
83
34
|
cat <<'EOF'
|
|
@@ -89,28 +40,6 @@ allowing project adapters to inject policy hooks.
|
|
|
89
40
|
EOF
|
|
90
41
|
}
|
|
91
42
|
|
|
92
|
-
shared_tools_dir="$(resolve_reconcile_tools_dir)"
|
|
93
|
-
resolve_reconcile_helper_path() {
|
|
94
|
-
local helper_name="${1:?helper name required}"
|
|
95
|
-
local candidate=""
|
|
96
|
-
|
|
97
|
-
for candidate in \
|
|
98
|
-
"${SCRIPT_DIR}/${helper_name}" \
|
|
99
|
-
"${BOOTSTRAP_TOOLS_DIR}/${helper_name}" \
|
|
100
|
-
"${shared_tools_dir}/${helper_name}"; do
|
|
101
|
-
if [[ -n "${candidate}" && -f "${candidate}" ]]; then
|
|
102
|
-
printf '%s\n' "${candidate}"
|
|
103
|
-
return 0
|
|
104
|
-
fi
|
|
105
|
-
done
|
|
106
|
-
|
|
107
|
-
echo "unable to locate ${helper_name} for reconcile bootstrap" >&2
|
|
108
|
-
return 1
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
FLOW_CONFIG_LIB_PATH="$(resolve_reconcile_helper_path "flow-config-lib.sh")"
|
|
112
|
-
# shellcheck source=/dev/null
|
|
113
|
-
source "${FLOW_CONFIG_LIB_PATH}"
|
|
114
43
|
verification_guard_script="${shared_tools_dir}/branch-verification-guard.sh"
|
|
115
44
|
session=""
|
|
116
45
|
repo_slug=""
|
|
@@ -257,6 +186,40 @@ if [[ "$status" == "RUNNING" && "$pr_state" != "MERGED" && "$pr_state" != "CLOSE
|
|
|
257
186
|
exit 0
|
|
258
187
|
fi
|
|
259
188
|
|
|
189
|
+
cleanup_output_value() {
|
|
190
|
+
local cleanup_output="${1:-}"
|
|
191
|
+
local key="${2:?key required}"
|
|
192
|
+
awk -F= -v target_key="${key}" '$1 == target_key { print substr($0, index($0, "=") + 1); exit }' <<<"${cleanup_output}"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
warn_cleanup_pr_session() {
|
|
196
|
+
local cleanup_output="${1:-}"
|
|
197
|
+
local cleanup_exit="${2:-0}"
|
|
198
|
+
local cleanup_status=""
|
|
199
|
+
local cleanup_mode=""
|
|
200
|
+
local cleanup_error=""
|
|
201
|
+
|
|
202
|
+
cleanup_status="$(cleanup_output_value "${cleanup_output}" "CLEANUP_STATUS")"
|
|
203
|
+
if [[ -z "${cleanup_status}" ]]; then
|
|
204
|
+
cleanup_status="${cleanup_exit}"
|
|
205
|
+
fi
|
|
206
|
+
[[ "${cleanup_status}" != "0" ]] || return 0
|
|
207
|
+
|
|
208
|
+
cleanup_mode="$(cleanup_output_value "${cleanup_output}" "CLEANUP_MODE")"
|
|
209
|
+
cleanup_error="$(cleanup_output_value "${cleanup_output}" "CLEANUP_ERROR")"
|
|
210
|
+
printf '[%s] pr cleanup warning session=%s status=%s mode=%s\n' \
|
|
211
|
+
"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
|
212
|
+
"${session}" \
|
|
213
|
+
"${cleanup_status}" \
|
|
214
|
+
"${cleanup_mode:-unknown}" >&2
|
|
215
|
+
if [[ -n "${cleanup_error}" ]]; then
|
|
216
|
+
printf '[%s] pr cleanup detail session=%s %s\n' \
|
|
217
|
+
"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
|
218
|
+
"${session}" \
|
|
219
|
+
"${cleanup_error}" >&2
|
|
220
|
+
fi
|
|
221
|
+
}
|
|
222
|
+
|
|
260
223
|
review_pass_action_from_result_action() {
|
|
261
224
|
case "${1:-}" in
|
|
262
225
|
host-advance-double-check-2)
|
|
@@ -684,15 +647,6 @@ attempt_blocked_pr_host_verification_recovery() {
|
|
|
684
647
|
return 0
|
|
685
648
|
}
|
|
686
649
|
|
|
687
|
-
require_transition() {
|
|
688
|
-
local step="${1:?step required}"
|
|
689
|
-
shift
|
|
690
|
-
if ! "$@"; then
|
|
691
|
-
echo "reconcile transition failed: ${step}" >&2
|
|
692
|
-
exit 1
|
|
693
|
-
fi
|
|
694
|
-
}
|
|
695
|
-
|
|
696
650
|
close_linked_issue_if_open() {
|
|
697
651
|
local issue_id="${1:-}"
|
|
698
652
|
[[ -n "$issue_id" ]] || return 0
|
|
@@ -957,13 +911,24 @@ approve_and_merge() {
|
|
|
957
911
|
}
|
|
958
912
|
|
|
959
913
|
cleanup_pr_session() {
|
|
960
|
-
"
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
914
|
+
local cleanup_output=""
|
|
915
|
+
local cleanup_exit="0"
|
|
916
|
+
|
|
917
|
+
if cleanup_output="$(
|
|
918
|
+
"${shared_tools_dir}/agent-project-cleanup-session" \
|
|
919
|
+
--repo-root "$repo_root" \
|
|
920
|
+
--runs-root "$runs_root" \
|
|
921
|
+
--history-root "$history_root" \
|
|
922
|
+
--session "$session" \
|
|
923
|
+
--worktree "$pr_worktree" \
|
|
924
|
+
--mode pr 2>&1
|
|
925
|
+
)"; then
|
|
926
|
+
cleanup_exit="0"
|
|
927
|
+
else
|
|
928
|
+
cleanup_exit="$?"
|
|
929
|
+
fi
|
|
930
|
+
|
|
931
|
+
warn_cleanup_pr_session "${cleanup_output}" "${cleanup_exit}"
|
|
967
932
|
}
|
|
968
933
|
|
|
969
934
|
notify_pr_reconciled() {
|
|
@@ -364,6 +364,16 @@ EOF
|
|
|
364
364
|
done
|
|
365
365
|
fi
|
|
366
366
|
|
|
367
|
+
# Always collect result.env from sandbox to artifact_dir
|
|
368
|
+
collect_copy_snippet+=$(
|
|
369
|
+
cat <<EOF
|
|
370
|
+
if [[ -f ${sandbox_run_dir_q}/result.env ]]; then
|
|
371
|
+
cp ${sandbox_run_dir_q}/result.env ${artifact_dir_q}/result.env
|
|
372
|
+
fi
|
|
373
|
+
EOF
|
|
374
|
+
)
|
|
375
|
+
collect_copy_snippet+=$'\n'
|
|
376
|
+
|
|
367
377
|
reconcile_snippet=""
|
|
368
378
|
if [[ -n "$reconcile_command" ]]; then
|
|
369
379
|
printf -v delayed_reconcile_q '%q' "export ACP_EXPECTED_RUN_STARTED_AT=${started_at_q}; export F_LOSNING_EXPECTED_RUN_STARTED_AT=${started_at_q}; while tmux has-session -t ${session_q} 2>/dev/null; do sleep 1; done; sleep 2; $reconcile_command"
|
|
@@ -774,12 +784,12 @@ fi
|
|
|
774
784
|
|
|
775
785
|
${collect_copy_snippet}
|
|
776
786
|
if [[ "\${status}" -eq 0 ]]; then
|
|
777
|
-
write_state
|
|
787
|
+
write_state succeeded "\${status}" '' "\${attempt}" "\$((attempt - 1))"
|
|
778
788
|
else
|
|
779
789
|
write_state failed "\${status}" "\${failure_reason}" "\${attempt}" "\$((attempt - 1))"
|
|
780
790
|
fi
|
|
781
791
|
${reconcile_snippet}
|
|
782
|
-
printf '\
|
|
792
|
+
printf '\n__CODEX_EXIT__:%s\n' "\${status}" | tee -a "\${output_file}"
|
|
783
793
|
exit "\${status}"
|
|
784
794
|
EOF
|
|
785
795
|
|