agent-control-plane 0.3.0 → 0.6.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 +141 -28
- package/assets/workflow-catalog.json +1 -1
- package/bin/pr-risk.sh +22 -7
- package/bin/sync-pr-labels.sh +1 -1
- package/hooks/heartbeat-hooks.sh +125 -12
- package/hooks/issue-reconcile-hooks.sh +1 -1
- package/hooks/pr-reconcile-hooks.sh +1 -1
- package/npm/bin/agent-control-plane.js +257 -59
- package/package.json +39 -32
- package/tools/bin/debug-session.sh +106 -0
- package/tools/bin/flow-config-lib.sh +1203 -60
- package/tools/bin/flow-runtime-doctor-linux.sh +136 -0
- package/tools/bin/flow-runtime-doctor.sh +5 -1
- package/tools/bin/flow-shell-lib.sh +32 -0
- package/tools/bin/github-core-rate-limit-state.sh +77 -0
- package/tools/bin/github-write-outbox.sh +470 -0
- package/tools/bin/heartbeat-loop-scheduling-lib.sh +7 -7
- package/tools/bin/heartbeat-safe-auto.sh +42 -0
- package/tools/bin/install-project-launchd.sh +17 -2
- package/tools/bin/install-project-systemd.sh +255 -0
- package/tools/bin/project-init.sh +21 -1
- package/tools/bin/project-launchd-bootstrap.sh +5 -1
- package/tools/bin/project-runtimectl.sh +91 -2
- package/tools/bin/project-systemd-bootstrap.sh +74 -0
- package/tools/bin/scaffold-profile.sh +61 -3
- package/tools/bin/uninstall-project-systemd.sh +87 -0
- package/tools/dashboard/app.js +228 -6
- package/tools/dashboard/dashboard_snapshot.py +55 -0
- package/tools/dashboard/issue_queue_state.py +101 -0
- package/tools/dashboard/server.py +123 -1
- package/tools/dashboard/styles.css +526 -455
- package/tools/templates/pr-fix-template.md +3 -1
- package/tools/templates/pr-merge-repair-template.md +2 -1
- package/references/architecture.md +0 -217
- package/references/commands.md +0 -128
- package/references/control-plane-map.md +0 -124
- package/references/docs-map.md +0 -73
- package/references/release-checklist.md +0 -65
- package/references/repo-map.md +0 -36
- package/tools/bin/agent-cleanup-worktree +0 -247
- package/tools/bin/agent-github-update-labels +0 -71
- package/tools/bin/agent-init-worktree +0 -216
- package/tools/bin/agent-project-archive-run +0 -52
- package/tools/bin/agent-project-capture-worker +0 -46
- package/tools/bin/agent-project-catch-up-issue-pr-links +0 -118
- package/tools/bin/agent-project-catch-up-merged-prs +0 -194
- package/tools/bin/agent-project-catch-up-scheduled-issue-retries +0 -123
- package/tools/bin/agent-project-cleanup-session +0 -513
- package/tools/bin/agent-project-detached-launch +0 -127
- package/tools/bin/agent-project-heartbeat-loop +0 -1029
- package/tools/bin/agent-project-open-issue-worktree +0 -89
- package/tools/bin/agent-project-open-pr-worktree +0 -80
- package/tools/bin/agent-project-publish-issue-pr +0 -465
- package/tools/bin/agent-project-reconcile-issue-session +0 -1398
- package/tools/bin/agent-project-reconcile-pr-session +0 -1230
- package/tools/bin/agent-project-retry-state +0 -147
- package/tools/bin/agent-project-run-claude-session +0 -805
- package/tools/bin/agent-project-run-codex-resilient +0 -955
- package/tools/bin/agent-project-run-codex-session +0 -435
- package/tools/bin/agent-project-run-kilo-session +0 -369
- package/tools/bin/agent-project-run-ollama-session +0 -658
- package/tools/bin/agent-project-run-openclaw-session +0 -1309
- package/tools/bin/agent-project-run-opencode-session +0 -377
- package/tools/bin/agent-project-run-pi-session +0 -479
- package/tools/bin/agent-project-sync-anchor-repo +0 -139
- package/tools/bin/agent-project-worker-status +0 -188
- package/tools/bin/branch-verification-guard.sh +0 -364
- package/tools/bin/capture-worker.sh +0 -18
- package/tools/bin/cleanup-worktree.sh +0 -52
- package/tools/bin/codex-quota +0 -31
- package/tools/bin/create-follow-up-issue.sh +0 -114
- package/tools/bin/dashboard-launchd-bootstrap.sh +0 -50
- package/tools/bin/issue-publish-localization-guard.sh +0 -142
- package/tools/bin/issue-publish-scope-guard.sh +0 -242
- package/tools/bin/issue-requires-local-workspace-install.sh +0 -31
- package/tools/bin/issue-resource-class.sh +0 -12
- package/tools/bin/kick-scheduler.sh +0 -75
- package/tools/bin/label-follow-up-issues.sh +0 -14
- package/tools/bin/new-pr-worktree.sh +0 -50
- package/tools/bin/new-worktree.sh +0 -49
- package/tools/bin/pr-risk.sh +0 -12
- package/tools/bin/prepare-worktree.sh +0 -142
- package/tools/bin/provider-cooldown-state.sh +0 -204
- package/tools/bin/publish-issue-worker.sh +0 -31
- package/tools/bin/reconcile-bootstrap-lib.sh +0 -113
- package/tools/bin/reconcile-issue-worker.sh +0 -34
- package/tools/bin/reconcile-pr-worker.sh +0 -34
- package/tools/bin/record-verification.sh +0 -71
- package/tools/bin/render-flow-config.sh +0 -98
- package/tools/bin/resident-issue-controller-lib.sh +0 -448
- package/tools/bin/resident-issue-queue-status.py +0 -35
- package/tools/bin/retry-state.sh +0 -31
- package/tools/bin/reuse-issue-worktree.sh +0 -121
- package/tools/bin/run-codex-bypass.sh +0 -3
- package/tools/bin/run-codex-safe.sh +0 -3
- package/tools/bin/run-codex-task.sh +0 -280
- package/tools/bin/serve-dashboard.sh +0 -5
- package/tools/bin/split-retained-slice.sh +0 -124
- package/tools/bin/start-issue-worker.sh +0 -943
- package/tools/bin/start-pr-fix-worker.sh +0 -491
- package/tools/bin/start-pr-merge-repair-worker.sh +0 -8
- package/tools/bin/start-pr-review-worker.sh +0 -261
- package/tools/bin/start-resident-issue-loop.sh +0 -499
- package/tools/bin/update-github-labels.sh +0 -14
- package/tools/bin/worker-status.sh +0 -19
- package/tools/bin/workflow-catalog.sh +0 -77
|
@@ -1,479 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
usage() {
|
|
5
|
-
cat <<'EOF'
|
|
6
|
-
Usage:
|
|
7
|
-
agent-project-run-pi-session --mode safe|bypass --session <id> --worktree <path> --prompt-file <path> --runs-root <path> --adapter-id <id> --task-kind <kind> --task-id <id> [options]
|
|
8
|
-
|
|
9
|
-
Launch a Pi coding agent worker session inside tmux for a project adapter and
|
|
10
|
-
persist the standard run artifacts.
|
|
11
|
-
|
|
12
|
-
Pi supports any OpenRouter model (or other providers) via --model provider/id.
|
|
13
|
-
|
|
14
|
-
Options:
|
|
15
|
-
--env-prefix <prefix> Export prefixed runtime/context env vars inside the worker
|
|
16
|
-
--context <KEY=VALUE> Extra metadata written to run.env and exported to the worker
|
|
17
|
-
--collect-file <name> Copy sandbox artifact file into the host run dir after execution
|
|
18
|
-
--reconcile-command <cmd> Host-side command queued after the worker exits
|
|
19
|
-
--sandbox-subdir <name> Subdir under the worktree for worker artifacts (default: .pi-artifacts)
|
|
20
|
-
--pi-model <id> Model ID for pi (e.g. openrouter/qwen/qwen3.6-plus:free)
|
|
21
|
-
--pi-thinking <level> Thinking level: off, minimal, low, medium, high, xhigh (default: low)
|
|
22
|
-
--pi-timeout-seconds <secs> Hard timeout in seconds (default: 900)
|
|
23
|
-
--pi-stall-seconds <secs> Abort if no output for this long, 0 disables (default: 300)
|
|
24
|
-
--help Show this help
|
|
25
|
-
EOF
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
mode=""
|
|
29
|
-
session=""
|
|
30
|
-
worktree=""
|
|
31
|
-
prompt_file=""
|
|
32
|
-
runs_root=""
|
|
33
|
-
adapter_id=""
|
|
34
|
-
task_kind=""
|
|
35
|
-
task_id=""
|
|
36
|
-
env_prefix=""
|
|
37
|
-
sandbox_subdir=".pi-artifacts"
|
|
38
|
-
reconcile_command=""
|
|
39
|
-
pi_model="${ACP_PI_MODEL:-${F_LOSNING_PI_MODEL:-openrouter/qwen/qwen3.6-plus:free}}"
|
|
40
|
-
pi_thinking="${ACP_PI_THINKING:-${F_LOSNING_PI_THINKING:-low}}"
|
|
41
|
-
pi_timeout_seconds="${ACP_PI_TIMEOUT_SECONDS:-${F_LOSNING_PI_TIMEOUT_SECONDS:-900}}"
|
|
42
|
-
pi_stall_seconds="${ACP_PI_STALL_SECONDS:-${F_LOSNING_PI_STALL_SECONDS:-300}}"
|
|
43
|
-
declare -a context_items=()
|
|
44
|
-
declare -a collect_files=()
|
|
45
|
-
|
|
46
|
-
while [[ $# -gt 0 ]]; do
|
|
47
|
-
case "$1" in
|
|
48
|
-
--mode) mode="${2:-}"; shift 2 ;;
|
|
49
|
-
--session) session="${2:-}"; shift 2 ;;
|
|
50
|
-
--worktree) worktree="${2:-}"; shift 2 ;;
|
|
51
|
-
--prompt-file) prompt_file="${2:-}"; shift 2 ;;
|
|
52
|
-
--runs-root) runs_root="${2:-}"; shift 2 ;;
|
|
53
|
-
--adapter-id) adapter_id="${2:-}"; shift 2 ;;
|
|
54
|
-
--task-kind) task_kind="${2:-}"; shift 2 ;;
|
|
55
|
-
--task-id) task_id="${2:-}"; shift 2 ;;
|
|
56
|
-
--env-prefix) env_prefix="${2:-}"; shift 2 ;;
|
|
57
|
-
--context) context_items+=("${2:-}"); shift 2 ;;
|
|
58
|
-
--collect-file) collect_files+=("${2:-}"); shift 2 ;;
|
|
59
|
-
--reconcile-command) reconcile_command="${2:-}"; shift 2 ;;
|
|
60
|
-
--sandbox-subdir) sandbox_subdir="${2:-}"; shift 2 ;;
|
|
61
|
-
--pi-model) pi_model="${2:-}"; shift 2 ;;
|
|
62
|
-
--pi-thinking) pi_thinking="${2:-}"; shift 2 ;;
|
|
63
|
-
--pi-timeout-seconds) pi_timeout_seconds="${2:-}"; shift 2 ;;
|
|
64
|
-
--pi-stall-seconds) pi_stall_seconds="${2:-}"; shift 2 ;;
|
|
65
|
-
--help|-h) usage; exit 0 ;;
|
|
66
|
-
*) echo "Unknown argument: $1" >&2; usage >&2; exit 1 ;;
|
|
67
|
-
esac
|
|
68
|
-
done
|
|
69
|
-
|
|
70
|
-
if [[ -z "$mode" || -z "$session" || -z "$worktree" || -z "$prompt_file" || -z "$runs_root" || -z "$adapter_id" || -z "$task_kind" || -z "$task_id" ]]; then
|
|
71
|
-
usage >&2
|
|
72
|
-
exit 1
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
case "$mode" in
|
|
76
|
-
safe|bypass) ;;
|
|
77
|
-
*) echo "--mode must be safe or bypass" >&2; exit 1 ;;
|
|
78
|
-
esac
|
|
79
|
-
|
|
80
|
-
case "$pi_thinking" in
|
|
81
|
-
off|minimal|low|medium|high|xhigh) ;;
|
|
82
|
-
*) echo "--pi-thinking must be one of: off, minimal, low, medium, high, xhigh" >&2; exit 1 ;;
|
|
83
|
-
esac
|
|
84
|
-
|
|
85
|
-
case "$pi_timeout_seconds" in
|
|
86
|
-
''|*[!0-9]*|0) echo "--pi-timeout-seconds must be a positive integer" >&2; exit 1 ;;
|
|
87
|
-
esac
|
|
88
|
-
|
|
89
|
-
case "$pi_stall_seconds" in
|
|
90
|
-
''|*[!0-9]*) echo "--pi-stall-seconds must be numeric" >&2; exit 1 ;;
|
|
91
|
-
esac
|
|
92
|
-
|
|
93
|
-
resolve_pi_bin() {
|
|
94
|
-
local configured_bin="${PI_BIN:-${ACP_PI_BIN:-${F_LOSNING_PI_BIN:-}}}"
|
|
95
|
-
if [[ -n "${configured_bin}" && -x "${configured_bin}" ]]; then
|
|
96
|
-
printf '%s\n' "${configured_bin}"
|
|
97
|
-
return 0
|
|
98
|
-
fi
|
|
99
|
-
if command -v pi >/dev/null 2>&1; then
|
|
100
|
-
command -v pi
|
|
101
|
-
return 0
|
|
102
|
-
fi
|
|
103
|
-
local -a fallback_paths=(
|
|
104
|
-
"/opt/homebrew/bin/pi"
|
|
105
|
-
"/usr/local/bin/pi"
|
|
106
|
-
"${HOME}/.local/bin/pi"
|
|
107
|
-
"${HOME}/.nvm/versions/node/$(node --version 2>/dev/null || true)/bin/pi"
|
|
108
|
-
)
|
|
109
|
-
local p
|
|
110
|
-
for p in "${fallback_paths[@]}"; do
|
|
111
|
-
if [[ -x "${p}" ]]; then
|
|
112
|
-
printf '%s\n' "${p}"
|
|
113
|
-
return 0
|
|
114
|
-
fi
|
|
115
|
-
done
|
|
116
|
-
return 1
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
pi_bin="$(resolve_pi_bin || true)"
|
|
120
|
-
if [[ -z "${pi_bin}" || ! -x "${pi_bin}" ]]; then
|
|
121
|
-
echo "unable to resolve a runnable pi binary — install with: npm install -g @mariozechner/pi-coding-agent" >&2
|
|
122
|
-
exit 1
|
|
123
|
-
fi
|
|
124
|
-
|
|
125
|
-
artifact_dir="${runs_root}/${session}"
|
|
126
|
-
output_file="${artifact_dir}/${session}.log"
|
|
127
|
-
inner_script="${artifact_dir}/${session}.sh"
|
|
128
|
-
meta_file="${artifact_dir}/run.env"
|
|
129
|
-
result_file="${artifact_dir}/result.env"
|
|
130
|
-
runner_state_file="${artifact_dir}/runner.env"
|
|
131
|
-
sandbox_run_dir="${worktree%/}/${sandbox_subdir}/${session}"
|
|
132
|
-
retained_repo_root="${ACP_RETAINED_REPO_ROOT:-${F_LOSNING_RETAINED_REPO_ROOT:-}}"
|
|
133
|
-
started_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
134
|
-
|
|
135
|
-
mkdir -p "$artifact_dir"
|
|
136
|
-
mkdir -p "$sandbox_run_dir"
|
|
137
|
-
|
|
138
|
-
if tmux has-session -t "$session" 2>/dev/null; then
|
|
139
|
-
echo "tmux session already exists: $session" >&2
|
|
140
|
-
exit 1
|
|
141
|
-
fi
|
|
142
|
-
|
|
143
|
-
branch_name="$(git -C "$worktree" branch --show-current 2>/dev/null || true)"
|
|
144
|
-
|
|
145
|
-
printf -v session_q '%q' "$session"
|
|
146
|
-
printf -v task_kind_q '%q' "$task_kind"
|
|
147
|
-
printf -v task_id_q '%q' "$task_id"
|
|
148
|
-
printf -v mode_q '%q' "$mode"
|
|
149
|
-
printf -v worktree_q '%q' "$worktree"
|
|
150
|
-
printf -v prompt_q '%q' "$prompt_file"
|
|
151
|
-
printf -v output_q '%q' "$output_file"
|
|
152
|
-
printf -v artifact_dir_q '%q' "$artifact_dir"
|
|
153
|
-
printf -v script_q '%q' "$inner_script"
|
|
154
|
-
printf -v result_q '%q' "$result_file"
|
|
155
|
-
printf -v meta_file_q '%q' "$meta_file"
|
|
156
|
-
printf -v runner_state_q '%q' "$runner_state_file"
|
|
157
|
-
printf -v branch_q '%q' "$branch_name"
|
|
158
|
-
printf -v sandbox_run_dir_q '%q' "$sandbox_run_dir"
|
|
159
|
-
printf -v retained_repo_root_q '%q' "$retained_repo_root"
|
|
160
|
-
printf -v adapter_id_q '%q' "$adapter_id"
|
|
161
|
-
printf -v started_at_q '%q' "$started_at"
|
|
162
|
-
printf -v pi_bin_q '%q' "$pi_bin"
|
|
163
|
-
printf -v pi_model_q '%q' "$pi_model"
|
|
164
|
-
printf -v pi_thinking_q '%q' "$pi_thinking"
|
|
165
|
-
printf -v pi_timeout_q '%q' "$pi_timeout_seconds"
|
|
166
|
-
printf -v pi_stall_q '%q' "$pi_stall_seconds"
|
|
167
|
-
|
|
168
|
-
{
|
|
169
|
-
printf 'TASK_KIND=%s\n' "$task_kind_q"
|
|
170
|
-
printf 'TASK_ID=%s\n' "$task_id_q"
|
|
171
|
-
printf 'SESSION=%s\n' "$session_q"
|
|
172
|
-
printf 'MODE=%s\n' "$mode_q"
|
|
173
|
-
printf 'WORKTREE=%s\n' "$worktree_q"
|
|
174
|
-
printf 'PROMPT_FILE=%s\n' "$prompt_q"
|
|
175
|
-
printf 'OUTPUT_FILE=%s\n' "$output_q"
|
|
176
|
-
printf 'SCRIPT=%s\n' "$script_q"
|
|
177
|
-
printf 'BRANCH=%s\n' "$branch_q"
|
|
178
|
-
printf 'RESULT_FILE=%s\n' "$result_q"
|
|
179
|
-
printf 'RUNNER_STATE_FILE=%s\n' "$runner_state_q"
|
|
180
|
-
printf 'SANDBOX_RUN_DIR=%s\n' "$sandbox_run_dir_q"
|
|
181
|
-
printf 'ADAPTER_ID=%s\n' "$adapter_id_q"
|
|
182
|
-
printf 'STARTED_AT=%s\n' "$started_at_q"
|
|
183
|
-
printf 'PI_BIN=%s\n' "$pi_bin_q"
|
|
184
|
-
printf 'PI_MODEL=%s\n' "$pi_model_q"
|
|
185
|
-
printf 'PI_THINKING=%s\n' "$pi_thinking_q"
|
|
186
|
-
printf 'PI_TIMEOUT_SECONDS=%s\n' "$pi_timeout_q"
|
|
187
|
-
printf 'PI_STALL_SECONDS=%s\n' "$pi_stall_q"
|
|
188
|
-
} >"$meta_file"
|
|
189
|
-
|
|
190
|
-
context_exports=""
|
|
191
|
-
if ((${#context_items[@]} > 0)); then
|
|
192
|
-
for item in "${context_items[@]}"; do
|
|
193
|
-
if [[ "$item" != *=* ]]; then
|
|
194
|
-
echo "--context must use KEY=VALUE syntax: $item" >&2
|
|
195
|
-
exit 1
|
|
196
|
-
fi
|
|
197
|
-
key="${item%%=*}"
|
|
198
|
-
value="${item#*=}"
|
|
199
|
-
if [[ ! "$key" =~ ^[A-Z0-9_]+$ ]]; then
|
|
200
|
-
echo "Invalid context key: $key" >&2
|
|
201
|
-
exit 1
|
|
202
|
-
fi
|
|
203
|
-
printf -v value_q '%q' "$value"
|
|
204
|
-
printf '%s=%s\n' "$key" "$value_q" >>"$meta_file"
|
|
205
|
-
if [[ -n "$env_prefix" ]]; then
|
|
206
|
-
context_exports+="export ${env_prefix}${key}=${value_q}"$'\n'
|
|
207
|
-
fi
|
|
208
|
-
context_exports+="export ACP_${key}=${value_q}"$'\n'
|
|
209
|
-
if [[ "$env_prefix" != "F_LOSNING_" ]]; then
|
|
210
|
-
context_exports+="export F_LOSNING_${key}=${value_q}"$'\n'
|
|
211
|
-
fi
|
|
212
|
-
done
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
runtime_exports=$(
|
|
216
|
-
cat <<EOF
|
|
217
|
-
export AGENT_PROJECT_SESSION=${session_q}
|
|
218
|
-
export AGENT_PROJECT_RUN_DIR=${sandbox_run_dir_q}
|
|
219
|
-
export AGENT_PROJECT_HOST_RUN_DIR=${artifact_dir_q}
|
|
220
|
-
export AGENT_PROJECT_RESULT_FILE=${sandbox_run_dir_q}/result.env
|
|
221
|
-
export AGENT_PROJECT_PI_BIN=${pi_bin_q}
|
|
222
|
-
export AGENT_PROJECT_RETAINED_REPO_ROOT=${retained_repo_root_q}
|
|
223
|
-
export ACP_SESSION=${session_q}
|
|
224
|
-
export ACP_RUN_DIR=${sandbox_run_dir_q}
|
|
225
|
-
export ACP_HOST_RUN_DIR=${artifact_dir_q}
|
|
226
|
-
export ACP_RESULT_FILE=${sandbox_run_dir_q}/result.env
|
|
227
|
-
export ACP_PI_BIN=${pi_bin_q}
|
|
228
|
-
export ACP_PI_MODEL=${pi_model_q}
|
|
229
|
-
export ACP_PI_THINKING=${pi_thinking_q}
|
|
230
|
-
export ACP_PI_TIMEOUT_SECONDS=${pi_timeout_q}
|
|
231
|
-
export ACP_RETAINED_REPO_ROOT=${retained_repo_root_q}
|
|
232
|
-
export F_LOSNING_SESSION=${session_q}
|
|
233
|
-
export F_LOSNING_RUN_DIR=${sandbox_run_dir_q}
|
|
234
|
-
export F_LOSNING_HOST_RUN_DIR=${artifact_dir_q}
|
|
235
|
-
export F_LOSNING_RESULT_FILE=${sandbox_run_dir_q}/result.env
|
|
236
|
-
export F_LOSNING_PI_BIN=${pi_bin_q}
|
|
237
|
-
export F_LOSNING_PI_MODEL=${pi_model_q}
|
|
238
|
-
export F_LOSNING_PI_THINKING=${pi_thinking_q}
|
|
239
|
-
export F_LOSNING_PI_TIMEOUT_SECONDS=${pi_timeout_q}
|
|
240
|
-
export F_LOSNING_RETAINED_REPO_ROOT=${retained_repo_root_q}
|
|
241
|
-
EOF
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
if [[ -n "$env_prefix" ]]; then
|
|
245
|
-
runtime_exports+=$'\n'
|
|
246
|
-
runtime_exports+=$(cat <<EOF
|
|
247
|
-
export ${env_prefix}SESSION=${session_q}
|
|
248
|
-
export ${env_prefix}RUN_DIR=${sandbox_run_dir_q}
|
|
249
|
-
export ${env_prefix}HOST_RUN_DIR=${artifact_dir_q}
|
|
250
|
-
export ${env_prefix}RESULT_FILE=${sandbox_run_dir_q}/result.env
|
|
251
|
-
export ${env_prefix}PI_BIN=${pi_bin_q}
|
|
252
|
-
export ${env_prefix}PI_MODEL=${pi_model_q}
|
|
253
|
-
export ${env_prefix}PI_THINKING=${pi_thinking_q}
|
|
254
|
-
export ${env_prefix}PI_TIMEOUT_SECONDS=${pi_timeout_q}
|
|
255
|
-
EOF
|
|
256
|
-
)
|
|
257
|
-
fi
|
|
258
|
-
|
|
259
|
-
collect_copy_snippet=""
|
|
260
|
-
if ((${#collect_files[@]} > 0)); then
|
|
261
|
-
for artifact_name in "${collect_files[@]}"; do
|
|
262
|
-
[[ -z "$artifact_name" ]] && continue
|
|
263
|
-
printf -v artifact_q '%q' "$artifact_name"
|
|
264
|
-
collect_copy_snippet+=$(
|
|
265
|
-
cat <<EOF
|
|
266
|
-
if [[ -f ${sandbox_run_dir_q}/${artifact_q} ]]; then
|
|
267
|
-
cp ${sandbox_run_dir_q}/${artifact_q} ${artifact_dir_q}/${artifact_q}
|
|
268
|
-
fi
|
|
269
|
-
EOF
|
|
270
|
-
)
|
|
271
|
-
collect_copy_snippet+=$'\n'
|
|
272
|
-
done
|
|
273
|
-
fi
|
|
274
|
-
|
|
275
|
-
# Always collect result.env from sandbox to artifact_dir
|
|
276
|
-
collect_copy_snippet+=$(
|
|
277
|
-
cat <<EOF
|
|
278
|
-
if [[ -f ${sandbox_run_dir_q}/result.env ]]; then
|
|
279
|
-
cp ${sandbox_run_dir_q}/result.env ${result_q}
|
|
280
|
-
fi
|
|
281
|
-
EOF
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
reconcile_snippet=""
|
|
285
|
-
if [[ -n "$reconcile_command" ]]; then
|
|
286
|
-
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"
|
|
287
|
-
reconcile_snippet="nohup bash -lc ${delayed_reconcile_q} >> ${output_q} 2>&1 </dev/null &"
|
|
288
|
-
fi
|
|
289
|
-
|
|
290
|
-
cat >"$inner_script" <<EOF
|
|
291
|
-
#!/usr/bin/env bash
|
|
292
|
-
set -euo pipefail
|
|
293
|
-
${runtime_exports}
|
|
294
|
-
${context_exports}cd ${worktree_q}
|
|
295
|
-
|
|
296
|
-
runner_state_file=${runner_state_q}
|
|
297
|
-
output_file=${output_q}
|
|
298
|
-
sandbox_run_dir=${sandbox_run_dir_q}
|
|
299
|
-
artifact_dir=${artifact_dir_q}
|
|
300
|
-
result_file_path=${sandbox_run_dir_q}/result.env
|
|
301
|
-
host_result_file=${result_q}
|
|
302
|
-
pi_bin=${pi_bin_q}
|
|
303
|
-
pi_model=${pi_model_q}
|
|
304
|
-
pi_thinking=${pi_thinking_q}
|
|
305
|
-
pi_timeout_seconds=${pi_timeout_q}
|
|
306
|
-
pi_stall_seconds=${pi_stall_q}
|
|
307
|
-
prompt_file=${prompt_q}
|
|
308
|
-
worktree=${worktree_q}
|
|
309
|
-
|
|
310
|
-
write_state() {
|
|
311
|
-
local runner_state="\${1:?runner state required}"
|
|
312
|
-
local last_exit_code="\${2:-}"
|
|
313
|
-
local failure_reason="\${3:-}"
|
|
314
|
-
local updated_at tmp_file
|
|
315
|
-
|
|
316
|
-
updated_at="\$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
317
|
-
tmp_file="\${runner_state_file}.tmp.\$\$"
|
|
318
|
-
{
|
|
319
|
-
printf 'RUNNER_STATE=%q\n' "\${runner_state}"
|
|
320
|
-
printf 'ATTEMPT=1\n'
|
|
321
|
-
printf 'RESUME_COUNT=0\n'
|
|
322
|
-
printf 'LAST_EXIT_CODE=%q\n' "\${last_exit_code}"
|
|
323
|
-
printf 'LAST_FAILURE_REASON=%q\n' "\${failure_reason}"
|
|
324
|
-
printf 'LAST_TRIGGER_REASON=%q\n' ''
|
|
325
|
-
printf 'AUTH_WAIT_STARTED_AT=%q\n' ''
|
|
326
|
-
printf 'UPDATED_AT=%q\n' "\${updated_at}"
|
|
327
|
-
} >"\${tmp_file}"
|
|
328
|
-
mv "\${tmp_file}" "\${runner_state_file}"
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
write_result_fallback() {
|
|
332
|
-
local detail="\${1:-missing-result-contract}"
|
|
333
|
-
local tmp_file
|
|
334
|
-
tmp_file="\${result_file_path}.tmp.\$\$"
|
|
335
|
-
{
|
|
336
|
-
printf 'OUTCOME=blocked\n'
|
|
337
|
-
printf 'ACTION=host-comment-blocker\n'
|
|
338
|
-
printf 'DETAIL=%s\n' "\${detail}"
|
|
339
|
-
} >"\${tmp_file}"
|
|
340
|
-
mv "\${tmp_file}" "\${result_file_path}"
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
record_final_git_state() {
|
|
344
|
-
local final_head final_branch tmp_file
|
|
345
|
-
final_head="\$(git -C ${worktree_q} rev-parse HEAD 2>/dev/null || true)"
|
|
346
|
-
final_branch="\$(git -C ${worktree_q} branch --show-current 2>/dev/null || true)"
|
|
347
|
-
tmp_file=${meta_file_q}.tmp.final.$$
|
|
348
|
-
grep -vE '^(FINAL_HEAD|FINAL_BRANCH)=' ${meta_file_q} >"\${tmp_file}" 2>/dev/null || true
|
|
349
|
-
{
|
|
350
|
-
printf 'FINAL_HEAD=%q\n' "\${final_head}"
|
|
351
|
-
printf 'FINAL_BRANCH=%q\n' "\${final_branch}"
|
|
352
|
-
} >>"\${tmp_file}"
|
|
353
|
-
mv "\${tmp_file}" ${meta_file_q}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
${reconcile_snippet}
|
|
357
|
-
|
|
358
|
-
write_state running
|
|
359
|
-
|
|
360
|
-
mkdir -p "\${sandbox_run_dir}"
|
|
361
|
-
|
|
362
|
-
# Run pi in print mode (non-interactive, single-shot)
|
|
363
|
-
# --no-session: ephemeral, don't persist session state
|
|
364
|
-
# --thinking: reasoning depth
|
|
365
|
-
# @prompt_file: pass prompt as file reference
|
|
366
|
-
pi_exit_code=0
|
|
367
|
-
# macOS does not ship 'timeout'; prefer it when available, else use background watchdog
|
|
368
|
-
if command -v timeout >/dev/null 2>&1; then
|
|
369
|
-
timeout "\${pi_timeout_seconds}" \\
|
|
370
|
-
"\${pi_bin}" --print --no-session \\
|
|
371
|
-
--model "\${pi_model}" \\
|
|
372
|
-
--thinking "\${pi_thinking}" \\
|
|
373
|
-
"@\${prompt_file}" \\
|
|
374
|
-
2>&1 | tee -a "\${output_file}" || pi_exit_code=\$?
|
|
375
|
-
elif command -v gtimeout >/dev/null 2>&1; then
|
|
376
|
-
gtimeout "\${pi_timeout_seconds}" \\
|
|
377
|
-
"\${pi_bin}" --print --no-session \\
|
|
378
|
-
--model "\${pi_model}" \\
|
|
379
|
-
--thinking "\${pi_thinking}" \\
|
|
380
|
-
"@\${prompt_file}" \\
|
|
381
|
-
2>&1 | tee -a "\${output_file}" || pi_exit_code=\$?
|
|
382
|
-
else
|
|
383
|
-
"\${pi_bin}" --print --no-session \\
|
|
384
|
-
--model "\${pi_model}" \\
|
|
385
|
-
--thinking "\${pi_thinking}" \\
|
|
386
|
-
"@\${prompt_file}" \\
|
|
387
|
-
2>&1 >> "\${output_file}" &
|
|
388
|
-
_pi_bgpid=\$!
|
|
389
|
-
# Hard timeout watchdog
|
|
390
|
-
( sleep "\${pi_timeout_seconds}" && kill "\${_pi_bgpid}" 2>/dev/null \\
|
|
391
|
-
&& printf '[pi] timed out after %s seconds\n' "\${pi_timeout_seconds}" >> "\${output_file}" ) &
|
|
392
|
-
_pi_timeout_wd=\$!
|
|
393
|
-
# Stall watchdog: kill if output file stops growing for pi_stall_seconds
|
|
394
|
-
if [[ "\${pi_stall_seconds}" -gt 0 ]]; then
|
|
395
|
-
(
|
|
396
|
-
_prev_size=-1
|
|
397
|
-
while kill -0 "\${_pi_bgpid}" 2>/dev/null; do
|
|
398
|
-
_cur_size="\$(wc -c < "\${output_file}" 2>/dev/null || echo 0)"
|
|
399
|
-
if [[ "\${_cur_size}" -eq "\${_prev_size}" ]]; then
|
|
400
|
-
if [[ -z "\${_idle_since:-}" ]]; then _idle_since="\$(date +%s)"; fi
|
|
401
|
-
if (( \$(date +%s) - _idle_since >= pi_stall_seconds )); then
|
|
402
|
-
printf '[pi] no output for %s seconds — aborting stalled worker\n' "\${pi_stall_seconds}" >> "\${output_file}"
|
|
403
|
-
kill "\${_pi_bgpid}" 2>/dev/null
|
|
404
|
-
break
|
|
405
|
-
fi
|
|
406
|
-
else
|
|
407
|
-
_idle_since=""
|
|
408
|
-
_prev_size="\${_cur_size}"
|
|
409
|
-
fi
|
|
410
|
-
sleep 5
|
|
411
|
-
done
|
|
412
|
-
) &
|
|
413
|
-
_pi_stall_wd=\$!
|
|
414
|
-
fi
|
|
415
|
-
wait "\${_pi_bgpid}" || pi_exit_code=\$?
|
|
416
|
-
kill "\${_pi_timeout_wd}" 2>/dev/null || true
|
|
417
|
-
wait "\${_pi_timeout_wd}" 2>/dev/null || true
|
|
418
|
-
if [[ -n "\${_pi_stall_wd:-}" ]]; then
|
|
419
|
-
kill "\${_pi_stall_wd}" 2>/dev/null || true
|
|
420
|
-
wait "\${_pi_stall_wd}" 2>/dev/null || true
|
|
421
|
-
fi
|
|
422
|
-
if [[ "\${pi_exit_code}" -eq 143 ]]; then
|
|
423
|
-
pi_exit_code=124
|
|
424
|
-
fi
|
|
425
|
-
fi
|
|
426
|
-
|
|
427
|
-
if [[ "\${pi_exit_code}" -eq 0 ]]; then
|
|
428
|
-
# Pi runs in --print mode and cannot write result.env itself.
|
|
429
|
-
# If the result file is missing, write a blocked fallback so reconcile
|
|
430
|
-
# sees a valid contract instead of an invalid-result-contract failure.
|
|
431
|
-
if [[ ! -f "\${result_file_path}" ]]; then
|
|
432
|
-
write_result_fallback "missing-result-contract"
|
|
433
|
-
fi
|
|
434
|
-
write_state succeeded 0
|
|
435
|
-
else
|
|
436
|
-
failure_reason="pi-exit-\${pi_exit_code}"
|
|
437
|
-
if [[ "\${pi_exit_code}" -eq 124 ]]; then
|
|
438
|
-
failure_reason="timeout"
|
|
439
|
-
fi
|
|
440
|
-
if [[ ! -f "\${result_file_path}" ]]; then
|
|
441
|
-
write_result_fallback "\${failure_reason}"
|
|
442
|
-
fi
|
|
443
|
-
write_state failed "\${pi_exit_code}" "\${failure_reason}"
|
|
444
|
-
fi
|
|
445
|
-
|
|
446
|
-
record_final_git_state
|
|
447
|
-
|
|
448
|
-
${collect_copy_snippet}
|
|
449
|
-
printf '\n__CODEX_EXIT__:%s\n' "\${pi_exit_code}" | tee -a "\${output_file}"
|
|
450
|
-
exit "\${pi_exit_code}"
|
|
451
|
-
EOF
|
|
452
|
-
|
|
453
|
-
chmod +x "$inner_script"
|
|
454
|
-
|
|
455
|
-
# Write initial runner state
|
|
456
|
-
{
|
|
457
|
-
printf 'RUNNER_STATE=%q\n' "running"
|
|
458
|
-
printf 'ATTEMPT=1\n'
|
|
459
|
-
printf 'RESUME_COUNT=0\n'
|
|
460
|
-
printf "LAST_EXIT_CODE=''\n"
|
|
461
|
-
printf "LAST_FAILURE_REASON=''\n"
|
|
462
|
-
printf "LAST_TRIGGER_REASON=''\n"
|
|
463
|
-
printf "AUTH_WAIT_STARTED_AT=''\n"
|
|
464
|
-
printf 'UPDATED_AT=%q\n' "$started_at"
|
|
465
|
-
printf 'RUNNER_STATE_FILE=%q\n' "$runner_state_file"
|
|
466
|
-
} >"$runner_state_file"
|
|
467
|
-
|
|
468
|
-
# Append pi-specific metadata to run.env for dashboard/status visibility
|
|
469
|
-
{
|
|
470
|
-
printf 'PI_MODEL=%s\n' "$pi_model_q"
|
|
471
|
-
printf 'PI_THINKING=%s\n' "$pi_thinking_q"
|
|
472
|
-
printf 'PI_TIMEOUT_SECONDS=%s\n' "$pi_timeout_q"
|
|
473
|
-
printf 'PI_STALL_SECONDS=%s\n' "$pi_stall_q"
|
|
474
|
-
printf 'PI_BIN=%s\n' "$pi_bin_q"
|
|
475
|
-
} >>"$meta_file"
|
|
476
|
-
|
|
477
|
-
tmux new-session -d -s "$session" -x 220 -y 50 \
|
|
478
|
-
"bash -l $script_q >> $output_q 2>&1; tmux wait-for -S $session_q-done" \; \
|
|
479
|
-
wait-for "$session-done" || true
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
usage() {
|
|
5
|
-
cat <<'EOF'
|
|
6
|
-
Usage:
|
|
7
|
-
agent-project-sync-anchor-repo --canonical-root <path> --anchor-root <path> [--remote <name>] [--default-branch <name>]
|
|
8
|
-
|
|
9
|
-
Initialize or refresh a local agent-owned anchor repository that is safe to use
|
|
10
|
-
as the Git parent for per-task worktrees. The canonical checkout is only used to
|
|
11
|
-
discover the authoritative remote URL.
|
|
12
|
-
EOF
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
canonical_root=""
|
|
16
|
-
anchor_root=""
|
|
17
|
-
remote_name="origin"
|
|
18
|
-
default_branch="main"
|
|
19
|
-
dirty_state_stashed="no"
|
|
20
|
-
dirty_stash_message=""
|
|
21
|
-
|
|
22
|
-
while [[ $# -gt 0 ]]; do
|
|
23
|
-
case "$1" in
|
|
24
|
-
--canonical-root) canonical_root="${2:-}"; shift 2 ;;
|
|
25
|
-
--anchor-root) anchor_root="${2:-}"; shift 2 ;;
|
|
26
|
-
--remote) remote_name="${2:-}"; shift 2 ;;
|
|
27
|
-
--default-branch) default_branch="${2:-}"; shift 2 ;;
|
|
28
|
-
--help|-h) usage; exit 0 ;;
|
|
29
|
-
*) echo "Unknown argument: $1" >&2; usage >&2; exit 1 ;;
|
|
30
|
-
esac
|
|
31
|
-
done
|
|
32
|
-
|
|
33
|
-
if [[ -z "$canonical_root" || -z "$anchor_root" ]]; then
|
|
34
|
-
usage >&2
|
|
35
|
-
exit 1
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
if [[ ! -d "$canonical_root/.git" && ! -f "$canonical_root/.git" ]]; then
|
|
39
|
-
echo "[agent-sync-anchor] canonical root is not a Git checkout: $canonical_root" >&2
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
origin_url="$(git -C "$canonical_root" remote get-url "$remote_name" 2>/dev/null || true)"
|
|
44
|
-
if [[ -z "$origin_url" ]]; then
|
|
45
|
-
echo "[agent-sync-anchor] missing remote '$remote_name' in canonical repo: $canonical_root" >&2
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
anchor_parent="$(dirname "$anchor_root")"
|
|
50
|
-
lock_dir="${anchor_root}.sync.lock"
|
|
51
|
-
pid_file="${lock_dir}/pid"
|
|
52
|
-
|
|
53
|
-
mkdir -p "$anchor_parent"
|
|
54
|
-
|
|
55
|
-
acquire_lock() {
|
|
56
|
-
local attempts=0
|
|
57
|
-
while ! mkdir "$lock_dir" 2>/dev/null; do
|
|
58
|
-
if [[ -f "$pid_file" ]]; then
|
|
59
|
-
local existing_pid
|
|
60
|
-
existing_pid="$(cat "$pid_file" 2>/dev/null || true)"
|
|
61
|
-
if [[ -n "$existing_pid" ]] && ! kill -0 "$existing_pid" 2>/dev/null; then
|
|
62
|
-
rm -rf "$lock_dir"
|
|
63
|
-
continue
|
|
64
|
-
fi
|
|
65
|
-
else
|
|
66
|
-
rm -rf "$lock_dir"
|
|
67
|
-
continue
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
attempts=$((attempts + 1))
|
|
71
|
-
if (( attempts >= 60 )); then
|
|
72
|
-
echo "[agent-sync-anchor] timed out waiting for anchor lock: $lock_dir" >&2
|
|
73
|
-
exit 1
|
|
74
|
-
fi
|
|
75
|
-
sleep 1
|
|
76
|
-
done
|
|
77
|
-
printf '%s\n' "$$" >"$pid_file"
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
cleanup() {
|
|
81
|
-
rm -rf "$lock_dir"
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
trap cleanup EXIT
|
|
85
|
-
acquire_lock
|
|
86
|
-
|
|
87
|
-
if [[ -e "$anchor_root" && ! -d "$anchor_root/.git" && ! -f "$anchor_root/.git" ]]; then
|
|
88
|
-
echo "[agent-sync-anchor] anchor root exists but is not a Git checkout: $anchor_root" >&2
|
|
89
|
-
exit 1
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
if [[ ! -d "$anchor_root/.git" && ! -f "$anchor_root/.git" ]]; then
|
|
93
|
-
git clone \
|
|
94
|
-
--origin "$remote_name" \
|
|
95
|
-
--branch "$default_branch" \
|
|
96
|
-
--single-branch \
|
|
97
|
-
--no-tags \
|
|
98
|
-
"$origin_url" \
|
|
99
|
-
"$anchor_root" >/dev/null
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
git -C "$anchor_root" remote set-url "$remote_name" "$origin_url"
|
|
103
|
-
git -C "$anchor_root" config "remote.${remote_name}.prune" true
|
|
104
|
-
git -C "$anchor_root" fetch "$remote_name" --prune >/dev/null
|
|
105
|
-
git -C "$anchor_root" worktree prune >/dev/null 2>&1 || true
|
|
106
|
-
|
|
107
|
-
if [[ -n "$(git -C "$anchor_root" status --porcelain --untracked-files=no)" ]]; then
|
|
108
|
-
dirty_stash_message="acp-anchor-sync-$(date -u +%Y%m%dT%H%M%SZ)"
|
|
109
|
-
git -C "$anchor_root" stash push --message "$dirty_stash_message" >/dev/null
|
|
110
|
-
if [[ -n "$(git -C "$anchor_root" status --porcelain --untracked-files=no)" ]]; then
|
|
111
|
-
echo "[agent-sync-anchor] anchor repo is dirty; refuse to reset: $anchor_root" >&2
|
|
112
|
-
exit 1
|
|
113
|
-
fi
|
|
114
|
-
dirty_state_stashed="yes"
|
|
115
|
-
fi
|
|
116
|
-
|
|
117
|
-
default_ref="${remote_name}/${default_branch}"
|
|
118
|
-
if git -C "$anchor_root" show-ref --verify --quiet "refs/remotes/${default_ref}"; then
|
|
119
|
-
current_branch="$(git -C "$anchor_root" symbolic-ref --quiet --short HEAD 2>/dev/null || true)"
|
|
120
|
-
if [[ "$current_branch" != "$default_branch" ]]; then
|
|
121
|
-
if git -C "$anchor_root" show-ref --verify --quiet "refs/heads/${default_branch}"; then
|
|
122
|
-
git -C "$anchor_root" checkout "$default_branch" >/dev/null
|
|
123
|
-
else
|
|
124
|
-
git -C "$anchor_root" checkout -b "$default_branch" "$default_ref" >/dev/null
|
|
125
|
-
fi
|
|
126
|
-
fi
|
|
127
|
-
git -C "$anchor_root" reset --hard "$default_ref" >/dev/null
|
|
128
|
-
git -C "$anchor_root" branch --set-upstream-to="$default_ref" "$default_branch" >/dev/null 2>&1 || true
|
|
129
|
-
fi
|
|
130
|
-
|
|
131
|
-
printf 'CANONICAL_ROOT=%s\n' "$canonical_root"
|
|
132
|
-
printf 'ANCHOR_ROOT=%s\n' "$anchor_root"
|
|
133
|
-
printf 'REMOTE=%s\n' "$remote_name"
|
|
134
|
-
printf 'DEFAULT_BRANCH=%s\n' "$default_branch"
|
|
135
|
-
printf 'ORIGIN_URL=%s\n' "$origin_url"
|
|
136
|
-
printf 'DIRTY_STATE_STASHED=%s\n' "$dirty_state_stashed"
|
|
137
|
-
if [[ "$dirty_state_stashed" == "yes" ]]; then
|
|
138
|
-
printf 'DIRTY_STASH_MESSAGE=%s\n' "$dirty_stash_message"
|
|
139
|
-
fi
|