v2.2-paseo-providers: Paseo provider stack + v2.2.1 pane-scoped chat fixes

Ship Paseo-equivalent provider snapshot, AgentComposerBar, ACP dispatch
rewrite with streaming/persist, permission prompts, and agent commands.
Follow-up: pane-scoped chat resolution, CoderMessageList tool timeline,
WS user-delta replace, and inference orphan tool_call stripping.
Archive openspec v2-2; update CHANGELOG and CURRENT.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-26 15:18:31 +00:00
parent 04673eaf59
commit 93d3f86c2b
96 changed files with 6694 additions and 1329 deletions

View File

@@ -1,15 +1,5 @@
/**
* PTY dispatch — runs external agents directly on the host.
*
* v2.1.3: Spawns agent binaries directly (no sh -c wrapper) using the
* install_path from agent-probe. Follows Paseo's pattern: direct binary
* path + args array + cwd.
*
* Supported agents:
* - claude: `claude -p --model <model>` (print mode, reads task from stdin)
* - opencode: `opencode --model <model>` (stdin pipe)
* - qwen: `qwen -p <task> --output-format stream-json`
* - goose: `goose run --text <task>`
*/
import type { FastifyBaseLogger } from 'fastify';
import { spawn } from 'node:child_process';
@@ -25,27 +15,44 @@ export interface PtyDispatchOpts {
task: string;
worktreePath: string;
model?: string;
modeId?: string;
thinkingOptionId?: string;
installPath?: string;
signal?: AbortSignal;
log: FastifyBaseLogger;
}
interface AgentCommand {
interface PtySpawnSpec {
binary: string;
args: string[];
stdin?: string;
}
function buildAgentCommand(agent: string, task: string, model?: string, installPath?: string): AgentCommand | null {
function buildPtySpawnSpec(
agent: string,
task: string,
model?: string,
modeId?: string,
thinkingOptionId?: string,
installPath?: string,
): PtySpawnSpec | null {
const binary = installPath ?? agent;
switch (agent) {
case 'claude':
return {
binary,
args: model ? ['-p', '--model', model] : ['-p'],
stdin: task,
};
case 'claude': {
const args = ['-p'];
if (model) args.push('--model', model);
if (modeId) args.push('--permission-mode', modeId);
if (thinkingOptionId) args.push('--effort', thinkingOptionId);
return { binary, args, stdin: task };
}
case 'qwen': {
const args = ['-p', task, '--output-format', 'stream-json'];
if (model) args.push('--model', model);
if (modeId) args.push('--approval-mode', modeId);
return { binary, args };
}
case 'opencode':
return {
@@ -54,20 +61,10 @@ function buildAgentCommand(agent: string, task: string, model?: string, installP
stdin: task,
};
case 'qwen':
return {
binary,
args: model
? ['-p', task, '--model', model, '--output-format', 'stream-json']
: ['-p', task, '--output-format', 'stream-json'],
};
case 'goose':
return {
binary,
args: model
? ['run', '--text', task, '--model', model]
: ['run', '--text', task],
args: model ? ['run', '--text', task, '--model', model] : ['run', '--text', task],
};
default:
@@ -76,9 +73,9 @@ function buildAgentCommand(agent: string, task: string, model?: string, installP
}
export async function dispatchViaPty(opts: PtyDispatchOpts): Promise<DispatchResult> {
const { agent, task, worktreePath, model, installPath, signal, log } = opts;
const { agent, task, worktreePath, model, modeId, thinkingOptionId, installPath, signal, log } = opts;
const cmd = buildAgentCommand(agent, task, model, installPath);
const cmd = buildPtySpawnSpec(agent, task, model, modeId, thinkingOptionId, installPath);
if (!cmd) {
return {
exitCode: 1,
@@ -87,7 +84,7 @@ export async function dispatchViaPty(opts: PtyDispatchOpts): Promise<DispatchRes
};
}
log.info({ agent, binary: cmd.binary, worktreePath }, 'pty-dispatch: starting');
log.info({ agent, binary: cmd.binary, worktreePath, modeId }, 'pty-dispatch: starting');
return new Promise<DispatchResult>((resolve, reject) => {
const child = spawn(cmd.binary, cmd.args, {