import type { SessionAcpIdentity, SessionAcpMeta } from "../../config/sessions/types.js"; import { isSessionIdentityPending, resolveSessionIdentityFromMeta } from "./session-identity.js"; export const ACP_SESSION_IDENTITY_RENDERER_VERSION = "v1"; export type AcpSessionIdentifierRenderMode = "status" | "thread"; type SessionResumeHintResolver = (params: { agentSessionId: string }) => string; const ACP_AGENT_RESUME_HINT_BY_KEY = new Map([ [ "codex", ({ agentSessionId }) => `resume in Codex CLI: \`codex resume ${agentSessionId}\` (continues this conversation).`, ], [ "openai-codex", ({ agentSessionId }) => `resume in Codex CLI: \`codex resume ${agentSessionId}\` (continues this conversation).`, ], [ "codex-cli", ({ agentSessionId }) => `resume in Codex CLI: \`codex resume ${agentSessionId}\` (continues this conversation).`, ], [ "kimi", ({ agentSessionId }) => `resume in Kimi CLI: \`kimi resume ${agentSessionId}\` (continues this conversation).`, ], [ "moonshot-kimi", ({ agentSessionId }) => `resume in Kimi CLI: \`kimi resume ${agentSessionId}\` (continues this conversation).`, ], ]); function normalizeText(value: unknown): string | undefined { if (typeof value !== "string") { return undefined; } const trimmed = value.trim(); return trimmed || undefined; } function normalizeAgentHintKey(value: unknown): string | undefined { const normalized = normalizeText(value); if (!normalized) { return undefined; } return normalized.toLowerCase().replace(/[\s_]+/g, "-"); } function resolveAcpAgentResumeHintLine(params: { agentId?: string; agentSessionId?: string; }): string | undefined { const agentSessionId = normalizeText(params.agentSessionId); const agentKey = normalizeAgentHintKey(params.agentId); if (!agentSessionId || !agentKey) { return undefined; } const resolver = ACP_AGENT_RESUME_HINT_BY_KEY.get(agentKey); return resolver ? resolver({ agentSessionId }) : undefined; } export function resolveAcpSessionIdentifierLines(params: { sessionKey: string; meta?: SessionAcpMeta; }): string[] { const backend = normalizeText(params.meta?.backend) ?? "backend"; const identity = resolveSessionIdentityFromMeta(params.meta); return resolveAcpSessionIdentifierLinesFromIdentity({ backend, identity, mode: "status", }); } export function resolveAcpSessionIdentifierLinesFromIdentity(params: { backend: string; identity?: SessionAcpIdentity; mode?: AcpSessionIdentifierRenderMode; }): string[] { const backend = normalizeText(params.backend) ?? "backend"; const mode = params.mode ?? "status"; const identity = params.identity; const agentSessionId = normalizeText(identity?.agentSessionId); const acpxSessionId = normalizeText(identity?.acpxSessionId); const acpxRecordId = normalizeText(identity?.acpxRecordId); const hasIdentifier = Boolean(agentSessionId || acpxSessionId || acpxRecordId); if (isSessionIdentityPending(identity) && hasIdentifier) { if (mode === "status") { return ["session ids: pending (available after the first reply)"]; } return []; } const lines: string[] = []; if (agentSessionId) { lines.push(`agent session id: ${agentSessionId}`); } if (acpxSessionId) { lines.push(`${backend} session id: ${acpxSessionId}`); } if (acpxRecordId) { lines.push(`${backend} record id: ${acpxRecordId}`); } return lines; } export function resolveAcpSessionCwd(meta?: SessionAcpMeta): string | undefined { const runtimeCwd = normalizeText(meta?.runtimeOptions?.cwd); if (runtimeCwd) { return runtimeCwd; } return normalizeText(meta?.cwd); } export function resolveAcpThreadSessionDetailLines(params: { sessionKey: string; meta?: SessionAcpMeta; }): string[] { const meta = params.meta; const identity = resolveSessionIdentityFromMeta(meta); const backend = normalizeText(meta?.backend) ?? "backend"; const lines = resolveAcpSessionIdentifierLinesFromIdentity({ backend, identity, mode: "thread", }); if (lines.length === 0) { return lines; } const hint = resolveAcpAgentResumeHintLine({ agentId: meta?.agent, agentSessionId: identity?.agentSessionId, }); if (hint) { lines.push(hint); } return lines; }