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:
148
apps/server/src/services/skill-invoke.ts
Normal file
148
apps/server/src/services/skill-invoke.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import type { Sql } from '../db.js';
|
||||
|
||||
export const DEFAULT_SKILL_USER_MESSAGE = 'Apply this skill.';
|
||||
|
||||
export interface SkillInvokeTransactionResult {
|
||||
synth_assistant_id: string;
|
||||
tool_message_id: string;
|
||||
user_message_id: string;
|
||||
assistant_message_id: string;
|
||||
}
|
||||
|
||||
export interface SkillInvokeToolCall {
|
||||
id: string;
|
||||
name: 'skill_use';
|
||||
args: { name: string };
|
||||
}
|
||||
|
||||
export type SkillInvokeSessionFrame = Record<string, unknown> & { type: string };
|
||||
|
||||
export async function runSkillInvokeTransaction(
|
||||
sql: Sql,
|
||||
args: {
|
||||
sessionId: string;
|
||||
chatId: string;
|
||||
skillName: string;
|
||||
skillBody: string;
|
||||
userText: string;
|
||||
},
|
||||
): Promise<{ result: SkillInvokeTransactionResult; toolCall: SkillInvokeToolCall }> {
|
||||
const toolCallId = randomUUID();
|
||||
const toolCall: SkillInvokeToolCall = {
|
||||
id: toolCallId,
|
||||
name: 'skill_use',
|
||||
args: { name: args.skillName },
|
||||
};
|
||||
const toolResults = {
|
||||
tool_call_id: toolCallId,
|
||||
output: args.skillBody,
|
||||
truncated: false,
|
||||
};
|
||||
|
||||
const result = await sql.begin(async (tx) => {
|
||||
const [synthAssistant] = await tx<{ id: string }[]>`
|
||||
INSERT INTO messages (session_id, chat_id, role, content, status, created_at)
|
||||
VALUES (${args.sessionId}, ${args.chatId}, 'assistant', '', 'complete', clock_timestamp())
|
||||
RETURNING id
|
||||
`;
|
||||
await tx`
|
||||
INSERT INTO message_parts (message_id, sequence, kind, payload)
|
||||
VALUES (${synthAssistant!.id}, 0, 'tool_call', ${tx.json({
|
||||
id: toolCallId,
|
||||
name: 'skill_use',
|
||||
args: { name: args.skillName },
|
||||
} as never)})
|
||||
`;
|
||||
const [toolMsg] = await tx<{ id: string }[]>`
|
||||
INSERT INTO messages (session_id, chat_id, role, content, status, created_at)
|
||||
VALUES (${args.sessionId}, ${args.chatId}, 'tool', '', 'complete', clock_timestamp())
|
||||
RETURNING id
|
||||
`;
|
||||
await tx`
|
||||
INSERT INTO message_parts (message_id, sequence, kind, payload)
|
||||
VALUES (${toolMsg!.id}, 0, 'tool_result', ${tx.json(toolResults as never)})
|
||||
`;
|
||||
const [userMsg] = await tx<{ id: string }[]>`
|
||||
INSERT INTO messages (session_id, chat_id, role, content, status, created_at)
|
||||
VALUES (${args.sessionId}, ${args.chatId}, 'user', ${args.userText}, 'complete', clock_timestamp())
|
||||
RETURNING id
|
||||
`;
|
||||
const [assistantMsg] = await tx<{ id: string }[]>`
|
||||
INSERT INTO messages (session_id, chat_id, role, content, status, created_at)
|
||||
VALUES (${args.sessionId}, ${args.chatId}, 'assistant', '', 'streaming', clock_timestamp())
|
||||
RETURNING id
|
||||
`;
|
||||
await tx`UPDATE sessions SET updated_at = clock_timestamp() WHERE id = ${args.sessionId}`;
|
||||
await tx`UPDATE chats SET updated_at = clock_timestamp() WHERE id = ${args.chatId}`;
|
||||
return {
|
||||
synth_assistant_id: synthAssistant!.id,
|
||||
tool_message_id: toolMsg!.id,
|
||||
user_message_id: userMsg!.id,
|
||||
assistant_message_id: assistantMsg!.id,
|
||||
};
|
||||
});
|
||||
|
||||
return { result, toolCall };
|
||||
}
|
||||
|
||||
export function buildSkillInvokeSyntheticFrames(
|
||||
chatId: string,
|
||||
result: SkillInvokeTransactionResult,
|
||||
toolCall: SkillInvokeToolCall,
|
||||
skillBody: string,
|
||||
): SkillInvokeSessionFrame[] {
|
||||
return [
|
||||
{
|
||||
type: 'message_started',
|
||||
message_id: result.synth_assistant_id,
|
||||
chat_id: chatId,
|
||||
role: 'assistant',
|
||||
},
|
||||
{
|
||||
type: 'tool_call',
|
||||
message_id: result.synth_assistant_id,
|
||||
chat_id: chatId,
|
||||
tool_call: toolCall,
|
||||
},
|
||||
{
|
||||
type: 'message_complete',
|
||||
message_id: result.synth_assistant_id,
|
||||
chat_id: chatId,
|
||||
},
|
||||
{
|
||||
type: 'tool_result',
|
||||
tool_message_id: result.tool_message_id,
|
||||
tool_call_id: toolCall.id,
|
||||
chat_id: chatId,
|
||||
output: skillBody,
|
||||
truncated: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function buildSkillInvokeUserFrames(
|
||||
chatId: string,
|
||||
userMessageId: string,
|
||||
userText: string,
|
||||
): SkillInvokeSessionFrame[] {
|
||||
return [
|
||||
{
|
||||
type: 'message_started',
|
||||
message_id: userMessageId,
|
||||
chat_id: chatId,
|
||||
role: 'user',
|
||||
},
|
||||
{
|
||||
type: 'delta',
|
||||
message_id: userMessageId,
|
||||
chat_id: chatId,
|
||||
content: userText,
|
||||
},
|
||||
{
|
||||
type: 'message_complete',
|
||||
message_id: userMessageId,
|
||||
chat_id: chatId,
|
||||
},
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user