feat: completions update, create promptrequest, LLM flag rename, ACP content fix
- Add prompts/promptrequests to shell completions (fish + bash) - Add approve, setup, prompt, promptrequest commands to completions - Add `create promptrequest` CLI command (POST /projects/:name/promptrequests) - Rename --proxy-mode-llm-provider/model to --llm-provider/model - Fix ACP client: handle single-object content format from real Gemini - Add tests for single-object content and agent_thought_chunk filtering Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -205,12 +205,16 @@ export class AcpClient {
|
||||
|
||||
// Collect text from agent_message_chunk
|
||||
if (update.sessionUpdate === 'agent_message_chunk') {
|
||||
const content = update.content as Array<{ type: string; text?: string }> | undefined;
|
||||
if (content) {
|
||||
for (const block of content) {
|
||||
if (block.type === 'text' && block.text) {
|
||||
this.activePromptChunks.push(block.text);
|
||||
}
|
||||
const content = update.content;
|
||||
// Gemini ACP sends content as a single object {type, text} or an array [{type, text}]
|
||||
const blocks: Array<{ type: string; text?: string }> = Array.isArray(content)
|
||||
? content as Array<{ type: string; text?: string }>
|
||||
: content && typeof content === 'object'
|
||||
? [content as { type: string; text?: string }]
|
||||
: [];
|
||||
for (const block of blocks) {
|
||||
if (block.type === 'text' && block.text) {
|
||||
this.activePromptChunks.push(block.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +230,77 @@ describe('AcpClient', () => {
|
||||
expect(result).toBe('Part A Part B');
|
||||
});
|
||||
|
||||
it('handles single-object content (real Gemini ACP format)', async () => {
|
||||
createClient();
|
||||
autoHandshake('sess-1');
|
||||
await client.ensureReady();
|
||||
|
||||
mock.stdin.write.mockImplementation((data: string) => {
|
||||
const msg = JSON.parse(data.trim()) as { id: number; method: string };
|
||||
if (msg.method === 'session/prompt') {
|
||||
setImmediate(() => {
|
||||
// Real Gemini ACP sends content as a single object, not an array
|
||||
mock.sendLine({
|
||||
jsonrpc: '2.0',
|
||||
method: 'session/update',
|
||||
params: {
|
||||
sessionId: 'sess-1',
|
||||
update: {
|
||||
sessionUpdate: 'agent_message_chunk',
|
||||
content: { type: 'text', text: 'ok' },
|
||||
},
|
||||
},
|
||||
});
|
||||
mock.sendResponse(msg.id, { stopReason: 'end_turn' });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const result = await client.prompt('test');
|
||||
expect(result).toBe('ok');
|
||||
});
|
||||
|
||||
it('ignores agent_thought_chunk notifications', async () => {
|
||||
createClient();
|
||||
autoHandshake('sess-1');
|
||||
await client.ensureReady();
|
||||
|
||||
mock.stdin.write.mockImplementation((data: string) => {
|
||||
const msg = JSON.parse(data.trim()) as { id: number; method: string };
|
||||
if (msg.method === 'session/prompt') {
|
||||
setImmediate(() => {
|
||||
// Gemini sends thought chunks before message chunks
|
||||
mock.sendLine({
|
||||
jsonrpc: '2.0',
|
||||
method: 'session/update',
|
||||
params: {
|
||||
sessionId: 'sess-1',
|
||||
update: {
|
||||
sessionUpdate: 'agent_thought_chunk',
|
||||
content: { type: 'text', text: 'Thinking about it...' },
|
||||
},
|
||||
},
|
||||
});
|
||||
mock.sendLine({
|
||||
jsonrpc: '2.0',
|
||||
method: 'session/update',
|
||||
params: {
|
||||
sessionId: 'sess-1',
|
||||
update: {
|
||||
sessionUpdate: 'agent_message_chunk',
|
||||
content: { type: 'text', text: 'ok' },
|
||||
},
|
||||
},
|
||||
});
|
||||
mock.sendResponse(msg.id, { stopReason: 'end_turn' });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const result = await client.prompt('test');
|
||||
expect(result).toBe('ok');
|
||||
});
|
||||
|
||||
it('calls ensureReady automatically (lazy init)', async () => {
|
||||
createClient();
|
||||
autoHandshake('sess-auto');
|
||||
|
||||
Reference in New Issue
Block a user