Skip to content

Commit d991609

Browse files
author
lijiuyang.5137
committed
Improve SDK MCP control restarts
1 parent ef60376 commit d991609

5 files changed

Lines changed: 569 additions & 26 deletions

File tree

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ keeps owning its own SDK, ACP, editor, or orchestration protocol.
351351
| `claude-agent-sdk` | Official Python Claude Agent SDK | Supported | `ClaudeAgentOptions(cli_path="cctty")` | Deterministic SDK test with fake Claude; live mini-game write test under `permission_mode="default"` with SDK `can_use_tool` approvals. | Same result metadata and synthetic partial-message limits as TypeScript. |
352352
| Hermes Agent | OpenAI-like agent runtime with a Claude SDK provider | Supported on the cctty branch | Select provider `claude-sdk` or alias `cctty`; set `HERMES_CLAUDE_SDK_COMMAND=/path/to/cctty` or `CCTTY_CLI_PATH=/path/to/cctty`. | Hermes unit coverage for provider routing and a local `ClaudeSDKClient` smoke through cctty. `copilot-acp` remains separate. | Upstream release has to include the `claude-sdk` provider path; cctty does not implement Hermes' ACP adapter. |
353353
| NanoClaw / NanoBot (`qwibitai/nanoclaw`) | Containerized TypeScript Agent SDK runner | Core supported; project integration needs a path override | Its runner passes `pathToClaudeCodeExecutable` to the SDK, but currently hard-codes `/pnpm/claude`. Patch NanoClaw to expose that setting, or install/symlink cctty at `/pnpm/claude` inside the agent image and set `CCTTY_CLAUDE_PATH` to the real Claude binary. | Source inspected at commit `0683c6e` / `nanoclaw@2.0.64`; NanoClaw-pinned `@anthropic-ai/claude-agent-sdk@0.2.138` smoke passed with cctty. | Full container/session runtime not run. Upstream should expose the executable path as config. |
354-
| `@agentclientprotocol/claude-agent-acp` | ACP server powered by the TypeScript Claude Agent SDK | Supported as a core executable override | Set `CLAUDE_CODE_EXECUTABLE=/path/to/cctty` in the ACP adapter environment. | `0.36.1` smoke passed through `acpx`, including SDK initialization metadata, `includePartialMessages`, and idle session-state handling. | ACP behavior belongs to the adapter. cctty only supplies the Claude executable behind it. Dynamic `set_model` / `set_permission_mode` control requests are compatibility-acknowledged; the underlying TTY run still comes from the CLI args/settings used at launch. |
354+
| `@agentclientprotocol/claude-agent-acp` | ACP server powered by the TypeScript Claude Agent SDK | Supported as a core executable override | Set `CLAUDE_CODE_EXECUTABLE=/path/to/cctty` in the ACP adapter environment. | `0.36.1` smoke passed through `acpx`, including SDK initialization metadata, `includePartialMessages`, idle session-state handling, SDK MCP proxying, and dynamic `set_model` / `set_permission_mode` argument updates. | ACP behavior belongs to the adapter. cctty only supplies the Claude executable behind it. |
355355
| `@zed-industries/claude-code-acp` | Deprecated ACP server powered by the TypeScript Claude Agent SDK | Supported as a core executable override | Set `CLAUDE_CODE_EXECUTABLE=/path/to/cctty` in the ACP adapter environment. | `0.16.2` smoke passed through `acpx`. npm marks this package deprecated in favor of `@agentclientprotocol/claude-agent-acp`. | Same boundary as above: keep the ACP adapter, replace only its Claude executable. |
356356
| `acp-claude-code` | ACP bridge for Claude Code | Conditional | Set `ACP_PATH_TO_CLAUDE_CODE_EXECUTABLE=/path/to/cctty`. | `0.8.0` path override works. A smoke passed with the older SDK-exporting `@anthropic-ai/claude-code@1.0.128` dependency and fake Claude under cctty. | A fresh install currently resolves `@anthropic-ai/claude-code: latest` to a CLI-only package, so the wrapper can fail before cctty starts. This is a wrapper dependency issue, not an ACP feature cctty should implement. |
357357
| `claude-code-acp` / `cc-acp` | Alternate ACP bridge for Claude Code | Unsupported without wrapper changes | No cctty setting found. | `0.1.1` source/package inspected. | The wrapper hard-resolves its bundled `@anthropic-ai/claude-code/cli.js` and ignores `CLAUDE_CODE_EXECUTABLE`; it needs an upstream executable-path option. |
@@ -375,6 +375,13 @@ For SDK permission callbacks, `cctty` consumes the hidden
375375
`control_request` messages, waits for SDK `control_response` decisions, and then
376376
drives Claude's interactive permission UI with keyboard input.
377377

378+
For SDK host metadata and controls, `cctty` returns Claude-compatible
379+
initialization metadata including the current model aliases used by Claude Code
380+
SDK hosts (`default`, `opus`, `haiku`, `sonnet[1m]`, and full model names). When
381+
the host sends `set_model` or `set_permission_mode` between turns, `cctty`
382+
updates the underlying Claude launch arguments, restarts the idle TTY, and uses
383+
the new model or permission mode for the next prompt.
384+
378385
Claude's interactive `AskUserQuestion` forms are bridged through the same SDK
379386
control channel. When Claude asks a structured question, `cctty` forwards the
380387
original `AskUserQuestion` tool input to the SDK caller when it is already in
@@ -555,7 +562,8 @@ Some SDKs pass flags that are not listed in current `claude --help`.
555562
differentials before being marked supported.
556563
- SDK control requests used by popular wrappers are compatibility-acked:
557564
`initialize`, `interrupt`, `set_model`, `set_permission_mode`,
558-
`set_max_thinking_tokens`, `apply_flag_settings`, and `mcp_status`. Dynamic
559-
model/mode changes after a TTY session is already running are still partial;
560-
the actual Claude process behavior comes from its launch args and local
561-
settings.
565+
`set_max_thinking_tokens`, `apply_flag_settings`, and `mcp_status`.
566+
`set_model` and `set_permission_mode` update the stored launch args and restart
567+
the idle TTY for the next user turn. Other flag-setting controls are still
568+
compatibility acknowledgements unless they already map to launch-time CLI
569+
flags.

src/pty.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::io::{self, Read, Write};
55
use std::os::fd::FromRawFd;
66
use std::path::{Path, PathBuf};
77
use std::sync::{Arc, Mutex};
8+
use std::time::{Duration, Instant};
89

910
use crate::error::{CcttyError, Result};
1011

@@ -55,6 +56,33 @@ impl PtyProcess {
5556
libc::kill(self.pid, libc::SIGTERM);
5657
}
5758
}
59+
60+
pub fn terminate(&mut self, timeout: Duration) {
61+
if self.pid <= 0 {
62+
return;
63+
}
64+
let pid = self.pid;
65+
self.kill();
66+
let started = Instant::now();
67+
loop {
68+
let mut status = 0;
69+
let result = unsafe { libc::waitpid(pid, &mut status, libc::WNOHANG) };
70+
if result == pid || result < 0 {
71+
self.pid = 0;
72+
return;
73+
}
74+
if started.elapsed() >= timeout {
75+
unsafe {
76+
libc::kill(-pid, libc::SIGKILL);
77+
libc::kill(pid, libc::SIGKILL);
78+
libc::waitpid(pid, &mut status, 0);
79+
}
80+
self.pid = 0;
81+
return;
82+
}
83+
std::thread::sleep(Duration::from_millis(25));
84+
}
85+
}
5886
}
5987

6088
impl Drop for PtyProcess {

0 commit comments

Comments
 (0)