import type { CanvasAgentSnapshot } from "@/lib/canvas/canvas-agent-ops"; import type { AgentReasoningEffort } from "@/stores/use-agent-store"; type AgentConfigResponse = { ok?: boolean; protocolVersion?: number; url?: string; token?: string; hasToken?: boolean }; export type AgentSkillScope = "user" | "repo" | "system" | "admin"; export type AgentSkillInterface = { displayName?: string | null; shortDescription?: string | null; defaultPrompt?: string | null }; export type AgentSkillSummary = { name: string; description: string; shortDescription?: string | null; interface?: AgentSkillInterface | null; dependencies?: unknown; path: string; scope: AgentSkillScope; enabled: boolean; managed: boolean; }; export type AgentSkillDetail = { name: string; description: string; instructions: string; interface?: AgentSkillInterface | null; path: string; managed: true; revision: string; }; export type AgentSkillInput = { name?: string; description: string; instructions: string; interface?: AgentSkillInterface | null; expectedRevision?: string }; export type AgentSkillDraft = { name: string; displayName: string; description: string; instructions: string; shortDescription: string; defaultPrompt: string }; export type AgentSkillDraftInput = { source: "conversation" | "canvas"; threadId: string; clientId: string; model?: string; effort?: AgentReasoningEffort }; export type AgentSkillsResponse = { ok?: boolean; data?: AgentSkillSummary[]; errors?: unknown[] }; export type AgentSkillResponse = { ok?: boolean; data?: AgentSkillDetail }; export type AgentSkillDraftResponse = { ok?: boolean; data?: AgentSkillDraft }; export async function postState(endpoint: string, token: string, clientId: string, snapshot: CanvasAgentSnapshot | null) { try { const response = await fetch(`${endpoint}/canvas/state?token=${encodeURIComponent(token)}&clientId=${encodeURIComponent(clientId)}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(snapshot ? { ...snapshot, hasCanvas: true } : { hasCanvas: false }), }); return response.ok; } catch { return false; } } export async function activateAgentClient(endpoint: string, token: string, clientId: string) { try { await fetch(`${endpoint}/canvas/activate?token=${encodeURIComponent(token)}&clientId=${encodeURIComponent(clientId)}`, { method: "POST" }); } catch {} } export async function postToolResult(endpoint: string, token: string, clientId: string, body: { requestId: string; result?: unknown; error?: string }) { await fetchAgentJson(endpoint, token, `/canvas/result?clientId=${encodeURIComponent(clientId)}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }); } export async function postCodexApproval(endpoint: string, token: string, requestId: string, decision: "accept" | "acceptForSession" | "decline") { await fetchAgentJson(endpoint, token, "/agent/codex/approval", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ requestId, decision }) }); } export async function interruptCodexTurn(endpoint: string, token: string, threadId?: string) { await fetchAgentJson(endpoint, token, "/agent/codex/interrupt", jsonPost({ threadId })); } export async function acknowledgeCodexHistory(endpoint: string, token: string, threadId: string, turnIds: string[]) { await fetchAgentJson(endpoint, token, "/agent/codex/history/ack", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ threadId, turnIds }) }); } export async function revealAgentLocalFile(endpoint: string, token: string, path: string) { await fetchAgentJson(endpoint, token, "/agent/local-file/reveal", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ path }) }); } export function fetchCodexSkills(endpoint: string, token: string, forceReload = false) { return fetchAgentJson(endpoint, token, `/agent/codex/skills${forceReload ? "?forceReload=1" : ""}`); } export function fetchCodexSkill(endpoint: string, token: string, name: string) { return fetchAgentJson(endpoint, token, `/agent/codex/skills/${encodeURIComponent(name)}`); } export function createCodexSkill(endpoint: string, token: string, input: AgentSkillInput) { return fetchAgentJson(endpoint, token, "/agent/codex/skills", jsonPost(input)); } export function createCodexSkillDraft(endpoint: string, token: string, input: AgentSkillDraftInput) { return fetchAgentJson(endpoint, token, "/agent/codex/skills/draft", jsonPost(input)); } export function updateCodexSkill(endpoint: string, token: string, name: string, input: AgentSkillInput) { return fetchAgentJson(endpoint, token, `/agent/codex/skills/${encodeURIComponent(name)}`, jsonPost(input)); } export function deleteCodexSkill(endpoint: string, token: string, name: string, expectedRevision: string) { return fetchAgentJson<{ ok?: boolean }>(endpoint, token, `/agent/codex/skills/${encodeURIComponent(name)}/delete`, jsonPost({ expectedRevision })); } export function setCodexSkillEnabled(endpoint: string, token: string, skill: Pick, enabled: boolean) { return fetchAgentJson<{ ok?: boolean }>(endpoint, token, `/agent/codex/skills/${encodeURIComponent(skill.name)}/enabled`, jsonPost({ ...skill, enabled })); } export async function fetchAgentJson(endpoint: string, token: string, path: string, init?: RequestInit) { const url = `${endpoint}${path}${path.includes("?") ? "&" : "?"}token=${encodeURIComponent(token)}`; const res = await fetch(url, init); const data = (await res.json().catch(() => ({}))) as T & { error?: string; msg?: string }; if (!res.ok) throw new Error(data.error || data.msg || "本地 Agent 请求失败"); return data; } export async function discoverAgentConfig(endpoint: string) { try { const res = await fetch(`${endpoint}/config`); if (!res.ok) return null; const data = (await res.json()) as AgentConfigResponse; return data.ok ? data : null; } catch { return null; } } function jsonPost(body: unknown): RequestInit { return { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }; }