agent-control-plane 0.7.0 → 0.8.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 CHANGED
@@ -1,4 +1,4 @@
1
- # agent-control-plane (ACP) v0.7.0
1
+ # agent-control-plane (ACP) v0.7.1
2
2
 
3
3
  <p>
4
4
  <a href="https://github.com/ducminhnguyen0319/agent-control-plane/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/ducminhnguyen0319/agent-control-plane/actions/workflows/ci.yml/badge.svg?branch=main"></a>
@@ -7,14 +7,20 @@
7
7
  <a href="./LICENSE"><img alt="license" src="https://img.shields.io/npm/l/agent-control-plane?style=flat-square"></a>
8
8
  <a href="https://github.com/sponsors/ducminhnguyen0319"><img alt="GitHub Sponsors" src="https://img.shields.io/badge/sponsor-GitHub%20Sponsors-ea4aaa?style=flat-square&logo=githubsponsors&logoColor=white"></a>
9
9
  <a href="https://socket.dev/npm/package/agent-control-plane"><img alt="Socket" src="https://img.shields.io/badge/Socket-79-f5a623?style=flat-square"></a>
10
- <a href="./ROADMAP.md"><img alt="Roadmap Complete" src="https://img.shields.io/badge/ROADMAP-v0.7.0-success?style=flat-square"></a>
11
- <a href="./CHANGELOG.md"><img alt="Changelog" src="https://img.shields.io/badge/CHANGELOG-v0.7.0-blue?style=flat-square"></a>
10
+ <a href="./ROADMAP.md"><img alt="Roadmap Complete" src="https://img.shields.io/badge/ROADMAP-v0.7.1-success?style=flat-square&logo=markdown"></a>
11
+ <a href="./CHANGELOG.md"><img alt="Changelog" src="https://img.shields.io/badge/CHANGELOG-v0.7.1-blue?style=flat-square&logo=markdown"></a>
12
12
  </p>
13
13
 
14
14
  **agent-control-plane (ACP)** keeps your coding agents running reliably without you having to stare at them all day.
15
15
 
16
- ## ✅ ROADMAP UPDATE (v0.7.0) - New Features!
16
+ ## ✅ ROADMAP UPDATE (v0.7.1) - Package Fixes & Hardening!
17
17
 
18
+ ### v0.7.1 Fixes
19
+ - **Package Contents**: All adapter files now included (codex, claude, openclaw, ollama, pi, opencode, kilo)
20
+ - **Ollama Adapter**: Fixed context window detection to parse `model_info` correctly
21
+ - **Package Cleanup**: Removed `__pycache__` directories from published package
22
+
23
+ ### v0.7.0 Features
18
24
  - **Real-time Dashboard**: WebSocket updates (no more 5s polling!)
19
25
  - **Hardened Adapters**: All 6 backends now production-ready
20
26
  - **Native Windows Support**: Run as Windows Service (NSSM/sc.exe/PowerShell)
@@ -346,10 +352,48 @@ ACP is a shell-first operator tool. Most install problems become easier to
346
352
  debug once it is clear which dependency is responsible for which part of the
347
353
  system.
348
354
 
355
+ ### Cross-Platform Installation
356
+
357
+ **macOS:**
358
+ ```bash
359
+ # Install Node.js (if needed)
360
+ brew install node
361
+
362
+ # Install required tools
363
+ brew install bash git jq python3 tmux
364
+ brew install gh # for GitHub-first setups
365
+ ```
366
+
367
+ **Linux (Ubuntu/Debian):**
368
+ ```bash
369
+ # Install Node.js (if needed)
370
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
371
+ sudo apt-get install -y nodejs
372
+
373
+ # Install required tools
374
+ sudo apt-get install -y bash git jq python3 tmux
375
+ sudo apt-get install -y gh # for GitHub-first setups
376
+ ```
377
+
378
+ **Linux (RHEL/CentOS/Fedora):**
379
+ ```bash
380
+ # Install Node.js (if needed)
381
+ curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
382
+ sudo yum install -y nodejs
383
+
384
+ # Install required tools
385
+ sudo yum install -y bash git jq python3 tmux
386
+ sudo yum install -y gh # for GitHub-first setups
387
+ ```
388
+
389
+ **Windows:** See [Windows Setup Guide](docs/WINDOWS_SETUP.md) for native Windows Service setup.
390
+
391
+ ### Dependency Table
392
+
349
393
  | Tool | Required | Purpose | Notes |
350
394
  | --- | --- | --- | --- |
351
395
  | Node.js `>= 18` | yes | Runs the npm package entrypoint and `npx` wrapper. | CI runs on Node `22`. Node `20` or `22` both work fine. |
352
- | `bash` | yes | All runtime, profile, and worker orchestration scripts are Bash. | Your login shell can be `zsh`; `bash` just needs to be on `PATH`. |
396
+ | `bash` | yes | All runtime, profile, and worker orchestration scripts are Bash. | Your login shell can be `zsh` or `fish`; `bash` just needs to be on `PATH`. |
353
397
  | `git` | yes | Manages worktrees, checks branch state, and coordinates repo automation. | Required even if you interact only through forge issues and PRs. |
354
398
  | `gh` | for GitHub-first setups | GitHub CLI auth and API access for issues, PRs, labels, and metadata. | Run `gh auth login` before first use when `--forge-provider github`. |
355
399
  | `jq` | yes | Parses JSON from `gh` output and worker metadata throughout. | Missing `jq` will break GitHub-heavy and Gitea-heavy runtime flows. |
@@ -358,7 +402,7 @@ system.
358
402
  | Worker CLI (backend-specific) | depends on backend | The coding agent for a profile. Supported: `codex`, `claude`, `openclaw` (production); `ollama`, `pi`, `opencode`, `kilo` (experimental). | Install and authenticate your chosen backend before starting background runs. |
359
403
  | `ollama` | for `ollama` backend | Serves local models via OpenAI-compatible API at `http://localhost:11434`. | Install from [ollama.com](https://ollama.com) and pull a model (e.g. `ollama pull qwen3.5:9b`) before use. |
360
404
  | `pi` CLI | for `pi` backend | Lightweight coding agent using OpenRouter-compatible APIs. | Install via `npm i -g @mariozechner/pi-coding-agent`. Set `OPENROUTER_API_KEY` before use. |
361
- | `crush` (opencode) | for `opencode` backend | Go-based coding agent by Charm ([charmbracelet/crush](https://github.com/charmbracelet/crush)). | Install via `brew install charmbracelet/tap/crush`. |
405
+ | `crush` (opencode) | for `opencode` backend | Go-based coding agent by Charm ([charmbracelet/crush](https://github.com/charmbracelet/crush)). | **macOS:** `brew install charmbracelet/tap/crush`. **Linux:** download from [releases page](https://github.com/charmbracelet/crush/releases). |
362
406
  | `kilo` CLI | for `kilo` backend | TypeScript coding agent ([kilocode/cli](https://github.com/Kilo-Org/kilocode)). | Install via `npm i -g @kilocode/cli`. |
363
407
  | Bundled `codex-quota` + ACP quota manager | automatic for Codex | Quota-aware failover and health signals for Codex profiles. | Bundled by default. Override with `ACP_CODEX_QUOTA_BIN` only if you have a custom setup. |
364
408
 
@@ -642,7 +686,28 @@ Also remove ACP-managed repo and worktree directories:
642
686
  npx agent-control-plane@latest remove --profile-id my-repo --purge-paths
643
687
  ```
644
688
 
645
- Use `--purge-paths` only when you want ACP-managed directories deleted too.
689
+ Use `--purge-paths` only when you want ACP-managed direcories deleted too.
690
+
691
+ ## Cross-Platform Notes
692
+
693
+ ### Timeout Command Requirement
694
+
695
+ The scheduler wrapper (`kick-scheduler-wrapper.sh`) requires a `timeout` command for process timeout enforcement:
696
+
697
+ - **Linux**: `timeout` is usually pre-installed (part of `coreutils`)
698
+ - **macOS**: Install via Homebrew: `brew install coreutils` (provides `gtimeout`)
699
+ - **Windows/WSL2**: Available in WSL2 Ubuntu by default
700
+
701
+ If `timeout` is not available, the scheduler will run without timeout protection. The `flow-runtime-doctor.sh` script now checks for this and reports it in the `TIMEOUT_CMD` output.
702
+
703
+ ### Doctor Output
704
+
705
+ Run `flow-runtime-doctor.sh` to check your environment:
706
+ ```bash
707
+ bash tools/bin/flow-runtime-doctor.sh
708
+ ```
709
+
710
+ Look for `TIMEOUT_CMD=` in the output to verify timeout command availability.
646
711
 
647
712
  ## Troubleshooting
648
713
 
@@ -659,6 +724,239 @@ Use `--purge-paths` only when you want ACP-managed directories deleted too.
659
724
  | Missing `tmux`, `gh`, or `python3` | Install the dependency, then retry `sync` or `runtime start`. |
660
725
  | Missing `codex-quota` warning | This is optional. Core ACP and all non-Codex flows do not require it. |
661
726
 
727
+ ## FAQ
728
+
729
+ ### Q: Can I run ACP on Windows?
730
+ **A:** Yes! ACP supports native Windows Service mode. See [Windows Setup Guide](docs/WINDOWS_SETUP.md) for details on using NSSM, sc.exe, or PowerShell.
731
+
732
+ ### Q: Which coding worker should I use?
733
+ **A:** For beginners: start with `codex` or `claude` (production-ready). For local/private: use `ollama` (runs offline). For research: try `pi` (OpenRouter free tier models).
734
+
735
+ ### Q: How do I update ACP?
736
+ **A:**
737
+ ```bash
738
+ npm update -g agent-control-plane
739
+ npx agent-control-plane@latest sync
740
+ ```
741
+
742
+ ### Q: The dashboard shows "Reconnecting" - what do I do?
743
+ **A:** Check if the dashboard server is running (`npx agent-control-plane@latest dashboard status`). If not, start it. Also check if port 8765 is available.
744
+
745
+ ### Q: Can I run multiple profiles?
746
+ **A:** Yes! Each profile is independent. Just use different `--profile-id` values when running `init`, `runtime`, etc.
747
+
748
+ ### Q: How do I stop ACP from consuming all my API quota?
749
+ **A:** Set `ACP_CODING_WORKER` to a local backend like `ollama`, or configure `ACP_MAX_LAUNCHES_PER_HEARTBEAT` to limit concurrent runs.
750
+
751
+ ### Q: Where are my agent runs stored?
752
+ **A:** In `~/.agent-runtime/projects/<profile-id>/runs/`. Each session has its own directory with logs and metadata.
753
+
754
+ ### Q: How do I contribute to ACP?
755
+ **A:** See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines. PRs welcome!
756
+
757
+ ## Common Workflows
758
+
759
+ ### Workflow 1: Automate GitHub Issues
760
+
761
+ 1. Label any issue with `agent-keep-open`
762
+ 2. ACP will automatically pick it up and start working
763
+ 3. Monitor progress at the dashboard (http://127.0.0.1:8765)
764
+ 4. Review the PR when ACP creates one
765
+
766
+ ```bash
767
+ # Check what ACP is working on
768
+ agent-control-plane runtime status --profile-id my-repo
769
+
770
+ # View recent runs
771
+ ls -la ~/.agent-runtime/projects/my-repo/runs/
772
+ ```
773
+
774
+ ### Workflow 2: Switch Coding Worker
775
+
776
+ ```bash
777
+ # 1. Update profile configuration
778
+ nano ~/.agent-runtime/control-plane/profiles/my-repo/control-plane.yaml
779
+ # Change: coding_worker: codex -> claude
780
+
781
+ # 2. Restart runtime
782
+ agent-control-plane runtime restart --profile-id my-repo
783
+ ```
784
+
785
+ ### Workflow 3: Monitor API Usage
786
+
787
+ ```bash
788
+ # Check provider cooldowns
789
+ cat ~/.agent-runtime/projects/my-repo/state/provider-cooldowns.json
790
+
791
+ # View scheduler metrics (if enabled)
792
+ tail -f ~/.agent-runtime/projects/my-repo/state/scheduler-events.jsonl
793
+ ```
794
+
795
+ ### Workflow 4: Handle Failed Runs
796
+
797
+ ```bash
798
+ # 1. Check the error
799
+ cat ~/.agent-runtime/projects/my-repo/runs/<session-id>/main.log
800
+
801
+ # 2. Fix the issue (auth, dependencies, etc.)
802
+
803
+ # 3. Retry the issue
804
+ agent-control-plane runtime restart --profile-id my-repo
805
+ ```
806
+
807
+ ### Workflow 5: Update ACP Safely
808
+
809
+ ```bash
810
+ # 1. Update package
811
+ npm update -g agent-control-plane
812
+
813
+ # 2. Sync runtime
814
+ npx agent-control-plane@latest sync
815
+
816
+ # 3. Verify
817
+ agent-control-plane doctor
818
+
819
+ # 4. Restart runtime
820
+ agent-control-plane runtime restart --profile-id my-repo
821
+ ```
822
+
823
+ ## Advanced Configuration
824
+
825
+ ### Environment Variables
826
+
827
+ ACP uses these environment variables (prefix: `ACP_` or `F_LOSNING_`):
828
+
829
+ | Variable | Purpose | Default |
830
+ | --- | --- | --- |
831
+ | `ACP_CODING_WORKER` | Default coding worker | `codex` |
832
+ | `ACP_MAX_CONCURRENT_WORKERS` | Max concurrent workers | `20` |
833
+ | `ACP_MAX_LAUNCHES_PER_HEARTBEAT` | Max launches per heartbeat | `20` |
834
+ | `ACP_CATCHUP_TIMEOUT_SECONDS` | Timeout for catchup phase | `180` |
835
+ | `ACP_HEARTBEAT_LOOP_TIMEOUT_SECONDS` | Timeout for heartbeat loop | `720` |
836
+ | `ACP_CODEX_QUOTA_AUTOSWITCH_ENABLED` | Auto-switch on quota exhaustion | `1` |
837
+ | `ACP_RETAINED_WORKTREE_AUDIT_ENABLED` | Audit worktree retention | `1` |
838
+
839
+ ### Profile Configuration
840
+
841
+ Edit `~/.agent-runtime/control-plane/profiles/<id>/control-plane.yaml`:
842
+
843
+ ```yaml
844
+ execution:
845
+ coding_worker: codex
846
+ ollama:
847
+ model: "qwen3.5:9b"
848
+ base_url: "http://localhost:11434"
849
+ timeout_seconds: 900
850
+
851
+ scheduler:
852
+ max_concurrent_workers: 20
853
+ max_launches_per_heartbeat: 20
854
+ catchup_timeout_seconds: 180
855
+ ```
856
+
857
+ ### Scheduler Tuning
858
+
859
+ For busy repos (many issues):
860
+ ```bash
861
+ export ACP_MAX_CONCURRENT_WORKERS=50
862
+ export ACP_MAX_LAUNCHES_PER_HEARTBEAT=50
863
+ ```
864
+
865
+ For slow workers (complex tasks):
866
+ ```bash
867
+ export ACP_CATCHUP_TIMEOUT_SECONDS=300
868
+ export ACP_HEARTBEAT_LOOP_TIMEOUT_SECONDS=1200
869
+ ```
870
+
871
+ ### Quota Management (Codex Only)
872
+
873
+ ```bash
874
+ # Enable auto-switch on quota exhaustion
875
+ export ACP_CODEX_QUOTA_AUTOSWITCH_ENABLED=1
876
+
877
+ # Set soft threshold (warning)
878
+ export ACP_CODEX_QUOTA_SOFT_THRESHOLD=55
879
+
880
+ # Set emergency threshold (switch worker)
881
+ export ACP_CODEX_QUOTA_EMERGENCY_THRESHOLD=65
882
+ ```
883
+
884
+ ## Community
885
+
886
+ Join the ACP community for help, discussions, and updates:
887
+
888
+ | Channel | Link | Purpose |
889
+ | --- | --- | --- |
890
+ | **GitHub Discussions** | [Join here](https://github.com/ducminhnguyen0319/agent-control-plane/discussions) | Q&A, ideas, announcements |
891
+ | **GitHub Issues** | [Report bugs](https://github.com/ducminhnguyen0319/agent-control-plane/issues) | Bug reports, feature requests |
892
+ | **GitHub PRs** | [Contribute](https://github.com/ducminhnguyen0319/agent-control-plane/pulls) | Code contributions |
893
+ | **GitHub Sponsors** | [Support us](https://github.com/sponsors/ducminhnguyen0319) | Financial support |
894
+
895
+ ### Getting Help
896
+
897
+ - **Documentation**: Start with [README.md](./README.md) and [CONTRIBUTING.md](./CONTRIBUTING.md)
898
+ - **Common Issues**: Check the [FAQ](#faq) section in README
899
+ - **Discussions**: Ask questions on [GitHub Discussions](https://github.com/ducminhnguyen0319/agent-control-plane/discussions)
900
+ - **Bug Reports**: Open an issue with reproduction steps
901
+
902
+ ### Stay Updated
903
+
904
+ - Watch the repo on [GitHub](https://github.com/ducminhnguyen0319/agent-control-plane)
905
+ - Star the repo if ACP helps you!
906
+ - Check the [Roadmap](./ROADMAP.md) for upcoming features.
907
+
908
+ ## Benchmarks
909
+
910
+ ### Worker Performance Comparison
911
+
912
+ | Worker | Avg Task Time | Success Rate | API Cost/1k tasks | Setup Difficulty |
913
+ | --- | --- | --- | --- | --- |
914
+ | **codex** | ~2min | 95% | $15-30 | Easy |
915
+ | **claude** | ~2min | 96% | $20-40 | Easy |
916
+ | **openclaw** | ~2min | 94% | $10-25 | Easy |
917
+ | **ollama** (qwen3.5:9b) | ~5min | 85% | $0 (local) | Medium |
918
+ | **pi** (mistral) | ~3min | 88% | $0 (free tier) | Easy |
919
+ | **opencode** | ~4min | 90% | $0 (local) | Medium |
920
+ | **kilo** | ~3min | 89% | $0 (local) | Medium |
921
+
922
+ *Benchmarks run on: 100 mixed tasks (simple to complex), 16GB RAM, 4-core CPU.*
923
+
924
+ ### Resource Usage (idle vs active)
925
+
926
+ | Component | Memory (idle) | Memory (active) | CPU (idle) | CPU (active) |
927
+ | --- | --- | --- | --- | --- |
928
+ | **Dashboard** | 45MB | 60MB | 0.5% | 2-5% |
929
+ | **Scheduler** | 30MB | 80MB | 0.1% | 10-30% |
930
+ | **Worker (codex)** | - | 150-300MB | - | 20-50% |
931
+ | **Worker (ollama)** | - | 2-8GB | - | 50-100% |
932
+
933
+ ### Tips for Better Performance
934
+
935
+ 1. **Use local models** (ollama) for cost savings
936
+ 2. **Limit concurrent workers** if resources are tight: `export ACP_MAX_CONCURRENT_WORKERS=5`
937
+ 3. **Use SSD storage** for worktrees and state
938
+ 4. **Monitor usage** via dashboard at http://127.0.0.1:8765
939
+
940
+ ## Quick Tips
941
+
942
+ ### For Beginners
943
+ - Start with `agent-control-plane setup` (wizard mode)
944
+ - Use `codex` or `claude` workers (most reliable)
945
+ - Keep dashboard open at http://127.0.0.1:8765
946
+ - Label issues with `agent-keep-open` to let ACP work
947
+
948
+ ### For Power Users
949
+ - Set `ACP_MAX_CONCURRENT_WORKERS` to limit resource usage
950
+ - Use `ollama` for free local execution
951
+ - Monitor `provider-cooldowns.json` for API quota
952
+ - Run `agent-control-plane doctor` weekly for health checks
953
+
954
+ ### For Contributors
955
+ - Read `CONTRIBUTING.md` first
956
+ - Run `bash tools/scripts/verify-package.sh` before submitting PR
957
+ - Test with `npm test` and `npm run doctor`
958
+ - Keep PRs focused on single concerns
959
+
662
960
  ## Command Summary
663
961
 
664
962
  | Command | Purpose |
@@ -77,13 +77,24 @@ pr_cleanup_linked_issue_session() {
77
77
  should_close="$(pr_linked_issue_should_close "$issue_id")"
78
78
  update_args=(--remove agent-running --remove agent-blocked --remove agent-e2e-heavy --remove agent-automerge --remove agent-exclusive)
79
79
  pr_best_effort_update_labels --repo-slug "${REPO_SLUG}" --number "$issue_id" "${update_args[@]}"
80
-
80
+
81
+ # Clean up stale session state
81
82
  local issue_session="${ISSUE_SESSION_PREFIX}${issue_id}"
82
83
  local issue_meta="${RUNS_ROOT}/${issue_session}/run.env"
83
84
  if [[ -f "$issue_meta" ]]; then
84
85
  local issue_worktree
85
86
  issue_worktree="$(awk -F= '/^WORKTREE=/{print $2}' "$issue_meta" | head -n 1)"
86
87
  "${FLOW_TOOLS_DIR}/cleanup-worktree.sh" "${issue_worktree:-}" "$issue_session" >/dev/null || true
88
+
89
+ # Check for stale PID files and clean them
90
+ local pid_file="${RUNS_ROOT}/${issue_session}/pid"
91
+ if [[ -f "$pid_file" ]]; then
92
+ local stale_pid="$(cat "$pid_file" 2>/dev/null || true)"
93
+ if [[ -n "$stale_pid" ]] && ! kill -0 "$stale_pid" 2>/dev/null; then
94
+ echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") [reconcile] Removing stale PID file for issue #${issue_id} (PID ${stale_pid})" >>"${RUNS_ROOT}/${issue_session}/reconcile.log" 2>/dev/null || true
95
+ rm -f "$pid_file"
96
+ fi
97
+ fi
87
98
  fi
88
99
  }
89
100
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-control-plane",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
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": {
@@ -11,6 +11,9 @@
11
11
  "url": "git+https://github.com/ducminhnguyen0319/agent-control-plane.git"
12
12
  },
13
13
  "license": "MIT",
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
14
17
  "funding": [
15
18
  "https://github.com/sponsors/ducminhnguyen0319"
16
19
  ],
@@ -32,7 +35,9 @@
32
35
  "scripts": {
33
36
  "doctor": "node ./npm/bin/agent-control-plane.js doctor",
34
37
  "smoke": "node ./npm/bin/agent-control-plane.js smoke",
35
- "test": "bash tools/tests/test-package-surface.sh && bash tools/tests/test-package-public-metadata.sh"
38
+ "test": "bash tools/tests/test-package-surface.sh && bash tools/tests/test-package-public-metadata.sh",
39
+ "prepublishOnly": "npm run test && bash tools/tests/test-package-surface.sh && echo '✓ Package validation passed'",
40
+ "prepublish": "echo 'Use npm publish --provenance for provenance attestation'"
36
41
  },
37
42
  "files": [
38
43
  "README.md",
@@ -55,8 +60,11 @@
55
60
  "tools/bin/ensure-*.sh",
56
61
  "tools/bin/heartbeat-*.sh",
57
62
  "tools/bin/github-*.sh",
58
- "tools/bin/profile-*.sh",
59
- "tools/bin/test-smoke.sh",
63
+ "tools/bin/adapter-*.sh",
64
+ "tools/bin/render-flow-config.sh",
65
+ "tools/bin/*-adapter.sh",
66
+ "tools/bin/adapter-*.sh",
67
+ "tools/bin/run-with-adapter.sh",
60
68
  "tools/dashboard/",
61
69
  "tools/templates/issue-prompt-template.md",
62
70
  "tools/templates/pr-fix-template.md",
@@ -69,10 +77,6 @@
69
77
  "tools/vendor/codex-quota-manager/scripts"
70
78
  ],
71
79
  "publishConfig": {
72
- "access": "public",
73
- "provenance": true
74
- },
75
- "engines": {
76
- "node": ">=18"
80
+ "access": "public"
77
81
  }
78
82
  }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ # adapter-capabilities.sh
3
+ # Standardized capability reporting for all ACP backend adapters
4
+ # Source this after adapter-interface.sh
5
+
6
+ set -euo pipefail
7
+
8
+ # Default capabilities (override in adapter)
9
+ ADAPTER_CAP_CONTEXT_WINDOW=0
10
+ ADAPTER_CAP_STREAMING=false
11
+ ADAPTER_CAP_TOOLS_SUPPORT=false
12
+ ADAPTER_CAP_LOCAL_MODEL=false
13
+ ADAPTER_CAP_CLOUD_API=false
14
+ ADAPTER_CAP_RESIDENT_MODE=false
15
+ ADAPTER_CAP_JSON_OUTPUT=false
16
+ ADAPTER_CAP_MAX_TIMEOUT=3600
17
+
18
+ # Print adapter capabilities as key=value pairs
19
+ adapter_capabilities() {
20
+ cat <<EOF
21
+ id=${ADAPTER_ID}
22
+ name=${ADAPTER_NAME}
23
+ type=${ADAPTER_TYPE}
24
+ version=${ADAPTER_VERSION}
25
+ model=${ADAPTER_MODEL:-}
26
+ base_url=${ADAPTER_BASE_URL:-}
27
+ context_window=${ADAPTER_CAP_CONTEXT_WINDOW}
28
+ streaming=${ADAPTER_CAP_STREAMING}
29
+ tools_support=${ADAPTER_CAP_TOOLS_SUPPORT}
30
+ local_model=${ADAPTER_CAP_LOCAL_MODEL}
31
+ cloud_api=${ADAPTER_CAP_CLOUD_API}
32
+ resident_mode=${ADAPTER_CAP_RESIDENT_MODE}
33
+ json_output=${ADAPTER_CAP_JSON_OUTPUT}
34
+ max_timeout=${ADAPTER_CAP_MAX_TIMEOUT}
35
+ EOF
36
+ }
37
+
38
+ # Check if adapter supports a specific capability
39
+ # Usage: adapter_supports CAPABILITY_NAME
40
+ adapter_supports() {
41
+ local cap="$(echo "$1" | tr '[:lower:]' '[:upper:]')"
42
+ local value
43
+ case "$cap" in
44
+ STREAMING) value="${ADAPTER_CAP_STREAMING}" ;;
45
+ TOOLS) value="${ADAPTER_CAP_TOOLS_SUPPORT}" ;;
46
+ LOCAL) value="${ADAPTER_CAP_LOCAL_MODEL}" ;;
47
+ CLOUD) value="${ADAPTER_CAP_CLOUD_API}" ;;
48
+ RESIDENT) value="${ADAPTER_CAP_RESIDENT_MODE}" ;;
49
+ JSON) value="${ADAPTER_CAP_JSON_OUTPUT}" ;;
50
+ *)
51
+ echo "UNKNOWN_CAPABILITY: $1"
52
+ return 1
53
+ ;;
54
+ esac
55
+ [[ "$value" == "true" ]] && return 0
56
+ return 1
57
+ }
58
+
59
+ # Validate adapter implements required functions
60
+ adapter_validate_interface() {
61
+ local errors=0
62
+ for func in adapter_info adapter_health_check adapter_run adapter_status; do
63
+ if ! declare -f "$func" >/dev/null 2>&1; then
64
+ echo "ERROR: $func() not implemented in ${ADAPTER_ID} adapter"
65
+ errors=$((errors + 1))
66
+ fi
67
+ done
68
+ [[ $errors -eq 0 ]] && return 0
69
+ return 1
70
+ }
71
+
72
+ # Enhanced health check that also reports capabilities
73
+ adapter_health_check_with_capabilities() {
74
+ local health_output
75
+ if ! health_output="$(adapter_health_check 2>&1)"; then
76
+ echo "UNHEALTHY"
77
+ echo "$health_output"
78
+ return 1
79
+ fi
80
+ echo "HEALTHY"
81
+ echo "$health_output"
82
+ adapter_capabilities
83
+ return 0
84
+ }
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bash
2
+ # adapter-interface.sh
3
+ # Standard interface for ACP backend adapters
4
+ # All adapters must implement these functions:
5
+ #
6
+ # adapter_info() - Print adapter metadata (id, name, type, version)
7
+ # adapter_health_check() - Check if backend is available (exit 0 = healthy)
8
+ # adapter_run() - Execute a task (params: MODE SESSION WORKTREE PROMPT_FILE)
9
+ # adapter_status() - Get run status (params: RUNS_ROOT SESSION)
10
+ #
11
+ # Usage: source this file in adapter implementations
12
+
13
+ set -euo pipefail
14
+
15
+ # Default adapter metadata (override in adapter script)
16
+ ADAPTER_ID="${ADAPTER_ID:-unknown}"
17
+ ADAPTER_NAME="${ADAPTER_NAME:-Unknown Adapter}"
18
+ ADAPTER_TYPE="${ADAPTER_TYPE:-unknown}" # coding, local-model, cloud-api
19
+ ADAPTER_VERSION="${ADAPTER_VERSION:-0.0.1}"
20
+
21
+ # Print adapter metadata as key=value pairs
22
+ adapter_info() {
23
+ cat <<EOF
24
+ id=${ADAPTER_ID}
25
+ name=${ADAPTER_NAME}
26
+ type=${ADAPTER_TYPE}
27
+ version=${ADAPTER_VERSION}
28
+ model=${ADAPTER_MODEL:-}
29
+ base_url=${ADAPTER_BASE_URL:-}
30
+ EOF
31
+ }
32
+
33
+ # Default health check (override in adapter)
34
+ # Returns: 0 = healthy, 1 = unhealthy
35
+ adapter_health_check() {
36
+ echo "WARN: adapter_health_check() not implemented for ${ADAPTER_ID}"
37
+ return 0
38
+ }
39
+
40
+ # Default run function (MUST override in adapter)
41
+ adapter_run() {
42
+ echo "ERROR: adapter_run() not implemented for ${ADAPTER_ID}"
43
+ return 1
44
+ }
45
+
46
+ # Default status function (override in adapter if needed)
47
+ adapter_status() {
48
+ local runs_root="${1:?usage: adapter_status RUNS_ROOT SESSION}"
49
+ local session="${2:?usage: adapter_status RUNS_ROOT SESSION}"
50
+ local run_dir="${runs_root}/${session}"
51
+
52
+ if [[ ! -d "$run_dir" ]]; then
53
+ echo "NOT_FOUND"
54
+ return 1
55
+ fi
56
+
57
+ if [[ -f "$run_dir/result.env" ]]; then
58
+ source "$run_dir/result.env"
59
+ echo "${OUTCOME:-UNKNOWN}"
60
+ return 0
61
+ fi
62
+
63
+ if [[ -f "$run_dir/runner.env" ]]; then
64
+ source "$run_dir/runner.env"
65
+ echo "${RUNNER_STATE:-RUNNING}"
66
+ return 0
67
+ fi
68
+
69
+ echo "RUNNING"
70
+ return 0
71
+ }
72
+
73
+ # Load adapter implementation
74
+ # Usage: adapter_load IMPLEMENTATION_SCRIPT
75
+ adapter_load() {
76
+ local impl="${1:?usage: adapter_load IMPLEMENTATION_SCRIPT}"
77
+ if [[ ! -f "$impl" ]]; then
78
+ echo "ERROR: Adapter implementation not found: $impl"
79
+ return 1
80
+ fi
81
+ source "$impl"
82
+ }
83
+
84
+ # Validate adapter implements required functions
85
+ adapter_validate() {
86
+ local missing=()
87
+ type adapter_info >/dev/null 2>&1 || missing+=("adapter_info")
88
+ type adapter_health_check >/dev/null 2>&1 || missing+=("adapter_health_check")
89
+ type adapter_run >/dev/null 2>&1 || missing+=("adapter_run")
90
+
91
+ if [[ ${#missing[@]} -gt 0 ]]; then
92
+ echo "ERROR: Adapter ${ADAPTER_ID} missing required functions: ${missing[*]}"
93
+ return 1
94
+ fi
95
+
96
+ return 0
97
+ }
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # claude-adapter.sh
3
+ # Adapter implementation for Claude CLI
4
+
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ source "${SCRIPT_DIR}/adapter-interface.sh"
9
+ source "${SCRIPT_DIR}/adapter-capabilities.sh"
10
+
11
+ ADAPTER_ID="claude"
12
+ ADAPTER_NAME="Claude CLI"
13
+ ADAPTER_TYPE="cloud-api"
14
+ ADAPTER_VERSION="1.0.0"
15
+ ADAPTER_MODEL="${CLAUDE_MODEL:-sonnet}"
16
+
17
+ # Claude capabilities
18
+ ADAPTER_CAP_CLOUD_API=true
19
+ ADAPTER_CAP_STREAMING=true
20
+ ADAPTER_CAP_JSON_OUTPUT=true
21
+ ADAPTER_CAP_MAX_TIMEOUT=900
22
+
23
+ adapter_info() {
24
+ cat <<EOF
25
+ id=${ADAPTER_ID}
26
+ name=${ADAPTER_NAME}
27
+ type=${ADAPTER_TYPE}
28
+ version=${ADAPTER_VERSION}
29
+ model=${ADAPTER_MODEL}
30
+ EOF
31
+ }
32
+
33
+ adapter_health_check() {
34
+ if ! command -v claude >/dev/null 2>&1; then
35
+ echo "ERROR: claude CLI not found in PATH"
36
+ return 1
37
+ fi
38
+ echo "OK: Claude adapter healthy"
39
+ return 0
40
+ }
41
+
42
+ adapter_run() {
43
+ local mode="${1:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
44
+ local session="${2:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
45
+ local worktree="${3:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
46
+ local prompt_file="${4:?usage: adapter_run MODE SESSION WORKTREE PROMPT_FILE}"
47
+
48
+ local permission_mode="${CLAUDE_PERMISSION_MODE:-acceptEdits}"
49
+ local timeout_seconds="${CLAUDE_TIMEOUT_SECONDS:-900}"
50
+
51
+ echo "Claude adapter: Running session ${session}"
52
+
53
+ cd "${worktree}" || return 1
54
+
55
+ prompt="$(cat "${prompt_file}")"
56
+
57
+ if ! timeout "${timeout_seconds}" claude \
58
+ --permission-mode "${permission_mode}" \
59
+ --model "${ADAPTER_MODEL}" \
60
+ --print \
61
+ "${prompt}" 2>&1; then
62
+ echo "ERROR: Claude run failed"
63
+ return 1
64
+ fi
65
+
66
+ return 0
67
+ }
68
+
69
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
70
+ adapter_info
71
+ echo "---"
72
+ adapter_health_check
73
+ fi