agent-control-plane 0.1.12 → 0.1.14

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/SKILL.md CHANGED
@@ -22,7 +22,7 @@ external profile registry, not inside this repository.
22
22
  - installed project profiles in `~/.agent-runtime/control-plane/profiles/*/control-plane.yaml`
23
23
  - installed profile notes in `~/.agent-runtime/control-plane/profiles/*/README.md`
24
24
  - workflow catalog in `assets/workflow-catalog.json`
25
- - worker dashboard in `tools/dashboard/` with launchers in `tools/bin/render-dashboard-snapshot.py`
25
+ - worker dashboard in `tools/dashboard/` with launcher at `tools/dashboard/dashboard_snapshot.py`
26
26
  and `tools/bin/serve-dashboard.sh`
27
27
  - dashboard autostart helpers in `tools/bin/dashboard-launchd-bootstrap.sh` and
28
28
  `tools/bin/install-dashboard-launchd.sh`
@@ -15,6 +15,8 @@ FLOW_TOOLS_DIR="${FLOW_SKILL_DIR}/tools/bin"
15
15
  DETACHED_LAUNCH_BIN="${FLOW_TOOLS_DIR}/agent-project-detached-launch"
16
16
  RESIDENT_ISSUE_LOOP_BIN="${FLOW_TOOLS_DIR}/start-resident-issue-loop.sh"
17
17
  REPO_SLUG="$(flow_resolve_repo_slug "${CONFIG_YAML}")"
18
+ AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
19
+ DEFAULT_BRANCH="$(flow_resolve_default_branch "${CONFIG_YAML}")"
18
20
  STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
19
21
  PENDING_LAUNCH_DIR="${ACP_PENDING_LAUNCH_DIR:-${F_LOSNING_PENDING_LAUNCH_DIR:-${STATE_ROOT}/pending-launches}}"
20
22
  AGENT_PR_PREFIXES_JSON="$(flow_managed_pr_prefixes_json "${CONFIG_YAML}")"
@@ -24,6 +26,51 @@ AGENT_EXCLUSIVE_LABEL="${AGENT_EXCLUSIVE_LABEL:-agent-exclusive}"
24
26
  CODING_WORKER="${ACP_CODING_WORKER:-${F_LOSNING_CODING_WORKER:-codex}}"
25
27
  HEARTBEAT_ISSUE_JSON_CACHE_DIR="${TMPDIR:-/tmp}/heartbeat-issue-json.$$"
26
28
 
29
+ heartbeat_issue_retry_state_file() {
30
+ local issue_id="${1:?issue id required}"
31
+ printf '%s/retries/issues/%s.env\n' "${STATE_ROOT}" "${issue_id}"
32
+ }
33
+
34
+ heartbeat_reason_requires_baseline_change() {
35
+ local reason="${1:-}"
36
+ case "${reason}" in
37
+ verification-guard-blocked|no-publishable-commits|no-publishable-delta)
38
+ return 0
39
+ ;;
40
+ *)
41
+ return 1
42
+ ;;
43
+ esac
44
+ }
45
+
46
+ heartbeat_current_baseline_head_sha() {
47
+ local head_sha=""
48
+ if [[ -d "${AGENT_REPO_ROOT}" ]]; then
49
+ head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "origin/${DEFAULT_BRANCH}" 2>/dev/null || true)"
50
+ if [[ -z "${head_sha}" ]]; then
51
+ head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "${DEFAULT_BRANCH}" 2>/dev/null || true)"
52
+ fi
53
+ fi
54
+ printf '%s\n' "${head_sha}"
55
+ }
56
+
57
+ heartbeat_retry_reason_is_baseline_blocked() {
58
+ local issue_id="${1:?issue id required}"
59
+ local reason="${2:-}"
60
+ local state_file baseline_head current_head
61
+
62
+ heartbeat_reason_requires_baseline_change "${reason}" || return 1
63
+ state_file="$(heartbeat_issue_retry_state_file "${issue_id}")"
64
+ [[ -f "${state_file}" ]] || return 1
65
+
66
+ baseline_head="$(awk -F= '/^BASELINE_HEAD_SHA=/{print substr($0, index($0, "=") + 1); exit}' "${state_file}" 2>/dev/null | tr -d '\r' || true)"
67
+ [[ -n "${baseline_head}" ]] || return 1
68
+ current_head="$(heartbeat_current_baseline_head_sha)"
69
+ [[ -n "${current_head}" ]] || return 1
70
+
71
+ [[ "${baseline_head}" == "${current_head}" ]]
72
+ }
73
+
27
74
  heartbeat_issue_json_cached() {
28
75
  local issue_id="${1:?issue id required}"
29
76
  local cache_file=""
@@ -48,6 +95,7 @@ heartbeat_open_agent_pr_issue_ids() {
48
95
  local pr_issue_ids_json=""
49
96
  pr_issue_ids_json="$(
50
97
  flow_github_pr_list_json "$REPO_SLUG" open 100 \
98
+ 2>/dev/null \
51
99
  | jq --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" --arg branchIssueRegex "${AGENT_PR_ISSUE_CAPTURE_REGEX}" '
52
100
  map(
53
101
  . as $pr
@@ -72,7 +120,7 @@ heartbeat_open_agent_pr_issue_ids() {
72
120
  | select(. != null and . != "")
73
121
  )
74
122
  | unique
75
- '
123
+ ' 2>/dev/null || true
76
124
  )"
77
125
 
78
126
  if [[ -z "${pr_issue_ids_json:-}" ]]; then
@@ -89,6 +137,7 @@ heartbeat_list_ready_issue_ids() {
89
137
 
90
138
  ready_issue_rows="$(
91
139
  flow_github_issue_list_json "$REPO_SLUG" open 100 \
140
+ 2>/dev/null \
92
141
  | jq -r --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
93
142
  map(select(
94
143
  (any(.labels[]?; .name == "agent-running") | not)
@@ -98,7 +147,7 @@ heartbeat_list_ready_issue_ids() {
98
147
  | .[]
99
148
  | [.number, (any(.labels[]?; .name == "agent-blocked"))]
100
149
  | @tsv
101
- '
150
+ ' 2>/dev/null || true
102
151
  )"
103
152
 
104
153
  while IFS=$'\t' read -r issue_id is_blocked; do
@@ -123,6 +172,7 @@ heartbeat_list_blocked_recovery_issue_ids() {
123
172
 
124
173
  blocked_issue_rows="$(
125
174
  flow_github_issue_list_json "$REPO_SLUG" open 100 \
175
+ 2>/dev/null \
126
176
  | jq -r --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
127
177
  map(select(
128
178
  any(.labels[]?; .name == "agent-blocked")
@@ -131,7 +181,7 @@ heartbeat_list_blocked_recovery_issue_ids() {
131
181
  ))
132
182
  | sort_by(.createdAt, .number)
133
183
  | .[].number
134
- '
184
+ ' 2>/dev/null || true
135
185
  )"
136
186
 
137
187
  while IFS= read -r issue_id; do
@@ -152,6 +202,9 @@ heartbeat_issue_blocked_recovery_reason() {
152
202
  retry_out="$("${FLOW_TOOLS_DIR}/retry-state.sh" issue "$issue_id" get 2>/dev/null || true)"
153
203
  retry_reason="$(awk -F= '/^LAST_REASON=/{print $2}' <<<"${retry_out:-}")"
154
204
  if [[ -n "${retry_reason:-}" && "${retry_reason}" != "issue-worker-blocked" ]]; then
205
+ if heartbeat_retry_reason_is_baseline_blocked "${issue_id}" "${retry_reason}"; then
206
+ return 0
207
+ fi
155
208
  printf '%s\n' "$retry_reason"
156
209
  return 0
157
210
  fi
@@ -218,6 +271,7 @@ heartbeat_list_exclusive_issue_ids() {
218
271
  open_agent_pr_issue_ids="$(heartbeat_open_agent_pr_issue_ids)"
219
272
 
220
273
  flow_github_issue_list_json "$REPO_SLUG" open 100 \
274
+ 2>/dev/null \
221
275
  | jq -r --arg exclusiveLabel "${AGENT_EXCLUSIVE_LABEL}" --argjson openAgentPrIssueIds "${open_agent_pr_issue_ids}" '
222
276
  map(select(
223
277
  any(.labels[]?; .name == $exclusiveLabel)
@@ -227,20 +281,22 @@ heartbeat_list_exclusive_issue_ids() {
227
281
  ))
228
282
  | sort_by(.createdAt, .number)
229
283
  | .[].number
230
- '
284
+ ' 2>/dev/null || true
231
285
  }
232
286
 
233
287
  heartbeat_list_running_issue_ids() {
234
288
  flow_github_issue_list_json "$REPO_SLUG" open 100 \
289
+ 2>/dev/null \
235
290
  | jq -r '
236
291
  map(select(any(.labels[]?; .name == "agent-running")))
237
292
  | sort_by(.createdAt, .number)
238
293
  | .[].number
239
- '
294
+ ' 2>/dev/null || true
240
295
  }
241
296
 
242
297
  heartbeat_list_open_agent_pr_ids() {
243
298
  flow_github_pr_list_json "$REPO_SLUG" open 100 \
299
+ 2>/dev/null \
244
300
  | jq -r --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" '
245
301
  map(select(
246
302
  . as $pr
@@ -252,11 +308,12 @@ heartbeat_list_open_agent_pr_ids() {
252
308
  ))
253
309
  | sort_by(.createdAt)
254
310
  | .[].number
255
- '
311
+ ' 2>/dev/null || true
256
312
  }
257
313
 
258
314
  heartbeat_list_exclusive_pr_ids() {
259
315
  flow_github_pr_list_json "$REPO_SLUG" open 100 \
316
+ 2>/dev/null \
260
317
  | jq -r --argjson agentPrPrefixes "${AGENT_PR_PREFIXES_JSON}" --arg handoffLabel "${AGENT_PR_HANDOFF_LABEL}" --arg exclusiveLabel "${AGENT_EXCLUSIVE_LABEL}" '
261
318
  map(select(
262
319
  . as $pr
@@ -269,7 +326,7 @@ heartbeat_list_exclusive_pr_ids() {
269
326
  ))
270
327
  | sort_by(.createdAt)
271
328
  | .[].number
272
- '
329
+ ' 2>/dev/null || true
273
330
  }
274
331
 
275
332
  heartbeat_issue_is_heavy() {
@@ -12,7 +12,9 @@ ADAPTER_BIN_DIR="${FLOW_SKILL_DIR}/bin"
12
12
  FLOW_TOOLS_DIR="${FLOW_SKILL_DIR}/tools/bin"
13
13
  REPO_SLUG="$(flow_resolve_repo_slug "${CONFIG_YAML}")"
14
14
  AGENT_ROOT="$(flow_resolve_agent_root "${CONFIG_YAML}")"
15
+ AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
15
16
  STATE_ROOT="$(flow_resolve_state_root "${CONFIG_YAML}")"
17
+ DEFAULT_BRANCH="$(flow_resolve_default_branch "${CONFIG_YAML}")"
16
18
  RUNS_ROOT="$(flow_resolve_runs_root "${CONFIG_YAML}")"
17
19
  BLOCKED_RECOVERY_STATE_DIR="${STATE_ROOT}/blocked-recovery-issues"
18
20
 
@@ -26,6 +28,49 @@ issue_clear_blocked_recovery_state() {
26
28
  rm -f "${BLOCKED_RECOVERY_STATE_DIR}/${ISSUE_ID}.env" 2>/dev/null || true
27
29
  }
28
30
 
31
+ issue_retry_state_file() {
32
+ printf '%s/retries/issues/%s.env\n' "${STATE_ROOT}" "${ISSUE_ID}"
33
+ }
34
+
35
+ issue_reason_requires_baseline_change() {
36
+ local reason="${1:-}"
37
+ case "${reason}" in
38
+ verification-guard-blocked|no-publishable-commits|no-publishable-delta)
39
+ return 0
40
+ ;;
41
+ *)
42
+ return 1
43
+ ;;
44
+ esac
45
+ }
46
+
47
+ issue_current_baseline_head_sha() {
48
+ local head_sha=""
49
+ if [[ -d "${AGENT_REPO_ROOT}" ]]; then
50
+ head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "origin/${DEFAULT_BRANCH}" 2>/dev/null || true)"
51
+ if [[ -z "${head_sha}" ]]; then
52
+ head_sha="$(git -C "${AGENT_REPO_ROOT}" rev-parse --verify --quiet "${DEFAULT_BRANCH}" 2>/dev/null || true)"
53
+ fi
54
+ fi
55
+ printf '%s\n' "${head_sha}"
56
+ }
57
+
58
+ issue_record_retry_baseline_gate() {
59
+ local reason="${1:-}"
60
+ local state_file head_sha tmp_file
61
+
62
+ issue_reason_requires_baseline_change "${reason}" || return 0
63
+ state_file="$(issue_retry_state_file)"
64
+ [[ -f "${state_file}" ]] || return 0
65
+ head_sha="$(issue_current_baseline_head_sha)"
66
+ [[ -n "${head_sha}" ]] || return 0
67
+
68
+ tmp_file="$(mktemp)"
69
+ grep -v '^BASELINE_HEAD_SHA=' "${state_file}" >"${tmp_file}" || true
70
+ printf 'BASELINE_HEAD_SHA=%s\n' "${head_sha}" >>"${tmp_file}"
71
+ mv "${tmp_file}" "${state_file}"
72
+ }
73
+
29
74
  issue_has_schedule_cadence() {
30
75
  local issue_json issue_body
31
76
  issue_json="$(flow_github_issue_view_json "${REPO_SLUG}" "${ISSUE_ID}" 2>/dev/null || true)"
@@ -155,6 +200,7 @@ issue_schedule_retry() {
155
200
  return 0
156
201
  fi
157
202
  "${FLOW_TOOLS_DIR}/retry-state.sh" issue "$ISSUE_ID" schedule "$reason" >/dev/null || true
203
+ issue_record_retry_baseline_gate "${reason}"
158
204
  }
159
205
 
160
206
  issue_mark_ready() {
@@ -156,7 +156,7 @@ function createExecutionContext(stage) {
156
156
  }
157
157
 
158
158
  function runScriptWithContext(context, scriptRelativePath, forwardedArgs, options = {}) {
159
- const scriptPath = path.join(packageRoot, scriptRelativePath);
159
+ const scriptPath = options.scriptPath || path.join(packageRoot, scriptRelativePath);
160
160
  const stdio = options.stdio || "inherit";
161
161
  const result = spawnSync("bash", [scriptPath, ...forwardedArgs], {
162
162
  stdio,
@@ -175,11 +175,78 @@ function runScriptWithContext(context, scriptRelativePath, forwardedArgs, option
175
175
  };
176
176
  }
177
177
 
178
+ function resolvePersistentSourceHome(context) {
179
+ if (process.env.ACP_PROJECT_RUNTIME_SOURCE_HOME) {
180
+ return process.env.ACP_PROJECT_RUNTIME_SOURCE_HOME;
181
+ }
182
+ if (fs.existsSync(path.join(packageRoot, ".git"))) {
183
+ return packageRoot;
184
+ }
185
+ return context.runtimeHome;
186
+ }
187
+
188
+ function runtimeSkillRoot(context) {
189
+ return path.join(context.runtimeHome, "skills", "openclaw", skillName);
190
+ }
191
+
192
+ function createRuntimeExecutionContext(context) {
193
+ const stableSkillRoot = runtimeSkillRoot(context);
194
+ const persistentSourceHome = resolvePersistentSourceHome(context);
195
+ const runtimeScriptEnv = {
196
+ ACP_PROJECT_RUNTIME_SYNC_SCRIPT:
197
+ context.env.ACP_PROJECT_RUNTIME_SYNC_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "sync-shared-agent-home.sh"),
198
+ ACP_PROJECT_RUNTIME_ENSURE_SYNC_SCRIPT:
199
+ context.env.ACP_PROJECT_RUNTIME_ENSURE_SYNC_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "ensure-runtime-sync.sh"),
200
+ ACP_PROJECT_RUNTIME_BOOTSTRAP_SCRIPT:
201
+ context.env.ACP_PROJECT_RUNTIME_BOOTSTRAP_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "project-launchd-bootstrap.sh"),
202
+ ACP_PROJECT_RUNTIME_SUPERVISOR_SCRIPT:
203
+ context.env.ACP_PROJECT_RUNTIME_SUPERVISOR_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "project-runtime-supervisor.sh"),
204
+ ACP_PROJECT_RUNTIME_KICK_SCRIPT:
205
+ context.env.ACP_PROJECT_RUNTIME_KICK_SCRIPT || path.join(stableSkillRoot, "tools", "bin", "kick-scheduler.sh")
206
+ };
207
+ return {
208
+ ...context,
209
+ stableSkillRoot,
210
+ persistentSourceHome,
211
+ env: {
212
+ ...context.env,
213
+ SHARED_AGENT_HOME: context.runtimeHome,
214
+ AGENT_CONTROL_PLANE_ROOT: stableSkillRoot,
215
+ ACP_ROOT: stableSkillRoot,
216
+ AGENT_FLOW_SOURCE_ROOT: stableSkillRoot,
217
+ ACP_PROJECT_INIT_SOURCE_HOME: persistentSourceHome,
218
+ ACP_PROJECT_RUNTIME_SOURCE_HOME: persistentSourceHome,
219
+ ACP_DASHBOARD_SOURCE_HOME: persistentSourceHome,
220
+ ...runtimeScriptEnv
221
+ }
222
+ };
223
+ }
224
+
225
+ function syncRuntimeHome(context, options = {}) {
226
+ const result = runScriptWithContext(context, "tools/bin/sync-shared-agent-home.sh", [], {
227
+ stdio: options.stdio || "inherit"
228
+ });
229
+ if (result.status !== 0) {
230
+ throw new Error("failed to sync runtime home before command execution");
231
+ }
232
+ }
233
+
178
234
  function runCommand(scriptRelativePath, forwardedArgs) {
179
235
  const stage = stageSharedHome();
180
236
  const context = createExecutionContext(stage);
181
237
 
182
238
  try {
239
+ if (scriptRelativePath !== "tools/bin/sync-shared-agent-home.sh") {
240
+ syncRuntimeHome(context, { stdio: "inherit" });
241
+ const runtimeContext = createRuntimeExecutionContext(context);
242
+ const runtimeScriptPath = path.join(runtimeContext.stableSkillRoot, scriptRelativePath);
243
+ const result = runScriptWithContext(runtimeContext, scriptRelativePath, forwardedArgs, {
244
+ stdio: "inherit",
245
+ scriptPath: runtimeScriptPath
246
+ });
247
+ return result.status;
248
+ }
249
+
183
250
  const result = runScriptWithContext(context, scriptRelativePath, forwardedArgs, { stdio: "inherit" });
184
251
  return result.status;
185
252
  } finally {
@@ -1470,8 +1537,8 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
1470
1537
  runtimeStartReason = `doctor-${doctorKv.DOCTOR_STATUS || "not-ok"}`;
1471
1538
  } else {
1472
1539
  actions.push("runtime-start");
1473
- runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId]);
1474
- const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId]);
1540
+ runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId], { useRuntimeCopy: true });
1541
+ const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId], { useRuntimeCopy: true });
1475
1542
  runtimeStatusKv = parseKvOutput(runtimeStatusOutput);
1476
1543
  runtimeStartStatus = "ok";
1477
1544
  runtimeStartReason = "";
@@ -1480,7 +1547,7 @@ async function maybeRunFinalSetupFixups(options, scopedContext, config, currentS
1480
1547
 
1481
1548
  if (config.installLaunchd && process.platform === "darwin" && launchdInstallStatus !== "ok" && runtimeStartStatus === "ok") {
1482
1549
  actions.push("launchd-install");
1483
- runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId]);
1550
+ runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId], { useRuntimeCopy: true });
1484
1551
  launchdInstallStatus = "ok";
1485
1552
  launchdInstallReason = "";
1486
1553
  }
@@ -1603,7 +1670,21 @@ async function collectSetupConfig(options, context) {
1603
1670
 
1604
1671
  function runSetupStep(context, title, scriptRelativePath, args, options = {}) {
1605
1672
  console.log(`\n== ${title} ==`);
1606
- const result = runScriptWithContext(context, scriptRelativePath, args, { stdio: "pipe", env: options.env, cwd: options.cwd });
1673
+ let executionContext = context;
1674
+ let scriptPath = undefined;
1675
+
1676
+ if (options.useRuntimeCopy) {
1677
+ syncRuntimeHome(context, { stdio: "pipe" });
1678
+ executionContext = createRuntimeExecutionContext(context);
1679
+ scriptPath = path.join(executionContext.stableSkillRoot, scriptRelativePath);
1680
+ }
1681
+
1682
+ const result = runScriptWithContext(executionContext, scriptRelativePath, args, {
1683
+ stdio: "pipe",
1684
+ env: options.env,
1685
+ cwd: options.cwd,
1686
+ scriptPath
1687
+ });
1607
1688
  if (result.status !== 0) {
1608
1689
  printFailureDetails(result);
1609
1690
  throw new Error(`${title} failed`);
@@ -1868,8 +1949,8 @@ async function runSetupFlow(forwardedArgs) {
1868
1949
  runtimeStartReason = "gh-auth-not-ready";
1869
1950
  console.log("runtime start skipped: GitHub CLI is not authenticated yet. Run `gh auth login` and start the runtime afterwards.");
1870
1951
  } else {
1871
- runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId]);
1872
- const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId]);
1952
+ runSetupStep(scopedContext, "Start the runtime", "tools/bin/project-runtimectl.sh", ["start", "--profile-id", config.profileId], { useRuntimeCopy: true });
1953
+ const runtimeStatusOutput = runSetupStep(scopedContext, "Read back runtime status", "tools/bin/project-runtimectl.sh", ["status", "--profile-id", config.profileId], { useRuntimeCopy: true });
1873
1954
  runtimeStatusKv = parseKvOutput(runtimeStatusOutput);
1874
1955
  runtimeStartStatus = "ok";
1875
1956
  runtimeStartReason = "";
@@ -1886,7 +1967,7 @@ async function runSetupFlow(forwardedArgs) {
1886
1967
  launchdInstallReason = "runtime-not-started";
1887
1968
  console.log("launchd install skipped: runtime was not started successfully in this setup run.");
1888
1969
  } else {
1889
- runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId]);
1970
+ runSetupStep(scopedContext, "Install macOS autostart", "tools/bin/install-project-launchd.sh", ["--profile-id", config.profileId], { useRuntimeCopy: true });
1890
1971
  launchdInstallStatus = "ok";
1891
1972
  launchdInstallReason = "";
1892
1973
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-control-plane",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Help a repo keep GitHub-driven coding agents running reliably without constant human babysitting",
5
5
  "homepage": "https://github.com/ducminhnguyen0319/agent-control-plane",
6
6
  "bugs": {
@@ -32,12 +32,14 @@
32
32
  "tools/bin",
33
33
  "!tools/bin/audit-*.sh",
34
34
  "!tools/bin/check-skill-contracts.sh",
35
+ "!tools/bin/render-dashboard-snapshot.py",
35
36
  "tools/dashboard/app.js",
36
37
  "tools/dashboard/dashboard_snapshot.py",
37
38
  "tools/dashboard/index.html",
38
39
  "tools/dashboard/server.py",
39
40
  "tools/dashboard/styles.css",
40
41
  "tools/templates",
42
+ "!tools/templates/legacy/",
41
43
  "tools/vendor/codex-quota/LICENSE",
42
44
  "tools/vendor/codex-quota/codex-quota.js",
43
45
  "tools/vendor/codex-quota/lib",
@@ -85,7 +85,7 @@ tools/bin/uninstall-project-launchd.sh --profile-id <id>
85
85
  tools/bin/project-remove.sh --profile-id <id>
86
86
  tools/bin/project-remove.sh --profile-id <id> --purge-paths
87
87
  tools/bin/sync-shared-agent-home.sh
88
- python3 tools/bin/render-dashboard-snapshot.py --pretty
88
+ python3 tools/dashboard/dashboard_snapshot.py --pretty
89
89
  bash tools/bin/serve-dashboard.sh --host 127.0.0.1 --port 8765
90
90
  bash tools/bin/install-dashboard-launchd.sh --host 127.0.0.1 --port 8765
91
91
  ```
@@ -97,7 +97,7 @@ prompts live under `tools/templates/`.
97
97
  ## Dashboard
98
98
 
99
99
  ```bash
100
- python3 tools/bin/render-dashboard-snapshot.py --pretty
100
+ python3 tools/dashboard/dashboard_snapshot.py --pretty
101
101
  bash tools/bin/serve-dashboard.sh --host 127.0.0.1 --port 8765
102
102
  bash tools/bin/install-dashboard-launchd.sh --host 127.0.0.1 --port 8765
103
103
  ```
@@ -64,7 +64,7 @@ roots, labels, worker preferences, prompts, and project-specific guardrails.
64
64
  before scheduler use.
65
65
  - `tools/bin/test-smoke.sh`
66
66
  Runs the main shared-package smoke gates in one operator-facing command.
67
- - `tools/bin/render-dashboard-snapshot.py`
67
+ - `tools/dashboard/dashboard_snapshot.py`
68
68
  Emits a JSON snapshot of active runs, resident controllers, cooldown state,
69
69
  queue depth, and scheduled issues across installed profiles.
70
70
  - `tools/bin/serve-dashboard.sh`
@@ -307,12 +307,14 @@ schedule_provider_quota_cooldown() {
307
307
  local reason="${1:-provider-quota-limit}"
308
308
  [[ "${reason}" == "provider-quota-limit" ]] || return 0
309
309
  [[ -x "${provider_cooldown_script}" ]] || return 0
310
+ [[ "${CODING_WORKER:-}" == "codex" ]] && return 0
310
311
 
311
312
  "${provider_cooldown_script}" schedule "${reason}" >/dev/null || true
312
313
  }
313
314
 
314
315
  clear_provider_quota_cooldown() {
315
316
  [[ -x "${provider_cooldown_script}" ]] || return 0
317
+ [[ "${CODING_WORKER:-}" == "codex" ]] && return 0
316
318
 
317
319
  "${provider_cooldown_script}" clear >/dev/null || true
318
320
  }