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.
Files changed (63) hide show
  1. package/README.md +93 -14
  2. package/bin/pr-risk.sh +28 -6
  3. package/hooks/heartbeat-hooks.sh +62 -22
  4. package/npm/bin/agent-control-plane.js +360 -10
  5. package/package.json +6 -3
  6. package/references/architecture.md +8 -0
  7. package/references/control-plane-map.md +6 -2
  8. package/references/release-checklist.md +0 -2
  9. package/tools/bin/agent-github-update-labels +6 -1
  10. package/tools/bin/agent-project-catch-up-issue-pr-links +118 -0
  11. package/tools/bin/agent-project-catch-up-merged-prs +78 -21
  12. package/tools/bin/agent-project-catch-up-scheduled-issue-retries +123 -0
  13. package/tools/bin/agent-project-cleanup-session +132 -4
  14. package/tools/bin/agent-project-heartbeat-loop +116 -1461
  15. package/tools/bin/agent-project-reconcile-issue-session +90 -117
  16. package/tools/bin/agent-project-reconcile-pr-session +76 -111
  17. package/tools/bin/agent-project-run-claude-session +12 -2
  18. package/tools/bin/agent-project-run-codex-resilient +86 -9
  19. package/tools/bin/agent-project-run-codex-session +16 -5
  20. package/tools/bin/agent-project-run-kilo-session +356 -14
  21. package/tools/bin/agent-project-run-ollama-session +658 -0
  22. package/tools/bin/agent-project-run-openclaw-session +37 -25
  23. package/tools/bin/agent-project-run-opencode-session +364 -14
  24. package/tools/bin/agent-project-run-pi-session +479 -0
  25. package/tools/bin/agent-project-worker-status +11 -8
  26. package/tools/bin/cleanup-worktree.sh +6 -1
  27. package/tools/bin/flow-config-lib.sh +196 -3
  28. package/tools/bin/flow-resident-worker-lib.sh +120 -2
  29. package/tools/bin/flow-shell-lib.sh +29 -2
  30. package/tools/bin/heartbeat-loop-cache-lib.sh +164 -0
  31. package/tools/bin/heartbeat-loop-counting-lib.sh +306 -0
  32. package/tools/bin/heartbeat-loop-pr-strategy-lib.sh +199 -0
  33. package/tools/bin/heartbeat-loop-scheduling-lib.sh +506 -0
  34. package/tools/bin/heartbeat-loop-worker-lib.sh +319 -0
  35. package/tools/bin/heartbeat-recovery-preflight.sh +13 -1
  36. package/tools/bin/heartbeat-safe-auto.sh +119 -20
  37. package/tools/bin/install-project-launchd.sh +19 -2
  38. package/tools/bin/prepare-worktree.sh +4 -4
  39. package/tools/bin/profile-activate.sh +2 -2
  40. package/tools/bin/profile-adopt.sh +2 -2
  41. package/tools/bin/project-init.sh +1 -1
  42. package/tools/bin/project-launchd-bootstrap.sh +11 -8
  43. package/tools/bin/project-runtimectl.sh +90 -7
  44. package/tools/bin/provider-cooldown-state.sh +14 -14
  45. package/tools/bin/reconcile-bootstrap-lib.sh +113 -0
  46. package/tools/bin/render-flow-config.sh +30 -33
  47. package/tools/bin/resident-issue-controller-lib.sh +448 -0
  48. package/tools/bin/resident-issue-queue-status.py +35 -0
  49. package/tools/bin/run-codex-task.sh +53 -4
  50. package/tools/bin/scaffold-profile.sh +18 -3
  51. package/tools/bin/start-issue-worker.sh +1 -1
  52. package/tools/bin/start-pr-fix-worker.sh +30 -0
  53. package/tools/bin/start-pr-review-worker.sh +31 -0
  54. package/tools/bin/start-resident-issue-loop.sh +27 -438
  55. package/tools/bin/sync-agent-repo.sh +2 -2
  56. package/tools/bin/sync-dependency-baseline.sh +3 -3
  57. package/tools/bin/sync-shared-agent-home.sh +4 -1
  58. package/tools/dashboard/app.js +7 -0
  59. package/tools/dashboard/dashboard_snapshot.py +13 -29
  60. package/tools/templates/pr-fix-template.md +3 -7
  61. package/tools/templates/pr-merge-repair-template.md +3 -7
  62. package/tools/templates/pr-review-template.md +2 -1
  63. package/SKILL.md +0 -149
@@ -219,6 +219,9 @@ function renderProfile(profile) {
219
219
  ? `<span class="badge">${profile.provider_pool.backend}: ${profile.provider_pool.model || "n/a"}</span>`
220
220
  : "",
221
221
  profile.provider_pool.name ? `<span class="badge">${profile.provider_pool.name}</span>` : "",
222
+ profile.provider_pool.pools_exhausted
223
+ ? `<span class="badge warn">pools exhausted</span>`
224
+ : "",
222
225
  profile.provider_pool.last_reason ? `<span class="badge warn">${profile.provider_pool.last_reason}</span>` : "",
223
226
  ]
224
227
  .filter(Boolean)
@@ -280,6 +283,7 @@ function renderProfile(profile) {
280
283
  [
281
284
  { label: "Issue", key: "issue_id" },
282
285
  { label: "State", render: renderControllerState },
286
+ { label: "Lane", render: (row) => `${row.lane_kind || "n/a"} / ${row.lane_value || "n/a"}` },
283
287
  { label: "Reason", render: (row) => row.reason || "n/a" },
284
288
  { label: "Provider", render: (row) => `${row.provider_backend || "n/a"} ${row.provider_model || ""}`.trim() },
285
289
  { label: "Failover", render: (row) => `${row.provider_failover_count} failovers / ${row.provider_switch_count} switches` },
@@ -320,6 +324,7 @@ function renderProfile(profile) {
320
324
  { label: "Scope", key: "scope" },
321
325
  { label: "Worker", key: "coding_worker" },
322
326
  { label: "Issue", render: (row) => row.issue_id || "n/a" },
327
+ { label: "Lane", render: (row) => `${row.resident_lane_kind || "n/a"} / ${row.resident_lane_value || "n/a"}` },
323
328
  { label: "Tasks", key: "task_count" },
324
329
  { label: "Last status", render: (row) => row.last_status || "n/a" },
325
330
  { label: "Last started", render: (row) => row.last_started_at ? `${relativeTime(row.last_started_at)}<div class="muted">${row.last_started_at}</div>` : "n/a" },
@@ -355,6 +360,7 @@ function renderProfile(profile) {
355
360
  [
356
361
  { label: "Issue", key: "issue_id" },
357
362
  { label: "Session", render: (row) => row.session ? `<div class="mono">${row.session}</div>` : "n/a" },
363
+ { label: "Queued by", key: "queued_by" },
358
364
  { label: "Updated", render: (row) => row.updated_at ? `${relativeTime(row.updated_at)}<div class="muted">${row.updated_at}</div>` : "n/a" },
359
365
  ],
360
366
  profile.issue_queue.pending,
@@ -365,6 +371,7 @@ function renderProfile(profile) {
365
371
  [
366
372
  { label: "Issue", key: "issue_id" },
367
373
  { label: "Session", render: (row) => row.session ? `<div class="mono">${row.session}</div>` : "n/a" },
374
+ { label: "Claimed by", key: "claimer" },
368
375
  { label: "Updated", render: (row) => row.updated_at ? `${relativeTime(row.updated_at)}<div class="muted">${row.updated_at}</div>` : "n/a" },
369
376
  ],
370
377
  profile.issue_queue.claims || [],
@@ -13,9 +13,15 @@ from typing import Any
13
13
 
14
14
  ROOT_DIR = Path(__file__).resolve().parents[2]
15
15
  TOOLS_BIN_DIR = ROOT_DIR / "tools" / "bin"
16
+ DASHBOARD_DIR = ROOT_DIR / "tools" / "dashboard"
16
17
  RENDER_FLOW_CONFIG = TOOLS_BIN_DIR / "render-flow-config.sh"
17
18
  WORKER_STATUS_TOOL = TOOLS_BIN_DIR / "agent-project-worker-status"
18
19
 
20
+ if str(DASHBOARD_DIR) not in sys.path:
21
+ sys.path.insert(0, str(DASHBOARD_DIR))
22
+
23
+ from issue_queue_state import collect_issue_queue
24
+
19
25
 
20
26
  def utc_now_iso() -> str:
21
27
  return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -493,6 +499,13 @@ def collect_resident_workers(state_root: Path) -> list[dict[str, Any]]:
493
499
  "last_action": env.get("LAST_ACTION", ""),
494
500
  "last_failure_reason": env.get("LAST_FAILURE_REASON", ""),
495
501
  "worktree": env.get("WORKTREE", ""),
502
+ "resident_lane_kind": env.get("RESIDENT_LANE_KIND", ""),
503
+ "resident_lane_value": env.get("RESIDENT_LANE_VALUE", ""),
504
+ "resident_lane": (
505
+ f"{env.get('RESIDENT_LANE_KIND', '')}/{env.get('RESIDENT_LANE_VALUE', '')}"
506
+ if env.get("RESIDENT_LANE_KIND", "") and env.get("RESIDENT_LANE_VALUE", "")
507
+ else ""
508
+ ),
496
509
  "metadata_file": str(path),
497
510
  }
498
511
  )
@@ -695,35 +708,6 @@ def resolve_history_root(render_env: dict[str, str], yaml_env: dict[str, str], r
695
708
  return runs_root.parent / "history"
696
709
  return Path(".")
697
710
 
698
-
699
- def collect_issue_queue(state_root: Path) -> dict[str, list[dict[str, Any]]]:
700
- queue_root = state_root / "resident-workers" / "issue-queue"
701
- pending_root = queue_root / "pending"
702
- claims_root = queue_root / "claims"
703
-
704
- def collect_files(root: Path) -> list[dict[str, Any]]:
705
- if not root.is_dir():
706
- return []
707
- items: list[dict[str, Any]] = []
708
- for path in sorted(root.glob("*.env"), key=lambda item: item.stat().st_mtime, reverse=True):
709
- env = read_env_file(path)
710
- items.append(
711
- {
712
- "issue_id": env.get("ISSUE_ID", path.stem.removeprefix("issue-")),
713
- "session": env.get("SESSION", ""),
714
- "claim_file": env.get("CLAIM_FILE", ""),
715
- "updated_at": env.get("UPDATED_AT", "") or file_mtime_iso(path),
716
- "state_file": str(path),
717
- }
718
- )
719
- return items
720
-
721
- return {
722
- "pending": collect_files(pending_root),
723
- "claims": collect_files(claims_root),
724
- }
725
-
726
-
727
711
  def build_profile_snapshot(profile_id: str, registry_root: Path) -> dict[str, Any]:
728
712
  env = env_with_profile(profile_id, registry_root)
729
713
  render_env = run_key_value_script(RENDER_FLOW_CONFIG, env)
@@ -2,13 +2,9 @@ You are the PR repair worker for `{REPO_SLUG}`.
2
2
 
3
3
  Before making any change:
4
4
 
5
- 1. Read `{REPO_ROOT}/AGENTS.md`.
6
- 2. If present, read `{REPO_ROOT}/openspec/AGENT_RULES.md`.
7
- 3. If present, read `{REPO_ROOT}/openspec/AGENTS.md`.
8
- 4. If present, read `{REPO_ROOT}/openspec/project.md`.
9
- 5. If present, read `{REPO_ROOT}/openspec/CONVENTIONS.md`.
10
- 6. If present, read `{REPO_ROOT}/docs/TESTING_AND_SEED_POLICY.md`.
11
- 7. Stay on this PR branch worktree. Do not push or mutate GitHub from inside the worker.
5
+ 1. Read the following repo context before changing code:
6
+ {PR_CONTEXT_READS_TEXT}
7
+ 2. Stay on this PR branch worktree. Do not push or mutate GitHub from inside the worker.
12
8
 
13
9
  PR metadata:
14
10
 
@@ -2,13 +2,9 @@ You are the PR merge-repair worker for `{REPO_SLUG}`.
2
2
 
3
3
  Before making any change:
4
4
 
5
- 1. Read `{REPO_ROOT}/AGENTS.md`.
6
- 2. If present, read `{REPO_ROOT}/openspec/AGENT_RULES.md`.
7
- 3. If present, read `{REPO_ROOT}/openspec/AGENTS.md`.
8
- 4. If present, read `{REPO_ROOT}/openspec/project.md`.
9
- 5. If present, read `{REPO_ROOT}/openspec/CONVENTIONS.md`.
10
- 6. If present, read `{REPO_ROOT}/docs/TESTING_AND_SEED_POLICY.md`.
11
- 7. Stay on this PR branch worktree. Do not push or mutate GitHub from inside the worker.
5
+ 1. Read the following repo context before changing code:
6
+ {PR_CONTEXT_READS_TEXT}
7
+ 2. Stay on this PR branch worktree. Do not push or mutate GitHub from inside the worker.
12
8
 
13
9
  PR metadata:
14
10
 
@@ -2,7 +2,8 @@ You are the PR review and final-merge worker for `{REPO_SLUG}`.
2
2
 
3
3
  Before making any decision:
4
4
 
5
- 1. Read `{REPO_ROOT}/AGENTS.md` and any repo-specific conventions or design docs relevant to the PR.
5
+ 1. Read the following repo context before deciding:
6
+ {PR_CONTEXT_READS_TEXT}
6
7
  2. Do not edit product code in this worktree. This is review and final-review only.
7
8
  3. Never run dependency bootstrap or workspace-mutating commands here.
8
9
 
package/SKILL.md DELETED
@@ -1,149 +0,0 @@
1
- ---
2
- name: agent-control-plane
3
- description: Use when working on the shared multi-project agent control plane, including scheduler/runtime orchestration, worktree and worker lifecycle, profile onboarding, and cross-project automation flows.
4
- ---
5
-
6
- # Agent Control Plane
7
-
8
- This repository is the canonical `agent-control-plane` package. It owns the
9
- generic scheduler/runtime, worktree lifecycle, profile onboarding, queue/risk
10
- automation, and profile-scoped prompt/template resolution used across multiple
11
- projects.
12
-
13
- Installed project profiles live under
14
- `~/.agent-runtime/control-plane/profiles/<id>/`. Treat the control plane itself
15
- as generic, then load the selected profile's local guidance only when the task
16
- is truly about that project. Integrated project data should stay in that
17
- external profile registry, not inside this repository.
18
-
19
- ## What Lives Here
20
-
21
- - core operating manual in this `SKILL.md`
22
- - installed project profiles in `~/.agent-runtime/control-plane/profiles/*/control-plane.yaml`
23
- - installed profile notes in `~/.agent-runtime/control-plane/profiles/*/README.md`
24
- - workflow catalog in `assets/workflow-catalog.json`
25
- - worker dashboard in `tools/dashboard/` with launcher at `tools/dashboard/dashboard_snapshot.py`
26
- and `tools/bin/serve-dashboard.sh`
27
- - dashboard autostart helpers in `tools/bin/dashboard-launchd-bootstrap.sh` and
28
- `tools/bin/install-dashboard-launchd.sh`
29
- - project autostart helpers in `tools/bin/project-launchd-bootstrap.sh`,
30
- `tools/bin/install-project-launchd.sh`, and
31
- `tools/bin/uninstall-project-launchd.sh`
32
- - queue/label/risk scripts in `bin/`
33
- - heartbeat and reconcile hooks in `hooks/`
34
- - shared runtime wrappers, onboarding tools, and tests in `tools/bin/` and
35
- `tools/tests/`
36
-
37
- The vendored runtime entrypoints used by live schedulers are published from this
38
- checkout into the shared canonical skill copy under
39
- `skills/openclaw/agent-control-plane`, then copied into
40
- `~/.agent-runtime/runtime-home/skills/openclaw/agent-control-plane` by
41
- `tools/bin/sync-shared-agent-home.sh`.
42
-
43
- ## Required Startup Sequence
44
-
45
- Before doing non-trivial work in this repository or on an integrated project:
46
-
47
- 1. Determine the active profile with `AGENT_PROJECT_ID`, `ACP_PROJECT_ID`, or
48
- `tools/bin/render-flow-config.sh`.
49
- 2. Read the selected profile notes in
50
- `~/.agent-runtime/control-plane/profiles/<id>/README.md` when they exist.
51
- 3. Read the selected repo's local startup docs before changing behavior:
52
- `AGENTS.md`, `openspec/AGENT_RULES.md`, `openspec/AGENTS.md`,
53
- `openspec/project.md`, and `openspec/CONVENTIONS.md`.
54
- 4. Use a clean read-only inspection checkout first; move to an isolated
55
- worktree or agent-owned checkout before making non-trivial edits.
56
- 5. If the task changes product behavior, inspect the active OpenSpec changes
57
- before implementation.
58
-
59
- For onboarding a new repository onto the shared control plane:
60
-
61
- 1. Prefer `tools/bin/project-init.sh --profile-id <id> --repo-slug <owner/repo>`
62
- 2. Fill in `~/.agent-runtime/control-plane/profiles/<id>/README.md`
63
- 3. If you need manual control, the underlying steps remain:
64
- `tools/bin/scaffold-profile.sh`, `tools/bin/profile-smoke.sh`,
65
- `tools/bin/profile-adopt.sh`, and `tools/bin/sync-shared-agent-home.sh`
66
-
67
- For runtime control of one installed profile:
68
-
69
- 1. Check state with `tools/bin/project-runtimectl.sh status --profile-id <id>`
70
- 2. Start or ensure runtime with `tools/bin/project-runtimectl.sh start --profile-id <id>`
71
- 3. Stop or recycle runtime with `tools/bin/project-runtimectl.sh stop --profile-id <id>`
72
- 4. Use `tools/bin/project-runtimectl.sh restart --profile-id <id>` for a clean bounce
73
- 5. Use `tools/bin/install-project-launchd.sh --profile-id <id>` when one
74
- profile should survive reboot/login via a per-project LaunchAgent
75
- 6. Remove per-project autostart with
76
- `tools/bin/uninstall-project-launchd.sh --profile-id <id>`
77
- 7. Remove an installed profile with `tools/bin/project-remove.sh --profile-id <id>`
78
-
79
- ## Task Routing
80
-
81
- Pick the smallest matching path and load only the relevant references:
82
-
83
- - Control-plane layout, publication model, and profile ownership:
84
- `references/control-plane-map.md`
85
- - Control-plane operator commands and profile-management entrypoints:
86
- `references/commands.md`
87
- - Control-plane repository layout:
88
- `references/repo-map.md`
89
- - Control-plane docs and profile guidance locations:
90
- `references/docs-map.md`
91
- - Project-specific rules and repo commands:
92
- `~/.agent-runtime/control-plane/profiles/<id>/README.md`
93
-
94
- ## Repo Rules That Matter Most
95
-
96
- - Keep the core engine generic. Put repo-specific behavior behind a profile,
97
- profile templates, or profile-scoped docs instead of hardcoding it into the
98
- shared runtime.
99
- - Follow OpenSpec and the selected repo's local rules before implementing
100
- product behavior changes.
101
- - Do not simplify or change approach without explicit user approval.
102
- - Prefer deterministic wrappers and config-driven routing over special-case
103
- conditionals.
104
- - For any non-trivial write task, use a dedicated agent worktree or another
105
- isolated clean checkout.
106
- - Preserve dirty retained checkouts; continue from a fresh isolated worktree
107
- instead of layering more edits there.
108
- - Prefer canonical docs and profile-local notes over stale audits or incidental
109
- markdown snapshots.
110
- - When updating the control plane itself, repair published copies after the
111
- source change so runtime and source do not drift.
112
-
113
- ## Common Operating Patterns
114
-
115
- ### Analysis and Planning
116
-
117
- - Resolve the active profile first.
118
- - Read `~/.agent-runtime/control-plane/profiles/<id>/README.md` for repo-local context.
119
- - Use `references/docs-map.md` to find the canonical control-plane or
120
- profile-local source instead of scanning random files.
121
-
122
- ### Implementation
123
-
124
- - Keep generic scheduler/runtime changes in the shared engine.
125
- - Put repo-specific prompts, commands, or heuristics in the installed profile
126
- directory under `~/.agent-runtime/control-plane/profiles/<id>/`.
127
- - Keep changes reversible and tightly scoped.
128
-
129
- ### Testing
130
-
131
- - Use `references/commands.md` for control-plane checks.
132
- - Use the selected profile's README for repo-specific dev/test commands.
133
- - Re-run `tools/bin/profile-smoke.sh`, `tools/bin/check-skill-contracts.sh`,
134
- dashboard tests, and targeted shell tests after meaningful control-plane
135
- changes.
136
-
137
- ### Publishing and Runtime Health
138
-
139
- - Use `tools/bin/flow-runtime-doctor.sh` to confirm source/runtime sync.
140
- - Use `tools/bin/sync-shared-agent-home.sh` after changing runtime-facing files.
141
- - Prefer copied published artifacts over symlink aliases.
142
-
143
- ## References
144
-
145
- - `references/control-plane-map.md`
146
- - `references/commands.md`
147
- - `references/repo-map.md`
148
- - `references/docs-map.md`
149
- - `~/.agent-runtime/control-plane/profiles/<id>/README.md`