Skip to content

feat(cli): migrate ACP runtime to acp-kit with agent management - #8

Merged
soorya-u merged 16 commits into
mainfrom
feature/cli-acp-manager
Jul 2, 2026
Merged

soorya-u merged 16 commits into
mainfrom
feature/cli-acp-manager

Conversation

@soorya-u

@soorya-u soorya-u commented Jul 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Migrate the CLI worker ACP layer from a hand-rolled client/process manager to @acp-kit/core, organized under core/{acp,agents,threads}
  • Add agent registry management (cyrusd agents add/list/update/rm/doctor) backed by ~/.cyrus/agents.yml
  • Extend the controller contract with agent config RPCs (models/modes/efforts/persona get/set), listProjects, shared chat event schemas, and projectId-based chat input
  • Wire the worker controller and web threads route to the new ThreadCoordinator + AgentPool runtime

Test plan

  • bun check and bun check:types pass
  • cyrusd agents add <name> --cmd <agent> registers an agent
  • cyrusd agents doctor <name> completes ACP initialize successfully
  • cyrusd start worker connects and serves controller RPCs
  • Web threads page can send chat with projectId: "default" and receive streamed events
  • Agent config getters/setters (getModels, setMode, etc.) work against a live agent

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added CLI commands to list, add, update, remove, and inspect agents.
    • Introduced agent-driven chat streaming with richer event updates.
    • Added project awareness and support for choosing models, modes, effort, and personas.
    • Added new environment settings for ACP timeouts, idle shutdown, and stream throttling.
  • Bug Fixes

    • Standardized command names and validation for CLI inputs.
    • Improved handling of agent availability, health checks, and shutdown behavior.
    • Updated chat delivery to work with structured events instead of raw chunks.

soorya-u and others added 14 commits July 1, 2026 11:04
Move OpenSpec slash commands to flat opsx-*.md files for dotagents deploy,
add Cursor as a deploy target, and ignore generated .agents/ and .cursor/ dirs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Define the process manager, session router, agent config, and CLI
requirements for running ACP agents in the Cyrus worker.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace hello with listAgents and add agentName, threadId, and cwd to
the chat input schema so controllers can target registered ACP agents.

Co-authored-by: Cursor <cursoragent@cursor.com>
Relocate device config persistence to store/config, extract name and
agent validators, and add small io/error helpers used by ACP runtime.

Co-authored-by: Cursor <cursoragent@cursor.com>
Persist registered ACP agents in agents.yml and expose add, list, update,
rm, and doctor subcommands under cyrusd agents.

Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce the ACP SDK integration with subprocess lifecycle management,
health pings, and per-thread session routing for agent prompts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Route chat prompts through the session router and expose listAgents so
controllers can discover registered agents on the local worker.

Co-authored-by: Cursor <cursoragent@cursor.com>
…input

Extend the controller contract with model/mode/effort/persona getters and setters
plus listProjects, and define shared chat event schemas for streaming agent output.

Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce acp-kit as the dependency for subprocess transport, session management,
and normalized agent runtime events.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the hand-rolled ACP client and process manager with acp-kit-backed
modules under core/{acp,agents,threads}, including pool lifecycle, event mapping,
thread coordination, and a mock projectId-to-cwd resolver.

Co-authored-by: Cursor <cursoragent@cursor.com>
Connect controller RPC handlers to the new runtime, route chat through
threadCoordinator, and update doctor ping to use the acp-kit health check.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align the web thread caller with the updated RTC chat input schema.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Jul 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cyrus Ready Ready Preview, Comment Jul 2, 2026 4:29pm

@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@soorya-u, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1386fcda-8e40-4df6-a92d-8e6607e67be7

📥 Commits

Reviewing files that changed from the base of the PR and between be7945f and 82c1872.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (28)
  • apps/cli/package.json
  • apps/cli/src/commands/agents/doctor.ts
  • apps/cli/src/commands/agents/list.ts
  • apps/cli/src/commands/agents/update.ts
  • apps/cli/src/commands/auth/login.ts
  • apps/cli/src/commands/auth/logout.ts
  • apps/cli/src/commands/service/worker.ts
  • apps/cli/src/core/acp/host.ts
  • apps/cli/src/core/acp/ping.ts
  • apps/cli/src/core/acp/pool.ts
  • apps/cli/src/core/agents/profile.ts
  • apps/cli/src/core/agents/runtime.ts
  • apps/cli/src/core/index.ts
  • apps/cli/src/handlers/controller.ts
  • apps/cli/src/mocks/projects.ts
  • apps/cli/src/store/agents.ts
  • apps/cli/src/utils/command.ts
  • apps/cli/src/utils/io.ts
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/.openspec.yaml
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/design.md
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/proposal.md
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/specs/acp-process-manager/spec.md
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/specs/acp-provider-cli/spec.md
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/specs/acp-provider-config/spec.md
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/specs/acp-session-router/spec.md
  • openspec/changes/archive/2026-07-02-acp-provider-runtime/tasks.md
  • openspec/specs/acp-process-manager/spec.md
  • shared/connections/src/schemas/chat.ts
📝 Walkthrough

Walkthrough

This PR introduces an ACP (Agent Client Protocol) provider runtime for the Cyrus CLI: new schemas and controller contract endpoints for agents/projects/models/modes, a YAML-backed agent registry, an AgentPool managing subprocess lifecycles, AgentRuntime/ThreadCoordinator for session prompting, new CLI agent commands, controller router rewiring, web chat client updates to typed AgentEvents, config-import path migrations, and OpenSpec documentation.

Changes

ACP Provider Runtime

Layer / File(s) Summary
Shared agent/chat schemas and controller contract
shared/connections/src/schemas/agents.ts, shared/connections/src/schemas/chat.ts, shared/connections/src/schemas/rtc.ts, shared/connections/src/contracts/controller.ts
Adds Zod schemas for agents/projects/select options and structured AgentEvents; updates rtc chat schemas and expands controllerContract with agent-related endpoints, removing hello.
Agent registry validators and persistence
apps/cli/src/validators/*.ts, apps/cli/src/store/agents.ts, apps/cli/src/constants/file.ts, apps/cli/src/utils/error.ts, apps/cli/src/utils/io.ts, apps/cli/package.json
Adds command/name validation, AgentEntry schema, a YAML-backed agents registry with CRUD and availability discovery, and new dependencies.
ACP process pooling and event mapping
apps/cli/src/core/acp/*.ts
Implements session config helper, runtime event/session-update mappers, default permission host, doctor ping, tracked transport, and AgentPool lifecycle management.
Agent/thread runtime and catalog coordination
apps/cli/src/core/agents/*.ts, apps/cli/src/core/threads/coordinator.ts, apps/cli/src/core/index.ts, apps/cli/src/mocks/projects.ts
Adds catalog derivation of models/modes/efforts/personas, AgentRuntime per-thread session management and prompt streaming, ThreadCoordinator, createWorkerRuntime, and project mock registry.
Controller router and worker service wiring
apps/cli/src/handlers/controller.ts, apps/cli/src/commands/service/worker.ts, apps/cli/src/lib/env.ts
Replaces controllerRouter with createControllerRouter(runtime), wires worker runtime and agent pool shutdown, adds new ACP-related env vars.
CLI agent commands and config store migration
apps/cli/src/commands/agents/*.ts, apps/cli/src/index.ts, apps/cli/src/commands/auth/*.ts, apps/cli/src/commands/config/*.ts, apps/cli/src/commands/service/status.ts, apps/cli/src/store/config.ts, apps/cli/src/lib/auth.ts
Adds agents CLI subcommands and migrates several commands' store imports to @/store/config.
Web chat client AgentEvent integration
apps/web/src/routes/threads/index.tsx
Updates chat streaming to consume typed AgentEvents instead of raw chunk text, passing agentName/projectId in requests.
OpenSpec design and specification documents
openspec/changes/acp-provider-runtime/*, openspec/specs/*
Adds proposal, design, task list, and spec documents for the ACP provider runtime.

Repository tooling and config tweaks

Layer / File(s) Summary
Dotagents metadata and gitignore updates
.dotagents/commands/opsx-*.md, .dotagents/config.toml, .gitignore
Renames command metadata names to slug form, extends config targets with cursor, and expands ignored directories.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • soorya-u/cyrus#6: Modifies the same WebRTC controller/chat schemas and apps/web/src/routes/threads/index.tsx, an earlier stage of the chat payload transition this PR builds on.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: migrating the CLI ACP runtime to acp-kit and adding agent management.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/cli-acp-manager

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/soorya-u/cyrus/issues/comments/4867594298","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- review_stack_entry_start -->\n\n[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/soorya-u/cyrus/pull/8?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)\n\n<!-- review_stack_entry_end -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: defaults\n> \n> **Review profile**: CHILL\n> \n> **Plan**: Pro\n> \n> **Run ID**: `1fae5a2f-56b4-487d-b531-a8311ca8a02d`\n> \n> </details>\n> \n> <details>\n> <summary>📥 Commits</summary>\n> \n> Reviewing files that changed from the base of the PR and between 1265c673233f472ce37eb587f2262ac8ff11b730 and be7945f9bd334d8dca59da155a609c2b7fdebe8a.\n> \n> </details>\n> \n> <details>\n> <summary>⛔ Files ignored due to path filters (1)</summary>\n> \n> * `bun.lock` is excluded by `!**/*.lock`\n> \n> </details>\n> \n> <details>\n> <summary>📒 Files selected for processing (65)</summary>\n> \n> * `.dotagents/commands/opsx-apply.md`\n> * `.dotagents/commands/opsx-archive.md`\n> * `.dotagents/commands/opsx-continue.md`\n> * `.dotagents/commands/opsx-explore.md`\n> * `.dotagents/commands/opsx-new.md`\n> * `.dotagents/commands/opsx-propose.md`\n> * `.dotagents/commands/opsx-sync.md`\n> * `.dotagents/commands/opsx-verify.md`\n> * `.dotagents/config.toml`\n> * `.gitignore`\n> * `apps/cli/package.json`\n> * `apps/cli/src/commands/agents/add.ts`\n> * `apps/cli/src/commands/agents/doctor.ts`\n> * `apps/cli/src/commands/agents/index.ts`\n> * `apps/cli/src/commands/agents/list.ts`\n> * `apps/cli/src/commands/agents/rm.ts`\n> * `apps/cli/src/commands/agents/update.ts`\n> * `apps/cli/src/commands/auth/login.ts`\n> * `apps/cli/src/commands/auth/logout.ts`\n> * `apps/cli/src/commands/auth/whoami.ts`\n> * `apps/cli/src/commands/config/index.ts`\n> * `apps/cli/src/commands/config/rename.ts`\n> * `apps/cli/src/commands/service/status.ts`\n> * `apps/cli/src/commands/service/worker.ts`\n> * `apps/cli/src/constants/file.ts`\n> * `apps/cli/src/core/acp/config.ts`\n> * `apps/cli/src/core/acp/events.ts`\n> * `apps/cli/src/core/acp/host.ts`\n> * `apps/cli/src/core/acp/ping.ts`\n> * `apps/cli/src/core/acp/pool.ts`\n> * `apps/cli/src/core/acp/transport.ts`\n> * `apps/cli/src/core/agents/catalog.ts`\n> * `apps/cli/src/core/agents/profile.ts`\n> * `apps/cli/src/core/agents/runtime.ts`\n> * `apps/cli/src/core/index.ts`\n> * `apps/cli/src/core/threads/coordinator.ts`\n> * `apps/cli/src/handlers/controller.ts`\n> * `apps/cli/src/index.ts`\n> * `apps/cli/src/lib/auth.ts`\n> * `apps/cli/src/lib/env.ts`\n> * `apps/cli/src/mocks/projects.ts`\n> * `apps/cli/src/store/agents.ts`\n> * `apps/cli/src/store/config.ts`\n> * `apps/cli/src/utils/error.ts`\n> * `apps/cli/src/utils/io.ts`\n> * `apps/cli/src/validators/acp.ts`\n> * `apps/cli/src/validators/agent.ts`\n> * `apps/cli/src/validators/name.ts`\n> * `apps/web/src/routes/threads/index.tsx`\n> * `openspec/changes/acp-provider-runtime/.openspec.yaml`\n> * `openspec/changes/acp-provider-runtime/design.md`\n> * `openspec/changes/acp-provider-runtime/proposal.md`\n> * `openspec/changes/acp-provider-runtime/specs/acp-process-manager/spec.md`\n> * `openspec/changes/acp-provider-runtime/specs/acp-provider-cli/spec.md`\n> * `openspec/changes/acp-provider-runtime/specs/acp-provider-config/spec.md`\n> * `openspec/changes/acp-provider-runtime/specs/acp-session-router/spec.md`\n> * `openspec/changes/acp-provider-runtime/tasks.md`\n> * `openspec/specs/acp-process-manager/spec.md`\n> * `openspec/specs/acp-provider-cli/spec.md`\n> * `openspec/specs/acp-provider-config/spec.md`\n> * `openspec/specs/acp-session-router/spec.md`\n> * `shared/connections/src/contracts/controller.ts`\n> * `shared/connections/src/schemas/agents.ts`\n> * `shared/connections/src/schemas/chat.ts`\n> * `shared/connections/src/schemas/rtc.ts`\n> \n> </details>\n> \n> ```ascii\n>  _____________________\n> < 🧯 Bug extinguished. >\n>  ---------------------\n>   \\\n>    \\   (\\__/)\n>        (•ㅅ•)\n>        /   づ\n> ```\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>📝 Generate docstrings</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> Create stacked PR\n- [ ] <!-- {\"checkboxId\": \"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98\"} --> Commit on current branch\n\n</details>\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feature/cli-acp-manager`\n\n</details>\n<details open>\n<summary>⚔️ Resolve merge conflicts</summary>\n\n- [ ] <!-- {\"checkboxId\": \"c3a5b2e1-4d7f-4a8c-b9d6-e1f2c3d4a5b6\"} --> Resolve merge conflict in branch `feature/cli-acp-manager`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=soorya-u/cyrus&utm_content=8)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

Resolve modify/delete conflict by dropping the legacy /threads route; main
replaced it with the workspace shell that already routes by projectId.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Nitpick comments (9)
apps/cli/src/validators/acp.ts (1)

7-25: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated zod→InvalidArgumentError pattern.

commandArgParser and nameArgParser repeat identical safeParse/throw boilerplate. Consider a small generic helper to reduce duplication.

♻️ Suggested consolidation
+function zodArgParser<T>(schema: z.ZodType<T>, fallbackMessage: string) {
+	return (value: string): T => {
+		const result = schema.safeParse(value);
+		if (!result.success) {
+			throw new InvalidArgumentError(
+				result.error.issues[0]?.message ?? fallbackMessage
+			);
+		}
+		return result.data;
+	};
+}
+
+export const commandArgParser = zodArgParser(commandSchema, "invalid command");
+export const nameArgParser = zodArgParser(nameSchema, "invalid name");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/validators/acp.ts` around lines 7 - 25, Both commandArgParser
and nameArgParser duplicate the same safeParse-to-InvalidArgumentError flow.
Extract the repeated validation/throw logic into a small shared helper that
accepts a schema and fallback message, then have commandArgParser and
nameArgParser delegate to it while preserving their current messages and return
types. Use the existing symbols commandSchema, nameSchema, and
InvalidArgumentError to keep the refactor localized.
apps/cli/src/commands/agents/update.ts (1)

22-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant re-merge in updateAgent.

entry is already fully merged with existing before being passed to updateAgent, which merges again (partial.command ?? existing.command) per the store snippet. Not a bug, but the double merge is unnecessary — mainly needed here because entry doubles as the display payload for the success message.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/commands/agents/update.ts` around lines 22 - 38, The
`updateAgent` call in `agents/update.ts` is redundantly merging data twice
because `entry` is already fully resolved from `existing`. Adjust the `update`
command so `entry` is only used as the display payload for the success message,
and pass a partial patch object into `updateAgent` instead of the already merged
`AgentEntry`. Keep the success output logic tied to the local `entry` variable,
and ensure the `saved.match` handling remains unchanged.
apps/cli/src/commands/config/index.ts (1)

2-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Cross-context reuse of nameArgParser from validators/acp.ts.

Device rename names now share validation with agent names via nameArgParser, which is defined in acp.ts — a file otherwise scoped to ACP agent command/name validation. Not incorrect, but the naming/location may confuse future readers since this validator is now used outside the ACP domain.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/commands/config/index.ts` around lines 2 - 9, The
`registerConfigCommands` rename flow is reusing `nameArgParser` from
`validators/acp`, but that validator is now shared outside ACP and its current
name/location is misleading. Move the shared name validation to a more generic
validator module or rename/export it to reflect broader device-name use, and
update the `registerConfigCommands` import so the `rename` command clearly
points to a cross-domain validator.
apps/cli/src/commands/agents/add.ts (1)

1-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate result-handling pattern across agent commands.

The saved.match({ ok: ..., err: (message) => { print.error...; process.exit(1); } }) pattern is repeated verbatim in rm.ts (and likely update.ts). Consider extracting a shared helper to avoid drift.

♻️ Suggested helper
// apps/cli/src/utils/cli.ts
import { print } from "`@/utils/style`";
import type { Result } from "better-result";

export function printResultOrExit<T>(
	result: Result<T, string>,
	onOk: (value: T) => void
): void {
	result.match({
		ok: onOk,
		err: (message) => {
			print.error`${message}`;
			process.exit(1);
		},
	});
}
-	saved.match({
-		ok: () => {
-			const args = entry.args.length > 0 ? ` ${entry.args.join(" ")}` : "";
-			print.success`✓ added agent "${name}" with command ${entry.command}${args}`;
-		},
-		err: (message) => {
-			print.error`${message}`;
-			process.exit(1);
-		},
-	});
+	printResultOrExit(saved, () => {
+		const args = entry.args.length > 0 ? ` ${entry.args.join(" ")}` : "";
+		print.success`✓ added agent "${name}" with command ${entry.command}${args}`;
+	});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/commands/agents/add.ts` around lines 1 - 17, The result-handling
in add() duplicates the same ok/err pattern used by other agent commands, so
extract it into a shared helper to keep behavior consistent. Create a reusable
function like printResultOrExit in a common CLI utility module, then update
add() to use it with the addAgent result and keep the success callback for the
success message while centralizing the error printing and process.exit(1) path.
apps/cli/src/core/acp/pool.ts (1)

99-149: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Type the placeholder runtime field as optional instead of an unsafe cast.

runtime: undefined as unknown as AcpRuntime (line 67, referenced from getRuntime) silences the type checker rather than modeling the actual optionality. Consider runtime?: AcpRuntime on ManagedAgent to make the "not yet booted" state type-safe.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/core/acp/pool.ts` around lines 99 - 149, The ManagedAgent
placeholder runtime is being modeled with an unsafe cast instead of the actual
“not yet booted” state. Update the ManagedAgent shape used by getRuntime and
bootRuntime so runtime is optional (runtime?: AcpRuntime), then adjust the
initialization and assignment sites in bootRuntime, resetIdleTimer, and
stopAgent to work with the optional field safely without using undefined as
unknown as AcpRuntime.
apps/cli/src/core/acp/events.ts (1)

54-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Merge identical tool.update/tool.end cases.

Both branches are byte-for-byte identical. Combine the case labels to avoid future divergence.

♻️ Proposed refactor
-		case "tool.update":
-			return [
-				ToolCallUpdateEventSchema.parse({
-					type: "tool_call_update",
-					toolCallId: event.toolCallId,
-					title: event.title,
-					status: mapToolStatus(event.status),
-					content: event.content,
-					rawOutput: event.output,
-				}),
-			];
-		case "tool.end":
+		case "tool.update":
+		case "tool.end":
			return [
				ToolCallUpdateEventSchema.parse({
					type: "tool_call_update",
					toolCallId: event.toolCallId,
					title: event.title,
					status: mapToolStatus(event.status),
					content: event.content,
					rawOutput: event.output,
				}),
			];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/core/acp/events.ts` around lines 54 - 75, The `tool.update` and
`tool.end` branches in `events.ts` are identical, so merge them into a single
shared handler to prevent drift. Update the `switch` in the event-mapping logic
so both case labels fall through to the same `ToolCallUpdateEventSchema.parse`
return path, keeping the existing `mapToolStatus`, `toolCallId`, `title`,
`content`, and `rawOutput` mapping unchanged.
apps/cli/src/core/agents/catalog.ts (1)

82-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Persona-matching fallback duplicated with runtime.ts.

The category/id substring fallback here (option.category?.includes("persona") || option.id.toLowerCase().includes("persona")) is duplicated verbatim in AgentRuntime.setPersona (apps/cli/src/core/agents/runtime.ts, shown in context snippet 3). If the heuristic ever needs a tweak, one copy will silently drift from the other.

Consider exporting a single findPersonaConfigOption/findPersonaConfigOptionId helper from this file and having runtime.ts consume it instead of re-implementing the match.

♻️ Suggested consolidation
+export function findPersonaConfigOption(
+	options: SessionConfigOption[]
+): Extract<SessionConfigOption, { type: "select" }> | undefined {
+	const match =
+		findSelectConfigOption(options, "persona") ??
+		options.find(
+			(option) =>
+				option.type === "select" &&
+				(option.category?.includes("persona") ||
+					option.id.toLowerCase().includes("persona"))
+		);
+	return match?.type === "select" ? match : undefined;
+}
+
 export function configOptionsToPersonas(
 	options: SessionConfigOption[]
 ): SelectOption[] {
-	const config =
-		findSelectConfigOption(options, "persona") ??
-		options.find(
-			(option) =>
-				option.type === "select" &&
-				(option.category?.includes("persona") ||
-					option.id.toLowerCase().includes("persona"))
-		);
-	if (!config || config.type !== "select") return [];
+	const config = findPersonaConfigOption(options);
+	if (!config) return [];
 	return selectOptionsToList(config.options);
 }

Then in runtime.ts, replace the inline fallback with findPersonaConfigOption(probe.transcript.session.configOptions)?.id.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/core/agents/catalog.ts` around lines 82 - 95, The persona
fallback matching logic is duplicated between configOptionsToPersonas and
AgentRuntime.setPersona, so the heuristic can drift over time. Extract the
shared category/id matching into a single helper such as findPersonaConfigOption
or findPersonaConfigOptionId in catalog.ts, then update runtime.ts to call that
helper instead of re-implementing the inline fallback. Keep the existing
configOptionsToPersonas flow intact and reuse the shared helper wherever persona
config lookup is needed.
apps/cli/src/handlers/controller.ts (2)

77-95: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No error handling around the prompt generator.

If runtime.threadCoordinator.prompt(...) throws mid-stream (agent crash, resolveProjectCwd failure for an unknown project, etc.), the generator just rejects with no broadcast/cleanup — subscribed peers (context.broadcaster) get no notice that the stream ended abnormally. Consider wrapping the loop in try/catch (or try/finally) to broadcast a terminal error event before rethrowing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/handlers/controller.ts` around lines 77 - 95, The chat handler
in controller.ts does not handle failures from
runtime.threadCoordinator.prompt(...) or the async iteration, so a mid-stream
exception can end the generator silently without notifying subscribers. Wrap the
prompt/gen loop in try/catch (and optionally finally) inside the chat handler,
catch errors from the prompt generator or for-await loop, broadcast a terminal
error/abort event through context.broadcaster before rethrowing, and ensure the
stream exits cleanly for peers.

77-95: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Unconditional per-event sleep may add significant latency to chatty streams.

Every yielded AgentEvent (including per-token/thought chunks) is followed by await Bun.sleep(env.CYRUS_STREAM_THROTTLING_MS) — for a response with hundreds of token events at the default 25ms, that's several seconds of added latency purely from throttling. Consider throttling by wall-clock interval (e.g., only sleep if less than N ms elapsed since the last flush) rather than sleeping after every single yield, so throttling scales with event volume instead of compounding linearly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cli/src/handlers/controller.ts` around lines 77 - 95, The chat handler
in os.chat.handler applies an unconditional Bun.sleep after every yielded event,
which adds latency linearly for chatty streams. Update the streaming loop around
runtime.threadCoordinator.prompt and context.broadcaster.broadcast to throttle
by elapsed wall-clock time instead of per event, so the delay only happens when
the last flush was too recent. Keep the existing stream behavior, but gate the
sleep using a last-flush timestamp and env.CYRUS_STREAM_THROTTLING_MS.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/cli/src/commands/service/worker.ts`:
- Around line 52-60: The shutdown handler exits the process before agent
termination finishes, so AgentPool.shutdown() needs to be awaited instead of
fire-and-forget. Update runtime.agentPool.shutdown() in the shutdown flow to
return a Promise<void> from AgentPool.shutdown() by waiting for all stopAgent
calls to settle, then make the shutdown handler async and await
runtime.agentPool.shutdown() before calling device.close(),
signalingSession.close(), and process.exit(0). Use the AgentPool.shutdown and
stopAgent symbols to locate the affected flow.

In `@apps/cli/src/core/acp/host.ts`:
- Around line 1-23: The requestPermission logic in createDefaultHost is
incorrectly falling through to AllowOnce when the first available option is a
reject kind. Update the option selection so it only chooses allow_once or
allow_always from request.options, and return PermissionDecision.Deny when
neither exists. Keep the fix contained within createDefaultHost and its
requestPermission handler so reject-only option lists cannot be treated as
allowed.

In `@apps/cli/src/core/acp/ping.ts`:
- Around line 18-29: Wrap the createAcpRuntime setup in ping() so it cannot
throw synchronously and instead returns a Result.err string on failure. The
current construction path for createAcpRuntime in ping() can escape the
Result<AcpPingSuccess, string> contract that doctor.ts consumes via .match(), so
catch any exception around the runtime creation and convert it into an err
result with a descriptive message.
- Around line 31-36: The bare method references in ping.ts can lose the
AcpRuntime instance context when passed to Result.tryPromise. Update the
acp.ready and acp.shutdown calls in the ping flow to use thunks so the methods
are invoked with acp as the receiver, and apply the same safe pattern used
elsewhere in the ACP code (for example, the pool logic).

In `@apps/cli/src/core/acp/pool.ts`:
- Around line 93-97: `shutdown()` in `AgentPool` is fire-and-forget, so callers
cannot wait for `stopAgent` to finish terminating spawned processes. Update
`AgentPool.shutdown()` to return a Promise that resolves only after all agents
have been stopped, and make the implementation await the async `stopAgent(name)`
calls for every entry in `this.agents` instead of ignoring the returned
promises. This will let the worker/service shutdown path wait for the pool to
fully terminate before resolving.
- Around line 46-91: The getRuntime flow allows duplicate startups because the
agent is not marked in this.agents until after await this.getEntry(name)
completes. In AcpRuntimePool.getRuntime, register a placeholder entry for the
name immediately before any await, then reuse that entry’s startPromise for
concurrent calls so only one bootRuntime runs per agent. Keep the existing state
transitions and updates to runtime, state, and startPromise in the same
getRuntime/bootRuntime path.

In `@apps/cli/src/core/agents/runtime.ts`:
- Around line 37-117: The config/session methods in Runtime still use stale
cached sessions after an agent crash because only prompt() checks pool state and
recoverSessions() does not refresh probeSession. Update Runtime’s
getProbeSession() and requireSession() (or add a shared crash-check helper) so
they verify this.pool.getState(this.agentName) and call recoverSessions() before
returning any cached RuntimeSession, and ensure probeSession is rebuilt
alongside sessions when recovery happens. Apply the same guard to getModels,
getModes, getEfforts, getPersonas, setModel, setMode, setEffort, and setPersona
so they always bind to a live subprocess.

In `@apps/cli/src/handlers/controller.ts`:
- Around line 78-83: The chat flow in controller.ts generates a fallback
threadId server-side when it is omitted, but that value is never returned to the
caller, so the client cannot resume or manage the thread later. Update the
handling around the chat/controller path (including the chat result construction
and any response type/schema used by controller.ts and AgentEventSchema) so the
generated threadId is included in the returned payload or initial event, while
preserving existing AgentEvent emission behavior for the rest of the stream.

In `@apps/cli/src/mocks/projects.ts`:
- Around line 9-34: The mock project registry and cwd resolver need to ensure
the default project directory exists before it is returned, because
ThreadCoordinator uses resolveProjectCwd and runtime.newSession({ cwd }) can
fail if /tmp/cyrus-agent-test is missing. Update the projects mock around
DEFAULT_PROJECT_ID, listProjects, and resolveProjectCwd to create the cwd on
demand (or initialize it during module setup) before returning it, and make sure
any lookup for non-default project IDs is handled consistently with the mock’s
intended behavior.

In `@apps/cli/src/store/agents.ts`:
- Around line 16-36: `readRegistry()` is hiding file/parse failures by returning
an empty registry, which lets `addAgent`, `updateAgent`, and `removeAgent`
overwrite `agents.yml` after a bad read. Change `readRegistry` to surface errors
instead of swallowing them (for both YAML/IO failures and invalid data), and
update the mutators to check that result and abort/report without calling
`writeRegistry()` when the read fails. Use the existing `readRegistry`,
`writeRegistry`, `addAgent`, `updateAgent`, and `removeAgent` flow to preserve
data on corrupted or unreadable files.

In `@apps/cli/src/utils/io.ts`:
- Around line 1-13: The stdinWritable helper currently writes to a Bun.FileSink
without forcing buffered data out, so incremental pipe messages may stay
buffered until close. Update stdinWritable to flush the sink after each write in
the WritableStream write handler, and keep the existing close/abort behavior
using stdin.end() so the child receives data promptly.

In `@apps/web/src/routes/threads/index.tsx`:
- Around line 135-153: The streaming handler in the threads route creates a new
broadcast message even when appendAgentEventText("", event) returns an empty
string, which leads to blank “…” bubbles for no-op events. Update the for await
event processing in the stream handler to skip events whose appended text is
empty before pushing a broadcast message, and keep the existing update path for
the last broadcast bubble when present.
- Around line 31-33: The event handling in the threads text accumulation
currently merges both "token" and "thought" into the same visible reply text,
which leaks internal thought content; update the switch in the thread rendering
logic to keep "thought" events separate or ignore them in the user-facing text,
while preserving the normal accumulation for assistant token events only. Use
the existing event-processing path in the threads route to ensure the final
bubble is built from tokens alone and thought content is handled elsewhere or
omitted.

In `@openspec/changes/acp-provider-runtime/design.md`:
- Around line 72-88: Add a language tag to the fenced code block in the ACP
provider runtime design doc so markdownlint no longer flags MD040. Update the
fence in the section listing src/acp and src/providers files to use a text-like
language identifier, keeping the content unchanged.

In `@openspec/changes/acp-provider-runtime/specs/acp-provider-cli/spec.md`:
- Around line 3-42: The CLI namespace is inconsistent across this spec and the
rest of the ACP docs, splitting the contract for the same provider feature set.
Update the requirement scenarios and command names to use a single namespace
consistently, matching the agreed CLI surface already used elsewhere (for
example, the provider list/detect behavior and worker capability advertising
docs), and ensure all references to the command group are aligned throughout
this spec.

In `@openspec/specs/acp-process-manager/spec.md`:
- Around line 83-95: The agent availability requirement is too narrow because it
only validates PATH membership, which would reject valid configured command
paths. Update the availability logic in the relevant worker/doctor check for the
agent command so it accepts executable absolute or relative paths when they
resolve successfully, and only emit the external-install hint for bare command
names that cannot be resolved on PATH. Use the existing agent command validation
flow to distinguish between path resolution and PATH lookup before reporting
availability.

---

Nitpick comments:
In `@apps/cli/src/commands/agents/add.ts`:
- Around line 1-17: The result-handling in add() duplicates the same ok/err
pattern used by other agent commands, so extract it into a shared helper to keep
behavior consistent. Create a reusable function like printResultOrExit in a
common CLI utility module, then update add() to use it with the addAgent result
and keep the success callback for the success message while centralizing the
error printing and process.exit(1) path.

In `@apps/cli/src/commands/agents/update.ts`:
- Around line 22-38: The `updateAgent` call in `agents/update.ts` is redundantly
merging data twice because `entry` is already fully resolved from `existing`.
Adjust the `update` command so `entry` is only used as the display payload for
the success message, and pass a partial patch object into `updateAgent` instead
of the already merged `AgentEntry`. Keep the success output logic tied to the
local `entry` variable, and ensure the `saved.match` handling remains unchanged.

In `@apps/cli/src/commands/config/index.ts`:
- Around line 2-9: The `registerConfigCommands` rename flow is reusing
`nameArgParser` from `validators/acp`, but that validator is now shared outside
ACP and its current name/location is misleading. Move the shared name validation
to a more generic validator module or rename/export it to reflect broader
device-name use, and update the `registerConfigCommands` import so the `rename`
command clearly points to a cross-domain validator.

In `@apps/cli/src/core/acp/events.ts`:
- Around line 54-75: The `tool.update` and `tool.end` branches in `events.ts`
are identical, so merge them into a single shared handler to prevent drift.
Update the `switch` in the event-mapping logic so both case labels fall through
to the same `ToolCallUpdateEventSchema.parse` return path, keeping the existing
`mapToolStatus`, `toolCallId`, `title`, `content`, and `rawOutput` mapping
unchanged.

In `@apps/cli/src/core/acp/pool.ts`:
- Around line 99-149: The ManagedAgent placeholder runtime is being modeled with
an unsafe cast instead of the actual “not yet booted” state. Update the
ManagedAgent shape used by getRuntime and bootRuntime so runtime is optional
(runtime?: AcpRuntime), then adjust the initialization and assignment sites in
bootRuntime, resetIdleTimer, and stopAgent to work with the optional field
safely without using undefined as unknown as AcpRuntime.

In `@apps/cli/src/core/agents/catalog.ts`:
- Around line 82-95: The persona fallback matching logic is duplicated between
configOptionsToPersonas and AgentRuntime.setPersona, so the heuristic can drift
over time. Extract the shared category/id matching into a single helper such as
findPersonaConfigOption or findPersonaConfigOptionId in catalog.ts, then update
runtime.ts to call that helper instead of re-implementing the inline fallback.
Keep the existing configOptionsToPersonas flow intact and reuse the shared
helper wherever persona config lookup is needed.

In `@apps/cli/src/handlers/controller.ts`:
- Around line 77-95: The chat handler in controller.ts does not handle failures
from runtime.threadCoordinator.prompt(...) or the async iteration, so a
mid-stream exception can end the generator silently without notifying
subscribers. Wrap the prompt/gen loop in try/catch (and optionally finally)
inside the chat handler, catch errors from the prompt generator or for-await
loop, broadcast a terminal error/abort event through context.broadcaster before
rethrowing, and ensure the stream exits cleanly for peers.
- Around line 77-95: The chat handler in os.chat.handler applies an
unconditional Bun.sleep after every yielded event, which adds latency linearly
for chatty streams. Update the streaming loop around
runtime.threadCoordinator.prompt and context.broadcaster.broadcast to throttle
by elapsed wall-clock time instead of per event, so the delay only happens when
the last flush was too recent. Keep the existing stream behavior, but gate the
sleep using a last-flush timestamp and env.CYRUS_STREAM_THROTTLING_MS.

In `@apps/cli/src/validators/acp.ts`:
- Around line 7-25: Both commandArgParser and nameArgParser duplicate the same
safeParse-to-InvalidArgumentError flow. Extract the repeated validation/throw
logic into a small shared helper that accepts a schema and fallback message,
then have commandArgParser and nameArgParser delegate to it while preserving
their current messages and return types. Use the existing symbols commandSchema,
nameSchema, and InvalidArgumentError to keep the refactor localized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1fae5a2f-56b4-487d-b531-a8311ca8a02d

📥 Commits

Reviewing files that changed from the base of the PR and between 1265c67 and be7945f.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (65)
  • .dotagents/commands/opsx-apply.md
  • .dotagents/commands/opsx-archive.md
  • .dotagents/commands/opsx-continue.md
  • .dotagents/commands/opsx-explore.md
  • .dotagents/commands/opsx-new.md
  • .dotagents/commands/opsx-propose.md
  • .dotagents/commands/opsx-sync.md
  • .dotagents/commands/opsx-verify.md
  • .dotagents/config.toml
  • .gitignore
  • apps/cli/package.json
  • apps/cli/src/commands/agents/add.ts
  • apps/cli/src/commands/agents/doctor.ts
  • apps/cli/src/commands/agents/index.ts
  • apps/cli/src/commands/agents/list.ts
  • apps/cli/src/commands/agents/rm.ts
  • apps/cli/src/commands/agents/update.ts
  • apps/cli/src/commands/auth/login.ts
  • apps/cli/src/commands/auth/logout.ts
  • apps/cli/src/commands/auth/whoami.ts
  • apps/cli/src/commands/config/index.ts
  • apps/cli/src/commands/config/rename.ts
  • apps/cli/src/commands/service/status.ts
  • apps/cli/src/commands/service/worker.ts
  • apps/cli/src/constants/file.ts
  • apps/cli/src/core/acp/config.ts
  • apps/cli/src/core/acp/events.ts
  • apps/cli/src/core/acp/host.ts
  • apps/cli/src/core/acp/ping.ts
  • apps/cli/src/core/acp/pool.ts
  • apps/cli/src/core/acp/transport.ts
  • apps/cli/src/core/agents/catalog.ts
  • apps/cli/src/core/agents/profile.ts
  • apps/cli/src/core/agents/runtime.ts
  • apps/cli/src/core/index.ts
  • apps/cli/src/core/threads/coordinator.ts
  • apps/cli/src/handlers/controller.ts
  • apps/cli/src/index.ts
  • apps/cli/src/lib/auth.ts
  • apps/cli/src/lib/env.ts
  • apps/cli/src/mocks/projects.ts
  • apps/cli/src/store/agents.ts
  • apps/cli/src/store/config.ts
  • apps/cli/src/utils/error.ts
  • apps/cli/src/utils/io.ts
  • apps/cli/src/validators/acp.ts
  • apps/cli/src/validators/agent.ts
  • apps/cli/src/validators/name.ts
  • apps/web/src/routes/threads/index.tsx
  • openspec/changes/acp-provider-runtime/.openspec.yaml
  • openspec/changes/acp-provider-runtime/design.md
  • openspec/changes/acp-provider-runtime/proposal.md
  • openspec/changes/acp-provider-runtime/specs/acp-process-manager/spec.md
  • openspec/changes/acp-provider-runtime/specs/acp-provider-cli/spec.md
  • openspec/changes/acp-provider-runtime/specs/acp-provider-config/spec.md
  • openspec/changes/acp-provider-runtime/specs/acp-session-router/spec.md
  • openspec/changes/acp-provider-runtime/tasks.md
  • openspec/specs/acp-process-manager/spec.md
  • openspec/specs/acp-provider-cli/spec.md
  • openspec/specs/acp-provider-config/spec.md
  • openspec/specs/acp-session-router/spec.md
  • shared/connections/src/contracts/controller.ts
  • shared/connections/src/schemas/agents.ts
  • shared/connections/src/schemas/chat.ts
  • shared/connections/src/schemas/rtc.ts

Comment thread apps/cli/src/commands/service/worker.ts Outdated
Comment thread apps/cli/src/core/acp/host.ts
Comment thread apps/cli/src/core/acp/ping.ts Outdated
Comment thread apps/cli/src/core/acp/ping.ts Outdated
Comment thread apps/cli/src/core/acp/pool.ts
Comment thread apps/web/src/routes/threads/index.tsx Outdated
Comment thread apps/web/src/routes/threads/index.tsx Outdated
Comment thread openspec/changes/acp-provider-runtime/design.md Outdated
Comment thread openspec/changes/acp-provider-runtime/specs/acp-provider-cli/spec.md Outdated
Comment thread openspec/specs/acp-process-manager/spec.md Outdated
Make agent pool shutdown awaitable, harden permission handling and session
recovery, propagate agents.yml read errors via better-result, return threadId
from chat, and archive the completed OpenSpec change.

Co-authored-by: Cursor <cursoragent@cursor.com>

This branch was successfully deployed

1 active deployment
Preview — 82c18727 Deployed Jul 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant