import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; import { StringEnum } from "@earendil-works/pi-ai"; import { randomUUID } from "crypto"; import { spawn, spawnSync } from "child_process"; import { Type } from "typebox"; import { Text } from "@earendil-works/pi-tui"; import { IntercomClient, type SendResult } from "./broker/client.ts"; import { parseBossControl } from "./broker/boss-adapter.ts"; import { spawnBrokerIfNeeded } from "./broker/spawn.ts"; import { INTERCOM_SCOPE_ENV, intercomScopeIdFromEnvForRegistration } from "./protocol-v4/contract.ts"; import { SessionListOverlay } from "./ui/session-list.ts"; import { ComposeOverlay, type ComposeResult } from "./ui/compose.ts"; import { InlineMessageComponent } from "./ui/inline-message.ts"; import { formatMessageTiming } from "./ui/timestamps.ts"; import { formatSessionDisplayName, sanitizeDisplayText, sessionOriginLabel, shortestUniqueIdPrefixes } from "./ui/session-identity.ts"; import { getAskTimeoutMs, getAskWaitMs, loadConfig, type IntercomConfig } from "./config.ts"; import type { SessionInfo, SessionRegistration, Message, Attachment } from "./types.ts"; import { pendingAskId, ReplyTracker, type IntercomContext } from "./reply-tracker.ts"; import { InboundMessageConflictError, PersistentInboundInbox, readPendingAsksSnapshot, type StoredInboundMessage } from "./inbound-inbox.ts"; import { PersistentOutboundOutbox } from "./outbound-outbox.ts"; import { formatIntercomTeam, resolveBossIntercomTeam, resolveIntercomTeam, resolveManagedInboxSession } from "./team.ts"; import { classifyMembership, currentTmuxWorkspace, formatJoinStatus, formatJoinSuccess, formatJoinableWorkspaceList, isZhLocale, listScopedWorkspaces, parseJoinArgs, readSessionScope, rejectManagedJoin, workspaceNameForScope, } from "./workspace-join.ts"; import { authorizeBossSender, BossTeamScopeError, bossSelfSessionError, filterBossSessions, isBossControllerReadinessControl, readBossTeamScope, resolveBossLiveTarget } from "./boss-team-scope.ts"; import { INTERCOM_CONTROL_DELIVERY_EVENT, INTERCOM_CONTROL_RECEIVED_EVENT, INTERCOM_CONTROL_REGISTER_EVENT, INTERCOM_CONTROL_SEND_EVENT, intercomControlKey, parseIntercomControlRegistration, parseIntercomControlSendRequest, type IntercomControlDeliveryEvent, type IntercomControlReceivedEvent, } from "./control.ts"; const SUBAGENT_CONTROL_INTERCOM_EVENT = "subagent:control-intercom"; const SUBAGENT_RESULT_INTERCOM_EVENT = "subagent:result-intercom"; const SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT = "subagent:result-intercom-delivery"; export const INTERCOM_INBOUND_ACTIVITY_EVENT = "agent-intercom:inbound-message"; export const INTERCOM_LIFECYCLE_SEND_EVENT = "agent-intercom:lifecycle-send"; const INBOUND_BATCH_QUIET_MS = 300; const INBOUND_BATCH_MAX_LATENCY_MS = 1000; const INBOUND_IDLE_RETRY_MS = 500; const CONTROL_REGISTRATION_GRACE_MS = 250; const DEFAULT_UNNAMED_SESSION_ALIAS_PREFIX = "subagent-chat"; const SUBAGENT_ORCHESTRATOR_TARGET_ENV = "PI_SUBAGENT_ORCHESTRATOR_TARGET"; const SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV = "PI_SUBAGENT_ORCHESTRATOR_SESSION_ID"; const INTERCOM_SESSION_ID_ENV = "PI_INTERCOM_SESSION_ID"; const NAME_POLL_MS_ENV = "PI_INTERCOM_NAME_POLL_MS"; const SUBAGENT_RUN_ID_ENV = "PI_SUBAGENT_RUN_ID"; const SUBAGENT_CHILD_AGENT_ENV = "PI_SUBAGENT_CHILD_AGENT"; const SUBAGENT_CHILD_INDEX_ENV = "PI_SUBAGENT_CHILD_INDEX"; const SUBAGENT_INTERCOM_SESSION_NAME_ENV = "PI_SUBAGENT_INTERCOM_SESSION_NAME"; export function isEmptyRpcBootstrapSession( ctx: ExtensionContext, environment: NodeJS.ProcessEnv = process.env, ): boolean { // Fleet-owned Pi peers must join Intercom before their first model turn so // the orchestrator can complete its exact-run readiness handshake. They are // intentionally launched as otherwise-empty RPC sessions, so deferring them // here deadlocks readiness and causes the orchestrator to stop a healthy peer. if (environment.AGENT_INTERCOM_OWNED === "1") return false; if (ctx.mode !== "rpc") return false; const sessionManager = ctx.sessionManager as typeof ctx.sessionManager & { getEntries?: () => Array<{ type?: string }>; }; if (typeof sessionManager.getEntries !== "function") return false; try { return !sessionManager.getEntries().some((entry) => entry.type === "message"); } catch { return false; } } interface ChildOrchestratorMetadata { orchestratorTarget: string; orchestratorSessionId?: string; runId: string; agent: string; index: string; sessionName?: string; } interface InboundMessageEntry { key?: string; from: SessionInfo; message: Message; receivedAt: number; readAt?: number; replyCommand?: string; bodyText: string; } interface InboundMessageBatchDetails { entries: InboundMessageEntry[]; } type ContactSupervisorReason = "need_decision" | "progress_update" | "interview_request"; interface SupervisorInterviewQuestion extends Record { id: string; type: "single" | "multi" | "text" | "image" | "info"; question: string; options?: unknown[]; } interface SupervisorInterviewRequest extends Record { title?: string; description?: string; questions: SupervisorInterviewQuestion[]; } interface SupervisorInterviewReply { responses: Array<{ id: string; value: unknown }>; } function getErrorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } function toError(error: unknown): Error { return error instanceof Error ? error : new Error(String(error)); } function toolErrorDetails(error: unknown): { error: true; code?: string } { return error instanceof BossTeamScopeError ? { error: true, code: error.code } : { error: true }; } class AskWaitElapsedError extends Error { constructor( readonly target: string, readonly replyTo: string, readonly waitMs: number, ) { super(`No reply from "${target}" within ${formatDuration(waitMs)}`); this.name = "AskWaitElapsedError"; } } function formatDuration(durationMs: number): string { if (durationMs % 60000 === 0) { const minutes = durationMs / 60000; return `${minutes} minute${minutes === 1 ? "" : "s"}`; } if (durationMs % 1000 === 0) { const seconds = durationMs / 1000; return `${seconds} second${seconds === 1 ? "" : "s"}`; } return `${durationMs}ms`; } function deferredAskResult(target: string, error: AskWaitElapsedError, deferred: boolean) { return { content: [{ type: "text" as const, text: deferred ? `Ask delivered to ${target}, but no reply arrived within ${formatDuration(error.waitMs)}. Deferral requested. Continuing asynchronously; a late reply will arrive as a new intercom message.` : `Ask delivered to ${target}, but no reply arrived within ${formatDuration(error.waitMs)}. Continuing without waiting; the connection closed before asynchronous deferral could be confirmed.`, }], details: { delivered: true, pending: true, deferred, deferRequested: deferred, waitTimedOut: true, messageId: error.replyTo, }, }; } function deliveryResultDetails(result: SendResult, extra: Record = {}): Record { return { messageId: result.id, accepted: result.accepted, delivered: result.delivered, ...(result.sentAt !== undefined ? { sentAt: result.sentAt } : {}), ...(result.deliveredAt !== undefined ? { deliveredAt: result.deliveredAt } : {}), ...(result.deliveryId ? { deliveryId: result.deliveryId } : {}), ...(result.code ? { code: result.code } : {}), ...(result.reason ? { reason: result.reason } : {}), ...extra, }; } function formatAttachments(attachments: Attachment[]): string { let text = ""; for (const att of attachments) { if (att.language) { text += `\n\n---\nπŸ“Ž ${att.name}\n~~~${att.language}\n${att.content}\n~~~`; } else { text += `\n\n---\nπŸ“Ž ${att.name}\n${att.content}`; } } return text; } function readChildOrchestratorMetadata(): ChildOrchestratorMetadata | null { const orchestratorTarget = process.env[SUBAGENT_ORCHESTRATOR_TARGET_ENV]?.trim(); const orchestratorSessionId = process.env[SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV]?.trim() || process.env[INTERCOM_SESSION_ID_ENV]?.trim(); const runId = process.env[SUBAGENT_RUN_ID_ENV]?.trim(); const agent = process.env[SUBAGENT_CHILD_AGENT_ENV]?.trim(); const index = process.env[SUBAGENT_CHILD_INDEX_ENV]?.trim(); if (!orchestratorTarget || !runId || !agent || !index) { return null; } const sessionName = process.env[SUBAGENT_INTERCOM_SESSION_NAME_ENV]?.trim(); return { orchestratorTarget, ...(orchestratorSessionId ? { orchestratorSessionId } : {}), runId, agent, index, ...(sessionName ? { sessionName } : {}), }; } function formatChildOrchestratorMessage(kind: "ask" | "update" | "interview", metadata: ChildOrchestratorMetadata, message: string): string { const heading = kind === "ask" ? "Subagent needs a supervisor decision." : kind === "interview" ? "Subagent requests a structured supervisor interview." : "Subagent progress update."; return [ heading, `Run: ${metadata.runId}`, `Agent: ${metadata.agent}`, `Child index: ${metadata.index}`, metadata.sessionName ? `Child intercom target: ${metadata.sessionName}` : undefined, "", message, ].filter((line): line is string => line !== undefined).join("\n"); } function validateSupervisorInterviewRequest(input: unknown): { ok: true; interview: SupervisorInterviewRequest } | { ok: false; error: string } { if (!input || typeof input !== "object" || Array.isArray(input)) { return { ok: false, error: "interview must be an object with a questions array" }; } const raw = input as Record; if (raw.title !== undefined && typeof raw.title !== "string") { return { ok: false, error: "interview.title must be a string when provided" }; } if (raw.description !== undefined && typeof raw.description !== "string") { return { ok: false, error: "interview.description must be a string when provided" }; } if (!Array.isArray(raw.questions) || raw.questions.length === 0) { return { ok: false, error: "interview.questions must be a non-empty array" }; } const validTypes = new Set(["single", "multi", "text", "image", "info"]); const ids = new Set(); const questions: SupervisorInterviewQuestion[] = []; for (let index = 0; index < raw.questions.length; index++) { const questionInput = raw.questions[index]; if (!questionInput || typeof questionInput !== "object" || Array.isArray(questionInput)) { return { ok: false, error: `interview.questions[${index}] must be an object` }; } const question = questionInput as Record; if (typeof question.id !== "string" || question.id.trim() === "") { return { ok: false, error: `interview.questions[${index}].id must be a non-empty string` }; } const id = question.id.trim(); if (ids.has(id)) { return { ok: false, error: `interview question id must be unique: ${id}` }; } ids.add(id); if (typeof question.type !== "string" || !validTypes.has(question.type)) { return { ok: false, error: `interview.questions[${index}].type must be one of: single, multi, text, image, info` }; } if (typeof question.question !== "string" || question.question.trim() === "") { return { ok: false, error: `interview.questions[${index}].question must be a non-empty string` }; } if (question.context !== undefined && typeof question.context !== "string") { return { ok: false, error: `interview.questions[${index}].context must be a string when provided` }; } let options: unknown[] | undefined; if (question.options !== undefined) { if (!Array.isArray(question.options)) { return { ok: false, error: `interview.questions[${index}].options must be an array when provided` }; } options = []; for (let optionIndex = 0; optionIndex < question.options.length; optionIndex++) { const option = question.options[optionIndex]; if (typeof option === "string") { const label = option.trim(); if (!label) { return { ok: false, error: `interview.questions[${index}].options[${optionIndex}] must not be empty` }; } options.push(label); } else if (!option || typeof option !== "object" || Array.isArray(option) || typeof (option as { label?: unknown }).label !== "string" || (option as { label: string }).label.trim() === "") { return { ok: false, error: `interview.questions[${index}].options[${optionIndex}] must be a non-empty string or an object with a non-empty label` }; } else { options.push({ ...option, label: (option as { label: string }).label.trim() }); } } } if ((question.type === "single" || question.type === "multi") && (!options || options.length === 0)) { return { ok: false, error: `interview.questions[${index}].options must be a non-empty array for ${question.type} questions` }; } if (question.type !== "single" && question.type !== "multi" && options) { return { ok: false, error: `interview.questions[${index}].options is only valid for single and multi questions` }; } questions.push({ ...question, id, type: question.type as SupervisorInterviewQuestion["type"], question: question.question.trim(), ...(options ? { options } : {}), }); } return { ok: true, interview: { ...raw, ...(typeof raw.title === "string" ? { title: raw.title.trim() } : {}), ...(typeof raw.description === "string" ? { description: raw.description.trim() } : {}), questions, }, }; } function interviewOptionLabel(option: unknown): string { return typeof option === "string" ? option : (option as { label: string }).label; } function interviewExampleValue(question: SupervisorInterviewQuestion): unknown { if (question.type === "multi") { return question.options?.slice(0, 2).map(interviewOptionLabel) ?? []; } if (question.type === "single") { return question.options?.[0] !== undefined ? interviewOptionLabel(question.options[0]) : "option label"; } if (question.type === "image") { return "image/file reference or description"; } return "answer text"; } function formatSupervisorInterviewRequest(interview: SupervisorInterviewRequest, message?: string): string { const lines: string[] = []; const title = interview.title?.trim(); if (title) lines.push(`Interview: ${title}`); const description = interview.description?.trim(); if (description) lines.push(description); const note = message?.trim(); if (note) lines.push(`Child note: ${note}`); if (lines.length > 0) lines.push(""); lines.push("Questions:"); interview.questions.forEach((question, index) => { lines.push(`${index + 1}. [${question.id}] (${question.type}) ${question.question}`); if (typeof question.context === "string" && question.context.trim()) { lines.push(` Context: ${question.context.trim()}`); } if (question.options?.length) { lines.push(" Options:"); for (const option of question.options) { lines.push(` - ${interviewOptionLabel(option)}`); } } }); const responseExample = { responses: interview.questions .filter((question) => question.type !== "info") .map((question) => ({ id: question.id, value: interviewExampleValue(question), })), }; lines.push( "", "Supervisor reply instructions:", "Reply with plain JSON or a fenced ```json block using this stable shape. Use the question ids exactly. Info questions are context-only and do not need responses. For single questions, value is one option label. For multi questions, value is an array of option labels. For text/image questions, value is a string unless the question asks otherwise.", "", "```json", JSON.stringify(responseExample, null, 2), "```", ); return lines.join("\n"); } function validateSupervisorInterviewReply(value: unknown, interview: SupervisorInterviewRequest): SupervisorInterviewReply { if (!value || typeof value !== "object" || Array.isArray(value)) { throw new Error("reply JSON must be an object with a responses array"); } const responsesInput = (value as Record).responses; if (!Array.isArray(responsesInput)) { throw new Error("reply JSON must include a responses array"); } const questionById = new Map(interview.questions .filter((question) => question.type !== "info") .map((question) => [question.id, question])); const seenIds = new Set(); const responses: SupervisorInterviewReply["responses"] = []; for (let index = 0; index < responsesInput.length; index++) { const response = responsesInput[index]; if (!response || typeof response !== "object" || Array.isArray(response)) { throw new Error(`responses[${index}] must be an object`); } const raw = response as Record; if (typeof raw.id !== "string" || raw.id.trim() === "") { throw new Error(`responses[${index}].id must be a non-empty string`); } const id = raw.id.trim(); const question = questionById.get(id); if (!question) { throw new Error(`responses[${index}].id must match a non-info interview question id`); } if (seenIds.has(id)) { throw new Error(`responses[${index}].id is duplicated: ${id}`); } seenIds.add(id); if (!Object.hasOwn(raw, "value")) { throw new Error(`responses[${index}].value is required`); } const value = raw.value; if (question.type === "single") { if (typeof value !== "string") throw new Error(`responses[${index}].value must be a string for single questions`); const optionLabels = new Set(question.options?.map(interviewOptionLabel)); if (!optionLabels.has(value.trim())) throw new Error(`responses[${index}].value must match one of the question options`); responses.push({ id, value: value.trim() }); continue; } if (question.type === "multi") { if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) { throw new Error(`responses[${index}].value must be an array of strings for multi questions`); } const optionLabels = new Set(question.options?.map(interviewOptionLabel)); const selected = value.map((item) => item.trim()); const invalid = selected.find((item) => !optionLabels.has(item)); if (invalid) throw new Error(`responses[${index}].value contains an option that is not in the question options: ${invalid}`); responses.push({ id, value: selected }); continue; } if (typeof value !== "string") { throw new Error(`responses[${index}].value must be a string for ${question.type} questions`); } responses.push({ id, value }); } return { responses }; } function parseStructuredSupervisorReply(text: string, interview: SupervisorInterviewRequest): { value?: SupervisorInterviewReply; error?: string } | undefined { const fencedMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/i); const candidate = (fencedMatch?.[1] ?? text).trim(); if (!candidate.startsWith("{") && !candidate.startsWith("[")) { return undefined; } try { return { value: validateSupervisorInterviewReply(JSON.parse(candidate), interview) }; } catch (error) { return { error: getErrorMessage(error) }; } } function duplicateSessionNames(sessions: SessionInfo[]): Set { return new Set( sessions .map(s => s.name?.toLowerCase()) .filter((name): name is string => Boolean(name)) .filter((name, index, names) => names.indexOf(name) !== index) ); } function parseSubagentIntercomPayload(payload: unknown): { to: string; message: string; requestId?: string } | null { if (typeof payload !== "object" || payload === null) { return null; } const record = payload as Record; if (typeof record.to !== "string" || typeof record.message !== "string") { return null; } const requestId = typeof record.requestId === "string" ? record.requestId : undefined; return { to: record.to, message: record.message, ...(requestId ? { requestId } : {}) }; } const GENERIC_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/; export function resolveIntercomSessionId( fallbackSessionId: string, environment: NodeJS.ProcessEnv = process.env, ): string { const harnessSessionId = environment[INTERCOM_SESSION_ID_ENV]?.trim(); if (harnessSessionId) { return harnessSessionId; } const genericSessionId = environment.AGENT_INTERCOM_SESSION_ID?.trim(); if (genericSessionId) { if (!GENERIC_SESSION_ID_PATTERN.test(genericSessionId)) { throw new Error("Invalid AGENT_INTERCOM_SESSION_ID: must match ^[A-Za-z0-9_-]{1,128}$"); } return genericSessionId; } return fallbackSessionId; } export function resolveIntercomPresenceName( sessionName: string | undefined, sessionId: string, environment: NodeJS.ProcessEnv = process.env, ): string { const trimmedName = sessionName?.trim(); if (trimmedName) { return trimmedName; } const subagentName = environment[SUBAGENT_INTERCOM_SESSION_NAME_ENV]?.trim(); if (subagentName) { return subagentName; } const genericName = environment.AGENT_INTERCOM_SESSION_NAME?.trim(); if (genericName) { return genericName; } const normalizedSessionId = sessionId.startsWith("session-") ? sessionId.slice("session-".length) : sessionId; return `${DEFAULT_UNNAMED_SESSION_ALIAS_PREFIX}-${normalizedSessionId.slice(0, 8)}`; } function buildPresenceIdentity(pi: ExtensionAPI, sessionId: string, environment: NodeJS.ProcessEnv = process.env): { name: string } { return { name: resolveIntercomPresenceName(pi.getSessionName(), sessionId, environment), }; } function formatSessionLabel(session: SessionInfo, duplicates: Set, idPrefix: string): string { if (!session.name) { return sanitizeDisplayText(idPrefix, session.id); } const name = formatSessionDisplayName(session, "Unnamed session"); return duplicates.has(session.name.toLowerCase()) ? `${name} (${sanitizeDisplayText(idPrefix, session.id)})` : name; } function formatSessionListRow(session: SessionInfo, currentCwd: string, isSelf: boolean, idPrefix: string): string { const name = sanitizeDisplayText(session.name, "Unnamed session"); const cwd = sanitizeDisplayText(session.cwd, "Unknown path"); const model = sanitizeDisplayText(session.model, "Unknown model"); const status = sanitizeDisplayText(session.status); const tags = [isSelf ? "self" : session.cwd === currentCwd ? "same cwd" : undefined, sessionOriginLabel(session), status || undefined] .filter((tag): tag is string => Boolean(tag)); const suffix = tags.length ? ` [${tags.join(", ")}]` : ""; return `β€’ ${name} (${sanitizeDisplayText(idPrefix, session.id)}) β€” ${cwd} (${model})${suffix}`; } function previewText(value: unknown, maxLength = 72): string | undefined { if (typeof value !== "string") { return undefined; } const normalized = value.replace(/\s+/g, " ").trim(); if (!normalized) { return undefined; } return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}…` : normalized; } function firstTextContent(result: { content?: Array<{ type: string; text?: string }> }): string { return result.content?.find((item) => item.type === "text" && typeof item.text === "string")?.text?.replace(/\*\*/g, "") ?? ""; } export function chooseContactTarget(currentSession: SessionInfo, sessions: SessionInfo[]): { target: string; name?: string; id: string; duplicateName: boolean } { const duplicates = duplicateSessionNames(sessions); const name = currentSession.name?.trim() || undefined; const duplicateName = Boolean(name && duplicates.has(name.toLowerCase())); return { target: name && !duplicateName ? name : currentSession.id, ...(name ? { name } : {}), id: currentSession.id, duplicateName, }; } export function formatContactInstruction(contact: { target: string; id: string; name?: string; duplicateName?: boolean }): string { return contact.target === contact.id ? `Intercom target: ${contact.id}` : `Intercom target: ${contact.target}\nStable session ID: ${contact.id}`; } interface ClipboardCopyResult { ok: boolean; method?: string; error?: string; } function runDetachedClipboardCommand(command: string, args: string[], text: string): ClipboardCopyResult { const found = spawnSync("which", [command], { stdio: "ignore", timeout: 1000 }); if (found.status !== 0) return { ok: false, error: `${command} not found` }; try { const proc = spawn(command, args, { stdio: ["pipe", "ignore", "ignore"] }); proc.stdin.on("error", () => { // Ignore EPIPE if the clipboard helper exits early. }); proc.stdin.write(text); proc.stdin.end(); proc.unref(); return { ok: true, method: command }; } catch (error) { return { ok: false, error: getErrorMessage(error) }; } } function runClipboardCommand(command: string, args: string[], text: string): ClipboardCopyResult { if (command === "wl-copy") return runDetachedClipboardCommand(command, args, text); const result = spawnSync(command, args, { input: text, encoding: "utf8", timeout: 2000, stdio: ["pipe", "ignore", "pipe"], }); if (result.status === 0) return { ok: true, method: command }; if (result.error) return { ok: false, error: result.error.message }; return { ok: false, error: result.stderr?.toString().trim() || `${command} exited ${result.status}` }; } export function copyTextToClipboard(text: string): ClipboardCopyResult { const candidates: Array<[string, string[]]> = []; if (process.platform === "darwin") candidates.push(["pbcopy", []]); else if (process.platform === "win32") candidates.push(["clip.exe", []]); else { if (process.env.WAYLAND_DISPLAY) candidates.push(["wl-copy", []]); if (process.env.DISPLAY) { candidates.push(["xclip", ["-selection", "clipboard"]]); candidates.push(["xsel", ["--clipboard", "--input"]]); } candidates.push(["clip.exe", []]); } let lastError = "No clipboard command available"; for (const [command, args] of candidates) { const result = runClipboardCommand(command, args, text); if (result.ok) return result; lastError = result.error ?? lastError; } return { ok: false, error: lastError }; } function getNamePollMs(): number { const configured = process.env[NAME_POLL_MS_ENV]; if (configured !== undefined) { const value = Number(configured); if (Number.isFinite(value) && value > 0) { return value; } } return 1000; } export default function piIntercomExtension(pi: ExtensionAPI) { let runtimeScopeId = intercomScopeIdFromEnvForRegistration(); const initialHarnessSessionId = process.env[INTERCOM_SESSION_ID_ENV]?.trim(); const initialGenericSessionId = process.env.AGENT_INTERCOM_SESSION_ID?.trim(); if (initialGenericSessionId && !initialHarnessSessionId) { if (!GENERIC_SESSION_ID_PATTERN.test(initialGenericSessionId)) { throw new Error("Invalid AGENT_INTERCOM_SESSION_ID: must match ^[A-Za-z0-9_-]{1,128}$"); } } const configuredSessionId = initialHarnessSessionId || initialGenericSessionId; let client: IntercomClient | null = null; const config: IntercomConfig = loadConfig(); const bossTeamScope = readBossTeamScope(); const askWaitMs = getAskWaitMs(); let runtimeContext: ExtensionContext | null = null; let currentSessionId: string | null = null; let currentModel = "unknown"; let sessionStartedAt: number | null = null; let runtimeInstanceId: string | null = null; let reconnectTimer: NodeJS.Timeout | null = null; let namePollTimer: NodeJS.Timeout | null = null; let lastPresenceName: string | null = null; const previousIntercomSessionId = process.env[INTERCOM_SESSION_ID_ENV]; let reconnectPromise: Promise | null = null; let reconnectPromiseGeneration: number | null = null; let startupConnectTimer: NodeJS.Timeout | null = null; let reconnectAttempt = 0; let shuttingDown = false; let disposed = true; let runtimeStarted = false; let runtimeGeneration = 0; let agentRunning = false; const activeTools = new Map(); const replyTracker = new ReplyTracker(); const registeredControlTypes = new Set(); let inboundInbox: PersistentInboundInbox | null = null; let inboundFirstQueuedAt: number | null = null; let inboundLastQueuedAt: number | null = null; let inboundFlushTimer: NodeJS.Timeout | null = null; let controlRegistrationGraceUntil = 0; const replyWaiters = new Map void; reject: (error: Error) => void; }>(); function hasReplyWaiterForTarget(from: string): boolean { return Array.from(replyWaiters.values()).some((waiter) => waiter.from.toLowerCase() === from.toLowerCase()); } function waitForReply(from: string, replyTo: string, signal?: AbortSignal, onCancel?: () => void): Promise { if (hasReplyWaiterForTarget(from)) { return Promise.reject(new Error(`Already waiting for a reply from "${from}"`)); } if (signal?.aborted) { return Promise.reject(new Error("Cancelled")); } return new Promise((resolve, reject) => { const timeout = setTimeout(() => { rejectReplyWaiter(replyTo, new AskWaitElapsedError(from, replyTo, askWaitMs)); }, askWaitMs); const cleanup = () => { clearTimeout(timeout); signal?.removeEventListener("abort", onAbort); replyWaiters.delete(replyTo); }; const onAbort = () => { onCancel?.(); cleanup(); reject(new Error("Cancelled")); }; signal?.addEventListener("abort", onAbort, { once: true }); replyWaiters.set(replyTo, { from, replyTo, resolve: (message) => { cleanup(); resolve(message); }, reject: (error) => { cleanup(); reject(error); }, }); }); } function rejectReplyWaiter(replyTo: string, error: Error): void { replyWaiters.get(replyTo)?.reject(error); } function rejectAllReplyWaiters(error: Error): void { for (const waiter of Array.from(replyWaiters.values())) { waiter.reject(error); } } function clearReconnectTimer(): void { if (!reconnectTimer) { return; } clearTimeout(reconnectTimer); reconnectTimer = null; } function clearStartupConnectTimer(): void { if (!startupConnectTimer) { return; } clearTimeout(startupConnectTimer); startupConnectTimer = null; } function clearNamePollTimer(): void { if (!namePollTimer) { return; } clearInterval(namePollTimer); namePollTimer = null; } function clearInboundFlushTimer(): void { if (!inboundFlushTimer) { return; } clearTimeout(inboundFlushTimer); inboundFlushTimer = null; } function getLiveContext(ctx: ExtensionContext | null = runtimeContext, generation = runtimeGeneration): ExtensionContext | null { if (disposed || shuttingDown || generation !== runtimeGeneration || !ctx) { return null; } try { if (currentSessionId) { const expectedSessionId = configuredSessionId || ctx.sessionManager.getSessionId(); if (expectedSessionId !== currentSessionId) { return null; } } void ctx.hasUI; return ctx; } catch { // A context that throws while reading session/UI state is no longer usable. return null; } } function notifyIfLive(ctx: ExtensionContext, message: string, level: "info" | "warning" | "error", generation = runtimeGeneration): void { const liveContext = getLiveContext(ctx, generation); if (!liveContext?.hasUI) { return; } try { liveContext.ui.notify(message, level); } catch { // The UI can disappear during session shutdown/reload while async overlay work is settling. } } function getReconnectDelayMs(): number { const backoffMs = [1000, 2000, 5000, 10000, 30000]; return backoffMs[Math.min(reconnectAttempt, backoffMs.length - 1)]!; } function currentStatus(): string { const activeToolName = activeTools.values().next().value; const lifecycleStatus = activeToolName ? `tool:${activeToolName}` : agentRunning ? "thinking" : "idle"; const queueStatus = inboundInbox?.size ? ` Β· inbox:${inboundInbox.size}` : ""; const outboxStatus = client?.outboxSize ? ` Β· outbox:${client.outboxSize}` : ""; return config.status ? `${lifecycleStatus}${queueStatus}${outboxStatus} Β· ${config.status}` : `${lifecycleStatus}${queueStatus}${outboxStatus}`; } function buildRegistration(): SessionRegistration { const liveContext = getLiveContext(); if (!liveContext || !currentSessionId || sessionStartedAt === null) { throw new Error("Intercom runtime not initialized"); } const identity = buildPresenceIdentity(pi, currentSessionId); return { name: identity.name, cwd: liveContext.cwd, model: currentModel, pid: process.pid, startedAt: sessionStartedAt, lastActivity: Date.now(), status: currentStatus(), ...(runtimeInstanceId ? { runtimeInstanceId } : {}), }; } function syncPresenceIdentity(sessionId?: string): void { if (!client || !getLiveContext()) { return; } const resolvedId = currentSessionId ?? (configuredSessionId || (sessionId ? sessionId : "default")); const identity = buildPresenceIdentity(pi, resolvedId); lastPresenceName = identity.name; client.updatePresence({ ...identity, status: currentStatus() }); } function startNamePoll(): void { clearNamePollTimer(); lastPresenceName = currentSessionId ? buildPresenceIdentity(pi, currentSessionId).name : null; namePollTimer = setInterval(() => { if (!currentSessionId || !getLiveContext()) { return; } const identity = buildPresenceIdentity(pi, currentSessionId); if (identity.name !== lastPresenceName) { syncPresenceIdentity(currentSessionId); } }, getNamePollMs()); namePollTimer.unref?.(); } function publishIntercomSessionId(sessionId: string): void { process.env[INTERCOM_SESSION_ID_ENV] = sessionId; } async function switchRuntimeScope(nextScopeId: string): Promise { if (runtimeScopeId === nextScopeId && process.env[INTERCOM_SCOPE_ENV] === nextScopeId) { return; } runtimeScopeId = nextScopeId; process.env[INTERCOM_SCOPE_ENV] = nextScopeId; const previousClient = client; client = null; if (previousClient) { await previousClient.disconnect(true).catch(() => undefined); } await ensureConnected("tool"); } function restoreIntercomSessionId(): void { if (previousIntercomSessionId === undefined) { delete process.env[INTERCOM_SESSION_ID_ENV]; return; } process.env[INTERCOM_SESSION_ID_ENV] = previousIntercomSessionId; } function syncPresenceStatus(): void { if (!client || !currentSessionId || !getLiveContext()) { return; } client.updatePresence({ status: currentStatus() }); } function currentSessionTargetMatches(to: string, resolvedTo?: string | null, activeClient?: IntercomClient): boolean { const targets = new Set(); const addTarget = (target: string | undefined | null) => { const trimmed = target?.trim(); if (trimmed) targets.add(trimmed.toLowerCase()); }; addTarget(currentSessionId); addTarget(activeClient?.sessionId); addTarget(pi.getSessionName()); if (currentSessionId) addTarget(buildPresenceIdentity(pi, currentSessionId).name); return Boolean(resolvedTo && activeClient?.sessionId && resolvedTo === activeClient.sessionId) || targets.has(to.trim().toLowerCase()); } function shouldTriggerInboundMessage(entry: InboundMessageEntry, forceTrigger = false): boolean { if (forceTrigger) { return true; } if (config.inboundTrigger === "always") { return true; } if (config.inboundTrigger === "replies") { return Boolean(entry.message.replyTo); } return false; } function entryFromStoredMessage(stored: StoredInboundMessage, batchSize: number): InboundMessageEntry { const attachmentText = stored.message.content.attachments?.length ? formatAttachments(stored.message.content.attachments) : ""; const replyCommand = config.replyHint && stored.message.expectsReply ? batchSize === 1 ? `intercom_reply({ message: "..." })` : `intercom_reply({ to: "${stored.from.id}", message: "..." })` : undefined; return { key: stored.key, from: stored.from, message: stored.message, receivedAt: stored.receivedAt, replyCommand, bodyText: `${stored.message.content.text}${attachmentText}`, }; } function formatIncomingEntry(entry: InboundMessageEntry, position?: { index: number; total: number }): string { const senderDisplay = formatSessionDisplayName(entry.from); const prefix = position ? `[${position.index}/${position.total}] ` : ""; const replyInstruction = entry.replyCommand ? `\n\nTo reply, use: ${entry.replyCommand}` : ""; return `**${prefix}πŸ“¨ From ${senderDisplay}** (${entry.from.cwd})${replyInstruction}\n\n${entry.bodyText}`; } function sendIncomingBatch(entries: InboundMessageEntry[], generation = runtimeGeneration, forceTrigger = false): void { if (entries.length === 0) return; if (runtimeStarted && !getLiveContext(runtimeContext, generation)) { return; } const readAt = Date.now(); const displayedEntries = entries.map((entry) => ({ ...entry, readAt })); const contexts = entries.map((entry) => replyTracker.recordIncomingMessage(entry.from, entry.message, entry.receivedAt)); replyTracker.queueTurnContexts(contexts); const content = displayedEntries.length === 1 ? formatIncomingEntry(displayedEntries[0]!) : `**πŸ“¨ Intercom batch (${displayedEntries.length} messages)**\n\n${displayedEntries.map((entry, index) => formatIncomingEntry(entry, { index: index + 1, total: displayedEntries.length })).join("\n\n---\n\n")}`; const triggerTurn = forceTrigger || entries.some((entry) => shouldTriggerInboundMessage(entry)); pi.sendMessage( { customType: "intercom_message", content, display: true, details: { entries: displayedEntries } satisfies InboundMessageBatchDetails, }, triggerTurn ? { triggerTurn: true } : { deliverAs: "followUp" } ); } function refreshInboundBatchWindow(): void { const pending = inboundInbox?.list() ?? []; inboundFirstQueuedAt = pending.length > 0 ? pending[0]!.receivedAt : null; inboundLastQueuedAt = pending.length > 0 ? pending[pending.length - 1]!.receivedAt : null; } function scheduleInboundFlush(delayMs?: number): void { if (!getLiveContext()) { return; } if (!inboundInbox || inboundInbox.size === 0) return; const scheduledGeneration = runtimeGeneration; const now = Date.now(); const computedDelay = delayMs ?? Math.max(0, Math.min( (inboundLastQueuedAt ?? now) + INBOUND_BATCH_QUIET_MS, (inboundFirstQueuedAt ?? now) + INBOUND_BATCH_MAX_LATENCY_MS, ) - now); clearInboundFlushTimer(); inboundFlushTimer = setTimeout(() => { inboundFlushTimer = null; flushIdleMessages(scheduledGeneration); }, computedDelay); inboundFlushTimer.unref?.(); } function flushIdleMessages(generation = runtimeGeneration): void { if (!inboundInbox || inboundInbox.size === 0) { return; } const ctx = getLiveContext(runtimeContext, generation); if (!ctx) { return; } let isIdle: boolean; try { isIdle = ctx.isIdle(); } catch { // Stale contexts are cleaned up by shutdown/reload; do not deliver queued messages through them. return; } if (!isIdle) { scheduleInboundFlush(INBOUND_IDLE_RETRY_MS); return; } const stored = inboundInbox.list(); const regularMessages = stored.filter((entry) => !deliverRegisteredControl(entry)); if (regularMessages.length === 0) return; const registrationGraceRemaining = controlRegistrationGraceUntil - Date.now(); if ( registrationGraceRemaining > 0 && regularMessages.some((entry) => Boolean(entry.message.content.control)) ) { scheduleInboundFlush(registrationGraceRemaining); return; } const entries = regularMessages.map((entry) => entryFromStoredMessage(entry, regularMessages.length)); try { sendIncomingBatch(entries, generation); inboundInbox.consume(regularMessages.map((entry) => entry.key)); refreshInboundBatchWindow(); syncPresenceStatus(); } catch (error) { pi.appendEntry("intercom_inbox_delivery_error", { messageIds: stored.map((entry) => entry.message.id), error: getErrorMessage(error), timestamp: Date.now(), }); scheduleInboundFlush(INBOUND_IDLE_RETRY_MS); } } function deliverRegisteredControl(stored: StoredInboundMessage): boolean { const control = stored.message.content.control; if (!control || !registeredControlTypes.has(intercomControlKey(control))) { return false; } const inboxAtDelivery = inboundInbox; if (!inboxAtDelivery) return false; // Persist consumption before notifying another extension. If the control // triggers a runtime reload, reconnect/replay must not deliver it twice. inboxAtDelivery.consume([stored.key]); refreshInboundBatchWindow(); syncPresenceStatus(); const event: IntercomControlReceivedEvent = { from: { id: stored.from.id, ...(stored.from.name ? { name: stored.from.name } : {}), cwd: stored.from.cwd, model: stored.from.model, ...(stored.from.origin ? { origin: stored.from.origin } : {}), ...(stored.from.parentSessionId ? { parentSessionId: stored.from.parentSessionId } : {}), ...(stored.from.rootSessionId ? { rootSessionId: stored.from.rootSessionId } : {}), }, messageId: stored.message.id, receivedAt: stored.receivedAt, control, }; try { pi.events.emit(INTERCOM_CONTROL_RECEIVED_EVENT, event); } catch (error) { pi.appendEntry("intercom_control_handler_error", { from: stored.from.id, messageId: stored.message.id, controlType: control.type, controlVersion: control.version, error: getErrorMessage(error), timestamp: Date.now(), }); } return true; } async function handleIncomingMessage(ctx: ExtensionContext, from: SessionInfo, message: Message, deliveryId: string, receivingClient: IntercomClient): Promise { const messageGeneration = runtimeGeneration; const liveContext = getLiveContext(ctx, messageGeneration); const inboxAtReceive = inboundInbox; if (!liveContext || !inboxAtReceive) { return; } const inboundAuthorization = authorizeBossSender(bossTeamScope, from.id, receivingClient.sessionId); const controllerReadinessProbe = isBossControllerReadinessControl( bossTeamScope, "inbound", from.id, receivingClient.sessionId, message.content.control, ); if ("code" in inboundAuthorization && !controllerReadinessProbe) { receivingClient.rejectMessage(deliveryId, `${inboundAuthorization.code}: ${inboundAuthorization.error}`); pi.appendEntry("intercom_inbox_policy_denied", { from: from.id, messageId: message.id, code: inboundAuthorization.code, error: inboundAuthorization.error, timestamp: Date.now(), }); return; } let enqueued; try { enqueued = inboxAtReceive.enqueue(from, message); } catch (error) { if (error instanceof InboundMessageConflictError) { receivingClient.rejectMessage(deliveryId, error.message); } pi.appendEntry("intercom_inbox_persist_error", { from: from.id, messageId: message.id, error: getErrorMessage(error), timestamp: Date.now(), }); return; } receivingClient.acknowledgeMessage(deliveryId); if (enqueued.duplicate) return; if (deliverRegisteredControl(enqueued.entry)) return; pi.events.emit(INTERCOM_INBOUND_ACTIVITY_EVENT, { from: { id: from.id, ...(from.name ? { name: from.name } : {}) }, message: { id: message.id, timestamp: message.timestamp, ...(message.replyTo ? { replyTo: message.replyTo } : {}), ...(message.expectsReply ? { expectsReply: true } : {}), }, receivedAt: enqueued.entry.receivedAt, }); const replyWaiter = message.replyTo ? replyWaiters.get(message.replyTo) : undefined; if (replyWaiter) { const senderTarget = from.name || from.id; const fromMatches = bossTeamScope.present ? from.id === replyWaiter.from : senderTarget.toLowerCase() === replyWaiter.from.toLowerCase() || from.id === replyWaiter.from; if (fromMatches) { replyWaiter.resolve(message); inboxAtReceive.consume([enqueued.entry.key]); refreshInboundBatchWindow(); syncPresenceStatus(); return; } } replyTracker.recordIncomingMessage(from, message, enqueued.entry.receivedAt); if (inboundFirstQueuedAt === null) inboundFirstQueuedAt = enqueued.entry.receivedAt; inboundLastQueuedAt = enqueued.entry.receivedAt; syncPresenceStatus(); scheduleInboundFlush(); } function attachClientHandlers(nextClient: IntercomClient): void { nextClient.on("message", (from: SessionInfo, message: Message, deliveryId: string) => { const liveContext = getLiveContext(); if (client !== nextClient || !liveContext) { return; } void handleIncomingMessage(liveContext, from, message, deliveryId, nextClient).catch((error) => { pi.appendEntry("intercom_inbox_error", { from: from.id, messageId: message.id, error: getErrorMessage(error), timestamp: Date.now(), }); }); }); nextClient.on("ask_deferred", (messageId: string, fromSessionId: string) => { const authorization = authorizeBossSender(bossTeamScope, fromSessionId, nextClient.sessionId); if ("code" in authorization) { pi.appendEntry("intercom_inbox_policy_denied", { from: fromSessionId, messageId, code: authorization.code, error: authorization.error, event: "ask_deferred", timestamp: Date.now() }); return; } replyTracker.markDeferred(messageId, fromSessionId); }); nextClient.on("ask_cancelled", (messageId: string, fromSessionId: string) => { const authorization = authorizeBossSender(bossTeamScope, fromSessionId, nextClient.sessionId); if ("code" in authorization) { pi.appendEntry("intercom_inbox_policy_denied", { from: fromSessionId, messageId, code: authorization.code, error: authorization.error, event: "ask_cancelled", timestamp: Date.now() }); return; } replyTracker.dismissPendingAsk(messageId, fromSessionId); inboundInbox?.dismissPendingAsk(messageId, fromSessionId); const cancelledKeys = (inboundInbox?.list() ?? []) .filter((entry) => entry.from.id === fromSessionId && entry.message.id === messageId) .map((entry) => entry.key); inboundInbox?.consume(cancelledKeys); refreshInboundBatchWindow(); syncPresenceStatus(); }); nextClient.on("outbox_delivered", (messageId: string, deliveryId: string) => { pi.appendEntry("intercom_outbox_delivered", { messageId, deliveryId, timestamp: Date.now() }); syncPresenceStatus(); }); nextClient.on("outbox_failed", (messageId: string, code: string, reason: string) => { pi.appendEntry("intercom_outbox_failed", { messageId, code, reason, timestamp: Date.now() }); syncPresenceStatus(); }); nextClient.on("disconnected", (error: Error) => { if (client !== nextClient) { return; } rejectAllReplyWaiters(new Error(`Disconnected while waiting for reply: ${error.message}`, { cause: error })); client = null; if (!shuttingDown && !disposed) { clearReconnectTimer(); scheduleReconnect(); } }); nextClient.on("error", () => { // Keep broker/socket noise out of the TUI. Reconnect logic runs from the disconnect path. }); } function scheduleReconnect(): void { if (disposed || shuttingDown || reconnectTimer || reconnectPromise || !getLiveContext()) { return; } const scheduledGeneration = runtimeGeneration; reconnectTimer = setTimeout(() => { reconnectTimer = null; if (scheduledGeneration !== runtimeGeneration || !getLiveContext()) { return; } reconnectAttempt += 1; void ensureConnected("background").catch(() => { // ensureConnected("background") already queued the next retry. }); }, getReconnectDelayMs()); } async function ensureConnected(reason: "startup" | "background" | "tool" | "overlay"): Promise { if (!config.enabled) { throw new Error("Intercom disabled"); } if (disposed || shuttingDown) { throw new Error("Intercom shutting down"); } if (bossTeamScope.present) { const selfError = currentSessionId ? bossSelfSessionError(bossTeamScope, currentSessionId) : "Boss session identity is unavailable"; if (selfError) throw new BossTeamScopeError("BOSS_TEAM_METADATA_INVALID", selfError); } if (client && client.isConnected()) { return client; } const contextAtStart = getLiveContext(); const generationAtStart = runtimeGeneration; if (!contextAtStart || !currentSessionId || sessionStartedAt === null) { throw new Error("Intercom runtime not initialized"); } clearReconnectTimer(); if (reconnectPromise && reconnectPromiseGeneration === generationAtStart) { return reconnectPromise; } const nextReconnectPromise = (async () => { const nextClient = new IntercomClient({ authorizeOutboxReplayTarget: () => !bossTeamScope.present, ...(runtimeScopeId === undefined ? { env: {} } : { scopeId: runtimeScopeId }), }); client = nextClient; attachClientHandlers(nextClient); try { await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs); await nextClient.connect(buildRegistration(), currentSessionId); if (!getLiveContext(contextAtStart, generationAtStart)) { await nextClient.disconnect(true); throw new Error("Intercom runtime no longer active"); } client = nextClient; reconnectAttempt = 0; return nextClient; } catch (error) { if (client === nextClient) { client = null; } if (reason === "background" && getLiveContext(contextAtStart, generationAtStart)) { scheduleReconnect(); } throw toError(error); } finally { if (reconnectPromise === nextReconnectPromise) { reconnectPromise = null; reconnectPromiseGeneration = null; } } })(); reconnectPromise = nextReconnectPromise; reconnectPromiseGeneration = generationAtStart; return nextReconnectPromise; } async function resolveSessionTarget(activeClient: IntercomClient, nameOrId: string): Promise { const sessions = await activeClient.listSessions(); const byId = sessions.find(s => s.id === nameOrId); if (byId) { return byId.id; } const lowerName = nameOrId.toLowerCase(); const byName = sessions.filter(s => s.name?.toLowerCase() === lowerName); if (byName.length > 1) { throw new Error(`Multiple sessions named "${nameOrId}" are connected. Use the session ID instead.`); } if (byName.length === 1) { return byName[0]!.id; } const byIdPrefix = sessions.filter(s => s.id.startsWith(nameOrId)); if (byIdPrefix.length === 1) { return byIdPrefix[0]!.id; } if (byIdPrefix.length > 1) { throw new Error(`Multiple sessions match ID prefix "${nameOrId}". Use a longer session ID prefix.`); } return null; } async function resolveAuthorizedTarget(activeClient: IntercomClient, target: string): Promise { if (bossTeamScope.present) { // Every Boss participant uses exact stable session IDs. Team-only mode // additionally applies the role allowlist; local visibility broadens the // set of sessions, never the target resolution semantics. const resolution = resolveBossLiveTarget(bossTeamScope, target, await activeClient.listSessions(), activeClient.sessionId); if ("code" in resolution) throw new BossTeamScopeError(resolution.code, resolution.error); return resolution.targetId; } return await resolveSessionTarget(activeClient, target) ?? target; } async function resolveCurrentIntercomTeam(activeClient: IntercomClient) { const sessions = await activeClient.listSessions(); const team = bossTeamScope.restricted ? resolveBossIntercomTeam({ selfId: activeClient.sessionId, sessions, scope: bossTeamScope }) : await resolveIntercomTeam({ selfId: activeClient.sessionId, sessions }); return { sessions, team }; } async function loadPendingAskContexts(activeClient: IntercomClient, requestedSession?: string): Promise<{ inboxSessionId: string; contexts: IntercomContext[]; }> { if (!requestedSession || requestedSession === activeClient.sessionId) { inboundInbox?.prunePendingAsks(getAskTimeoutMs()); return { inboxSessionId: activeClient.sessionId, contexts: replyTracker.listPending() }; } const { sessions, team } = await resolveCurrentIntercomTeam(activeClient); const liveSession = resolveManagedInboxSession({ team, sessions, requestedSession }); const inboxSessionId = liveSession.id; const cutoff = Date.now() - getAskTimeoutMs(); const contexts = readPendingAsksSnapshot(inboxSessionId) .filter((entry) => entry.receivedAt >= cutoff) .map(({ from, message, receivedAt }) => ({ from, message, receivedAt })); return { inboxSessionId, contexts }; } async function resolveSupervisorTarget(activeClient: IntercomClient, metadata: ChildOrchestratorMetadata): Promise { if (metadata.orchestratorSessionId) { const bySessionId = await resolveSessionTarget(activeClient, metadata.orchestratorSessionId); if (bySessionId) { return bySessionId; } } return await resolveSessionTarget(activeClient, metadata.orchestratorTarget) ?? metadata.orchestratorTarget; } function deliverLocalSubagentRelayMessage(sender: "subagent-control" | "subagent-result" | "fleet-lifecycle", status: string, messageText: string): void { const liveContext = getLiveContext(); const now = Date.now(); sendIncomingBatch([{ from: { id: sender, name: sender, cwd: liveContext?.cwd ?? "", model: sender, pid: process.pid, startedAt: now, lastActivity: now, status, }, message: { id: randomUUID(), timestamp: now, content: { text: messageText }, }, receivedAt: now, bodyText: messageText, }], runtimeGeneration, true); } function recordSubagentDeliveryError(entryType: string, to: string, message: string, error: unknown): void { pi.appendEntry(entryType, { to, message, error: getErrorMessage(error), timestamp: Date.now(), }); } function startSessionRuntime(ctx: ExtensionContext): void { const previousClient = client; if (previousClient) { client = null; void previousClient.disconnect(true).catch(() => undefined); } shuttingDown = false; disposed = false; runtimeStarted = true; runtimeGeneration += 1; reconnectAttempt = 0; clearReconnectTimer(); clearStartupConnectTimer(); clearNamePollTimer(); clearInboundFlushTimer(); rejectAllReplyWaiters(new Error("Session replaced")); replyTracker.reset(); runtimeContext = ctx; currentSessionId = configuredSessionId || ctx.sessionManager.getSessionId(); publishIntercomSessionId(currentSessionId); if (bossTeamScope.present && bossSelfSessionError(bossTeamScope, currentSessionId)) { const staleOutbox = new PersistentOutboundOutbox(currentSessionId); const removed = staleOutbox.list().length; staleOutbox.clear(); if (removed > 0) pi.appendEntry("intercom_outbox_policy_denied", { removed, reason: "invalid Boss metadata or session identity", timestamp: Date.now() }); } inboundInbox = new PersistentInboundInbox(currentSessionId); controlRegistrationGraceUntil = Date.now() + CONTROL_REGISTRATION_GRACE_MS; const initialRecovered = inboundInbox.list(); const deniedPendingKeys = new Set(); for (const recovered of initialRecovered) { const authorization = authorizeBossSender(bossTeamScope, recovered.from.id, currentSessionId); if ("code" in authorization) { inboundInbox.consume([recovered.key]); inboundInbox.dismissPendingAsk(recovered.message.id, recovered.from.id); deniedPendingKeys.add(`${recovered.from.id}\0${recovered.message.id}`); pi.appendEntry("intercom_inbox_policy_denied", { from: recovered.from.id, messageId: recovered.message.id, code: authorization.code, error: authorization.error, event: "recovery", timestamp: Date.now() }); } } for (const pendingAsk of inboundInbox.listPendingAsks()) { const authorization = authorizeBossSender(bossTeamScope, pendingAsk.from.id, currentSessionId); if ("code" in authorization) { inboundInbox.dismissPendingAsk(pendingAsk.message.id, pendingAsk.from.id); const key = `${pendingAsk.from.id}\0${pendingAsk.message.id}`; if (!deniedPendingKeys.has(key)) { pi.appendEntry("intercom_inbox_policy_denied", { from: pendingAsk.from.id, messageId: pendingAsk.message.id, code: authorization.code, error: authorization.error, event: "pending_recovery", timestamp: Date.now() }); } } } inboundInbox.prunePendingAsks(getAskTimeoutMs()); const authorizedRecovered = inboundInbox.list(); for (const recovered of authorizedRecovered) { replyTracker.recordIncomingMessage(recovered.from, recovered.message, recovered.receivedAt); } for (const pendingAsk of inboundInbox.listPendingAsks()) { replyTracker.recordIncomingMessage(pendingAsk.from, pendingAsk.message, pendingAsk.receivedAt); } refreshInboundBatchWindow(); publishIntercomSessionId(currentSessionId); currentModel = ctx.model?.id ?? "unknown"; sessionStartedAt = Date.now(); runtimeInstanceId = randomUUID(); lastPresenceName = buildPresenceIdentity(pi, currentSessionId).name; agentRunning = false; activeTools.clear(); startNamePoll(); if (authorizedRecovered.length > 0) scheduleInboundFlush(0); const startupGeneration = runtimeGeneration; startupConnectTimer = setTimeout(() => { startupConnectTimer = null; if (!getLiveContext(ctx, startupGeneration)) { return; } void ensureConnected("startup").catch(() => { if (!getLiveContext(ctx, startupGeneration)) { return; } client = null; scheduleReconnect(); }); }, 0); } function emitControlDelivery(event: IntercomControlDeliveryEvent): void { pi.events.emit(INTERCOM_CONTROL_DELIVERY_EVENT, event); } function relayControlSendRequest(payload: unknown): void { const parsed = parseIntercomControlSendRequest(payload); if (!parsed) return; if (parseBossControl(parsed.control) !== null) { emitControlDelivery({ requestId: parsed.requestId, delivered: false, code: "CONTROL_DISPATCH_UNAVAILABLE", error: "Boss typed control is unavailable until the durable Controller delivery authority is installed", }); return; } const relayGeneration = runtimeGeneration; void (async () => { const relayStillLive = () => !runtimeStarted || Boolean(getLiveContext(runtimeContext, relayGeneration)); if (!relayStillLive()) return; let activeClient: IntercomClient; let target: string; try { activeClient = await ensureConnected("background"); if (isBossControllerReadinessControl(bossTeamScope, "outbound", parsed.to, activeClient.sessionId, parsed.control)) { const exactController = (await activeClient.listSessions()).find((session) => session.id === parsed.to); if (!exactController) throw new BossTeamScopeError("BOSS_TEAM_TARGET_NOT_CONNECTED", `Boss Controller exact session ID "${parsed.to}" is not connected`); target = exactController.id; } else { target = await resolveAuthorizedTarget(activeClient, parsed.to); } if (currentSessionTargetMatches(parsed.to, target, activeClient)) { throw new Error("Intercom controls cannot target the current session"); } } catch (error) { if (!relayStillLive()) return; emitControlDelivery({ requestId: parsed.requestId, delivered: false, error: getErrorMessage(error), }); return; } try { const result = await activeClient.send(target, { text: parsed.fallbackText ?? `[Intercom control ${parsed.control.type}@${parsed.control.version}; compatible extension required]`, control: parsed.control, messageId: parsed.messageId, }); if (!relayStillLive()) return; emitControlDelivery({ requestId: parsed.requestId, delivered: result.delivered, targetSessionId: target, messageId: result.id, ...(result.deliveryId ? { deliveryId: result.deliveryId } : {}), ...(result.code ? { code: result.code } : {}), ...(!result.delivered && result.reason ? { error: result.reason } : {}), }); } catch (error) { if (!relayStillLive()) return; emitControlDelivery({ requestId: parsed.requestId, delivered: false, error: getErrorMessage(error), }); } })(); } function emitResultDelivery(requestId: string | undefined, delivered: boolean, error?: unknown): void { if (!requestId) return; pi.events.emit(SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT, { requestId, delivered, ...(error ? { error: getErrorMessage(error) } : {}), }); } function relaySubagentIntercomPayload(payload: unknown, options: { sender: "subagent-control" | "subagent-result" | "fleet-lifecycle"; status: string; errorEntryType: string; acknowledge?: boolean; }): void { const parsed = parseSubagentIntercomPayload(payload); if (!parsed) return; const relayGeneration = runtimeGeneration; void (async () => { const relayStillLive = () => !runtimeStarted || Boolean(getLiveContext(runtimeContext, relayGeneration)); if (!relayStillLive()) { return; } if (!bossTeamScope.present && currentSessionTargetMatches(parsed.to)) { deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message); if (options.acknowledge) emitResultDelivery(parsed.requestId, true); return; } let activeClient: IntercomClient; let target: string; try { activeClient = await ensureConnected("background"); target = await resolveAuthorizedTarget(activeClient, parsed.to); } catch (error) { if (!relayStillLive()) return; recordSubagentDeliveryError(options.errorEntryType, parsed.to, parsed.message, error); if (options.acknowledge) emitResultDelivery(parsed.requestId, false, error); return; } if (!relayStillLive()) { return; } if (currentSessionTargetMatches(parsed.to, target, activeClient)) { if (bossTeamScope.present) { const error = new Error("Boss team scope denies local pseudo-sender self delivery"); recordSubagentDeliveryError(options.errorEntryType, parsed.to, parsed.message, error); if (options.acknowledge) emitResultDelivery(parsed.requestId, false, error); return; } deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message); if (options.acknowledge) emitResultDelivery(parsed.requestId, true); return; } try { const result = await activeClient.send(target, { text: parsed.message }); if (!relayStillLive()) return; if (!result.delivered) { const error = new Error(result.reason ?? "Session may not exist or has disconnected."); recordSubagentDeliveryError(options.errorEntryType, parsed.to, parsed.message, error); if (options.acknowledge) emitResultDelivery(parsed.requestId, false, error); return; } if (options.acknowledge) emitResultDelivery(parsed.requestId, true); } catch (error) { if (!relayStillLive()) return; recordSubagentDeliveryError(options.errorEntryType, parsed.to, parsed.message, error); if (options.acknowledge) emitResultDelivery(parsed.requestId, false, error); } })(); } const unsubscribeControlRegistration = pi.events.on(INTERCOM_CONTROL_REGISTER_EVENT, (payload) => { const registration = parseIntercomControlRegistration(payload); if (!registration) return; registeredControlTypes.add(intercomControlKey(registration)); if (inboundInbox?.size) scheduleInboundFlush(0); }); const unsubscribeControlSend = pi.events.on(INTERCOM_CONTROL_SEND_EVENT, (payload) => { relayControlSendRequest(payload); }); const unsubscribeSubagentControlIntercom = pi.events.on(SUBAGENT_CONTROL_INTERCOM_EVENT, (payload) => { relaySubagentIntercomPayload(payload, { sender: "subagent-control", status: "needs_attention", errorEntryType: "intercom_control_error", }); }); const unsubscribeSubagentResultIntercom = pi.events.on(SUBAGENT_RESULT_INTERCOM_EVENT, (payload) => { relaySubagentIntercomPayload(payload, { sender: "subagent-result", status: "result", errorEntryType: "intercom_result_error", acknowledge: true, }); }); const unsubscribeFleetLifecycleIntercom = pi.events.on(INTERCOM_LIFECYCLE_SEND_EVENT, (payload) => { relaySubagentIntercomPayload(payload, { sender: "fleet-lifecycle", status: "needs_attention", errorEntryType: "intercom_lifecycle_error", }); }); pi.on("session_start", (_event, ctx) => { if (!config.enabled || isEmptyRpcBootstrapSession(ctx)) { return; } startSessionRuntime(ctx); }); pi.on("before_agent_start", (_event, ctx) => { if (!config.enabled || getLiveContext(ctx)) { return; } startSessionRuntime(ctx); }); pi.on("session_shutdown", async () => { unsubscribeControlRegistration(); unsubscribeControlSend(); unsubscribeSubagentControlIntercom(); unsubscribeSubagentResultIntercom(); unsubscribeFleetLifecycleIntercom(); shuttingDown = true; disposed = true; runtimeGeneration += 1; clearStartupConnectTimer(); clearReconnectTimer(); clearNamePollTimer(); restoreIntercomSessionId(); rejectAllReplyWaiters(new Error("Session shutting down")); replyTracker.reset(); clearInboundFlushTimer(); inboundInbox = null; inboundFirstQueuedAt = null; inboundLastQueuedAt = null; controlRegistrationGraceUntil = 0; agentRunning = false; activeTools.clear(); if (client) { await client.disconnect(true); client = null; } runtimeContext = null; currentSessionId = null; sessionStartedAt = null; runtimeInstanceId = null; }); pi.on("turn_end", () => { if (!getLiveContext()) { return; } scheduleInboundFlush(0); }); pi.on("agent_start", () => { if (!getLiveContext()) { return; } agentRunning = true; activeTools.clear(); syncPresenceStatus(); }); pi.on("tool_execution_start", (event) => { if (!getLiveContext()) { return; } activeTools.set(event.toolCallId, event.toolName); syncPresenceStatus(); }); pi.on("tool_execution_end", (event) => { if (!getLiveContext()) { return; } activeTools.delete(event.toolCallId); syncPresenceStatus(); }); pi.on("agent_end", () => { if (!getLiveContext()) { return; } replyTracker.endTurn(); agentRunning = false; activeTools.clear(); syncPresenceStatus(); scheduleInboundFlush(0); }); pi.on("turn_start", (_event, ctx) => { const rawSessionId = ctx.sessionManager.getSessionId(); const sessionId = configuredSessionId || rawSessionId; if (!currentSessionId || sessionId !== currentSessionId) { if (!config.enabled) { return; } startSessionRuntime(ctx); replyTracker.beginTurn(); return; } if (!getLiveContext(ctx)) { return; } syncPresenceIdentity(sessionId); replyTracker.beginTurn(); }); pi.on("model_select", (event, ctx) => { if (!getLiveContext(ctx)) { return; } currentModel = event.model.id; if (client) { client.updatePresence({ ...buildPresenceIdentity(pi, currentSessionId ?? (configuredSessionId || ctx.sessionManager.getSessionId())), model: event.model.id, status: currentStatus(), }); } }); pi.registerMessageRenderer("intercom_message", (message, options, theme) => { const details = message.details as InboundMessageBatchDetails | InboundMessageEntry | undefined; if (!details) return undefined; const entries = "entries" in details ? details.entries : [details]; if (entries.length !== 1) { const batchText = entries.map((entry, index) => { const timing = formatMessageTiming({ sentAt: entry.message.timestamp, receivedAt: entry.receivedAt, readAt: entry.readAt }); return `${formatIncomingEntry(entry, { index: index + 1, total: entries.length })}${timing ? `\n\n${theme.fg("dim", timing)}` : ""}`; }).join("\n\n---\n\n"); return new Text(batchText, 0, 0); } const entry = entries[0]!; return new InlineMessageComponent(entry.from, entry.message, theme, entry.replyCommand, entry.bodyText, !options.expanded, entry.receivedAt, entry.readAt); }); const intercomResultToolNames = new Set([ "intercom", "intercom_send", "intercom_ask", "intercom_reply", "intercom_list", "intercom_pending", "intercom_status", "intercom_team", "contact_supervisor", ]); pi.on("tool_result", (event) => { if (!intercomResultToolNames.has(event.toolName)) { return; } if (!event.details || typeof event.details !== "object") { return; } const details = event.details as { error?: unknown; delivered?: unknown }; if (details.error === true || details.delivered === false) { return { isError: true }; } }); const childOrchestratorMetadata = readChildOrchestratorMetadata(); if (childOrchestratorMetadata) { pi.registerTool({ name: "contact_supervisor", label: "Contact Supervisor", description: "Subagent-only tool for contacting the supervisor agent that delegated this task. Use need_decision when blocked, uncertain, needing approval, or facing a product/API/scope decision before continuing; this waits up to 30 seconds, then continues asynchronously if unanswered. Use interview_request when multiple structured questions need supervisor answers; it has the same soft wait. Use progress_update only for meaningful progress or unexpected discoveries that change the plan; this does not wait for a reply. Do not use for routine completion handoffs.", promptSnippet: "Subagent-only: contact the supervisor for decisions, structured interviews, or meaningful plan-changing updates. Do not use for routine completion handoffs.", promptGuidelines: [ "Use contact_supervisor with reason='need_decision' when a subagent is blocked, uncertain, needs approval, or faces a product/API/scope decision before continuing; after the 30-second soft wait, continue only with work that does not depend on the answer.", "Use contact_supervisor with reason='interview_request' when the child needs multiple structured answers from the supervisor; after the 30-second soft wait, the reply may arrive asynchronously.", "Use contact_supervisor with reason='progress_update' only for meaningful progress or unexpected discoveries that change the plan.", "Do not use contact_supervisor for routine completion handoffs; return the final subagent result normally.", ], parameters: Type.Object({ reason: StringEnum(["need_decision", "progress_update", "interview_request"] as const, { description: "Contact reason: 'need_decision' and 'interview_request' wait up to 30 seconds before continuing asynchronously; 'progress_update' sends a non-blocking update", }), message: Type.Optional(Type.String({ description: "Decision request, optional interview note, or meaningful progress update for the supervisor", })), interview: Type.Optional(Type.Object({ title: Type.Optional(Type.String()), description: Type.Optional(Type.String()), questions: Type.Array(Type.Object({ id: Type.String(), type: StringEnum(["single", "multi", "text", "image", "info"] as const, { description: "Question type: single, multi, text, image, or info", }), question: Type.String(), options: Type.Optional(Type.Array(Type.Any())), context: Type.Optional(Type.String()), })), }, { description: "Structured interview request for reason='interview_request'" })), }), async execute(_toolCallId, params, signal, _onUpdate, ctx) { const reason = params.reason as ContactSupervisorReason; if (reason !== "need_decision" && reason !== "progress_update" && reason !== "interview_request") { return { content: [{ type: "text", text: "Invalid reason. Use 'need_decision', 'interview_request', or 'progress_update'." }], details: { error: true }, }; } if ((reason === "need_decision" || reason === "progress_update") && typeof params.message !== "string") { return { content: [{ type: "text", text: `Missing 'message' parameter for reason '${reason}'.` }], details: { error: true }, }; } const interviewValidation = reason === "interview_request" ? validateSupervisorInterviewRequest(params.interview) : undefined; if (interviewValidation?.ok === false) { return { content: [{ type: "text", text: `Invalid interview request: ${interviewValidation.error}` }], details: { error: true }, }; } const supervisorInterview = interviewValidation?.ok === true ? interviewValidation.interview : undefined; let connectedClient: IntercomClient; try { connectedClient = await ensureConnected("tool"); } catch (error) { return { content: [{ type: "text", text: `Intercom not connected: ${getErrorMessage(error)}` }], details: toolErrorDetails(error), }; } syncPresenceIdentity(ctx.sessionManager.getSessionId()); if (signal?.aborted) { return { content: [{ type: "text", text: "Cancelled" }], details: { error: true }, }; } const metadata = childOrchestratorMetadata; let sendTo: string; try { if (bossTeamScope.present) { const selfError = bossSelfSessionError(bossTeamScope, connectedClient.sessionId); if (selfError) throw new BossTeamScopeError("BOSS_TEAM_METADATA_INVALID", selfError); } if (bossTeamScope.present) { const exactTarget = metadata.orchestratorSessionId ?? metadata.orchestratorTarget; const resolution = resolveBossLiveTarget(bossTeamScope, exactTarget, await connectedClient.listSessions(), connectedClient.sessionId); if ("code" in resolution) throw new BossTeamScopeError(resolution.code, resolution.error); sendTo = resolution.targetId; } else { sendTo = await resolveSupervisorTarget(connectedClient, metadata); } } catch (error) { return { content: [{ type: "text", text: `Failed to resolve supervisor target: ${getErrorMessage(error)}` }], details: toolErrorDetails(error), }; } if (signal?.aborted) { return { content: [{ type: "text", text: "Cancelled" }], details: { error: true }, }; } if (sendTo === connectedClient.sessionId) { return { content: [{ type: "text", text: "Cannot message the current session" }], details: { error: true }, }; } if (reason === "progress_update") { const message = params.message as string; try { const result = await connectedClient.send(sendTo, { text: formatChildOrchestratorMessage("update", metadata, message), }); if (!result.delivered) { const errorText = result.reason ?? "Session may not exist or has disconnected."; return { content: [{ type: "text", text: `Message to "${metadata.orchestratorTarget}" was not delivered: ${errorText}` }], details: deliveryResultDetails(result), }; } pi.appendEntry("intercom_sent", { to: metadata.orchestratorTarget, message: { text: message, reason }, messageId: result.id, timestamp: Date.now(), subagent: { runId: metadata.runId, agent: metadata.agent, index: metadata.index }, }); return { content: [{ type: "text", text: `Progress update sent to supervisor ${metadata.orchestratorTarget}` }], details: deliveryResultDetails(result), }; } catch (error) { return { content: [{ type: "text", text: `Failed to send progress update: ${getErrorMessage(error)}` }], details: { error: true }, }; } } if (hasReplyWaiterForTarget(sendTo)) { return { content: [{ type: "text", text: `Already waiting for a reply from "${metadata.orchestratorTarget}"` }], details: { error: true }, }; } let replyPromise: Promise | null = null; let questionId: string | null = null; try { questionId = randomUUID(); replyPromise = waitForReply(sendTo, questionId, signal, () => { void connectedClient.cancelAsk(questionId!); }); replyPromise.catch(() => undefined); if (signal?.aborted) { rejectReplyWaiter(questionId, new Error("Cancelled")); try { await replyPromise; } catch { // The waiter was intentionally rejected above; the tool result reports cancellation. } return { content: [{ type: "text", text: "Cancelled" }], details: { error: true }, }; } const requestText = reason === "interview_request" ? formatChildOrchestratorMessage("interview", metadata, formatSupervisorInterviewRequest(supervisorInterview!, typeof params.message === "string" ? params.message : undefined)) : formatChildOrchestratorMessage("ask", metadata, params.message as string); const sendResult = await connectedClient.send(sendTo, { messageId: questionId, text: requestText, expectsReply: true, }); if (!sendResult.delivered) { const errorText = sendResult.reason ?? "Session may not exist or has disconnected."; rejectReplyWaiter(questionId, new Error(`Message to "${metadata.orchestratorTarget}" was not delivered: ${errorText}`)); if (replyPromise) { try { await replyPromise; } catch { // The waiter was already rejected above. Keep the delivery failure as the only error here. } } return { content: [{ type: "text", text: `Message to "${metadata.orchestratorTarget}" was not delivered: ${errorText}` }], details: { ...deliveryResultDetails(sendResult), error: true }, }; } pi.appendEntry("intercom_sent", { to: metadata.orchestratorTarget, message: { text: reason === "interview_request" ? requestText : params.message, reason, ...(reason === "interview_request" ? { interview: supervisorInterview } : {}), }, messageId: sendResult.id, timestamp: Date.now(), subagent: { runId: metadata.runId, agent: metadata.agent, index: metadata.index }, }); const replyMessage = await replyPromise; const replyText = replyMessage.content.text; const replyAttachments = replyMessage.content.attachments?.length ? formatAttachments(replyMessage.content.attachments) : ""; const structuredReply = reason === "interview_request" ? parseStructuredSupervisorReply(replyText, supervisorInterview!) : undefined; pi.appendEntry("intercom_received", { from: metadata.orchestratorTarget, message: { text: replyText, attachments: replyMessage.content.attachments }, messageId: replyMessage.id, timestamp: replyMessage.timestamp, subagent: { runId: metadata.runId, agent: metadata.agent, index: metadata.index }, }); return { content: [{ type: "text", text: `**Reply from supervisor:**\n${replyText}${replyAttachments}` }], details: structuredReply ? structuredReply.value !== undefined ? { structuredReply: structuredReply.value } : { structuredReplyParseError: structuredReply.error } : {}, }; } catch (error) { if (error instanceof AskWaitElapsedError) { return deferredAskResult(metadata.orchestratorTarget, error, await connectedClient.deferAsk(error.replyTo)); } if (questionId) rejectReplyWaiter(questionId, toError(error)); if (replyPromise) { try { await replyPromise; } catch { // The waiter is cleanup-only on this path. The real failure is the one from the outer catch. } } return { content: [{ type: "text", text: `Failed: ${getErrorMessage(error)}` }], details: { error: true }, }; } }, renderCall(args, theme) { const reason = typeof args.reason === "string" ? args.reason : "contact"; const messagePreview = previewText(args.message, 96); const interview = args.interview && typeof args.interview === "object" ? args.interview as { title?: unknown } : undefined; let text = theme.fg("toolTitle", theme.bold("contact_supervisor ")); text += theme.fg(reason === "need_decision" ? "warning" : reason === "progress_update" ? "muted" : "accent", reason); if (typeof interview?.title === "string" && interview.title.trim()) { text += " " + theme.fg("accent", interview.title.trim()); } if (messagePreview) { text += "\n " + theme.fg("dim", messagePreview); } return new Text(text, 0, 0); }, renderResult(result, { isPartial }, theme, context) { if (isPartial) { return new Text(theme.fg("warning", "Waiting for supervisor..."), 0, 0); } const details = result.details as { delivered?: boolean; error?: boolean; messageId?: string; reason?: string; structuredReplyParseError?: string } | undefined; const textContent = firstTextContent(result); const failed = Boolean(context.isError || details?.error === true || details?.delivered === false); const parseWarning = typeof details?.structuredReplyParseError === "string"; let text = failed ? theme.fg("error", "βœ— ") : parseWarning ? theme.fg("warning", "⚠ ") : theme.fg("success", "βœ“ "); text += theme.fg(failed ? "error" : "text", textContent); if (parseWarning) { text += "\n" + theme.fg("warning", `Structured reply parse issue: ${details.structuredReplyParseError}`); } return new Text(text, 0, 0); }, } as any); } const legacyIntercomTool = { name: "intercom", label: "Intercom (Legacy)", description: `Send a message to another pi session running on this machine. Use this to communicate findings, request help, or coordinate work with other sessions. Usage: intercom({ action: "list" }) β†’ List active sessions intercom({ action: "send", to: "session-name", message: "..." }) β†’ Send message intercom({ action: "ask", to: "session-name", message: "..." }) β†’ Ask, waiting up to 30 seconds before continuing asynchronously intercom({ action: "reply", message: "..." }) β†’ Reply to the active/single pending ask intercom({ action: "pending" }) β†’ List unresolved inbound asks with stable IDs intercom({ action: "pending", askId: "ask-..." }) β†’ Retrieve one untruncated ask body intercom({ action: "status" }) β†’ Show connection status`, promptSnippet: "Use to coordinate with other local pi sessions: list peers, send updates, ask for help, or check intercom connectivity.", parameters: Type.Object({ action: StringEnum(["list", "send", "ask", "reply", "pending", "status"] as const, { description: "Action: 'list', 'send', 'ask', 'reply', 'pending', or 'status'", }), to: Type.Optional(Type.String({ description: "Target session name or ID (for 'send', 'ask', or disambiguating 'reply')", })), message: Type.Optional(Type.String({ description: "Message to send (for 'send', 'ask', or 'reply' action)", })), attachments: Type.Optional(Type.Array(Type.Object({ type: StringEnum(["file", "snippet", "context"] as const), name: Type.String(), content: Type.String(), language: Type.Optional(Type.String()), }))), replyTo: Type.Optional(Type.String({ description: "Message ID to reply to (legacy threading only)", })), askId: Type.Optional(Type.String({ description: "Stable ask selector returned by the pending action (for 'reply' or full-body pending retrieval)", })), session: Type.Optional(Type.String({ description: "Owned coworker session whose pending inbox a manager wants to inspect (for 'pending' only)", })), which: Type.Optional(StringEnum(["oldest", "latest"] as const, { description: "Select the oldest or latest unresolved ask when one sender has multiple pending asks", })), }), async execute(_toolCallId, params, _signal, _onUpdate, ctx) { let connectedClient: IntercomClient; try { connectedClient = await ensureConnected("tool"); } catch (error) { return { content: [{ type: "text", text: `Intercom not connected: ${getErrorMessage(error)}` }], details: toolErrorDetails(error), }; } syncPresenceIdentity(ctx.sessionManager.getSessionId()); if (bossTeamScope.present) { const selfError = bossSelfSessionError(bossTeamScope, connectedClient.sessionId); if (selfError) { return { content: [{ type: "text", text: `Boss team scope is unavailable: ${selfError}` }], details: { error: true, code: "BOSS_TEAM_METADATA_INVALID" }, }; } } const { action, to, message, attachments, replyTo, askId, session, which } = params; switch (action) { case "list": { if (bossTeamScope.restricted) { const reason = bossTeamScope.valid ? "Global intercom discovery is unavailable in Boss team-only mode; use intercom_team" : `Global intercom discovery is unavailable: ${"error" in bossTeamScope ? bossTeamScope.error : "invalid Boss metadata"}`; return { content: [{ type: "text", text: reason }], details: { error: true } }; } try { const mySessionId = connectedClient.sessionId; const sessions = await connectedClient.listSessions(); const currentSession = sessions.find(s => s.id === mySessionId); const otherSessions = sessions.filter(s => s.id !== mySessionId); const idPrefixes = shortestUniqueIdPrefixes(sessions.map((session) => session.id), 8); if (!currentSession) { return { content: [{ type: "text", text: "Current session is missing from intercom session list." }], details: { error: true }, }; } const currentSection = `**Current session:**\n${formatSessionListRow(currentSession, currentSession.cwd, true, idPrefixes.get(currentSession.id) ?? currentSession.id)}`; const otherSection = otherSessions.length === 0 ? "**Other sessions:**\nNo other sessions connected." : `**Other sessions:**\n${otherSessions.map(s => formatSessionListRow(s, currentSession.cwd, false, idPrefixes.get(s.id) ?? s.id)).join("\n")}`; return { content: [{ type: "text", text: `${currentSection}\n\n${otherSection}` }], details: {}, }; } catch (error) { return { content: [{ type: "text", text: `Failed to list sessions: ${getErrorMessage(error)}` }], details: { error: true }, }; } } case "send": { if (!to && replyTo) { return { content: [{ type: "text", text: `Missing required 'to' for action 'send'. 'replyTo' is a message ID, not a recipient. Retry with { action: "send", to: "${replyTo}", message: "..." } if that value is the intended session target.` }], details: { error: true, missing: ["to"], invalidRecipientField: "replyTo" }, }; } if (!to || !message) { return { content: [{ type: "text", text: "Missing 'to' or 'message' parameter" }], details: { error: true }, }; } try { const sendTo = await resolveAuthorizedTarget(connectedClient, to); if (sendTo === connectedClient.sessionId) { return { content: [{ type: "text", text: "Cannot message the current session" }], details: { error: true }, }; } if (!replyTo && config.confirmSend && ctx.hasUI) { const attachmentText = attachments?.length ? formatAttachments(attachments) : ""; const confirmed = await ctx.ui.confirm( "Send Message", `Send to "${to}":\n\n${message}${attachmentText}`, ); if (!confirmed) { return { content: [{ type: "text", text: "Message cancelled by user" }], details: {}, }; } } const result = await connectedClient.send(sendTo, { text: message, attachments, replyTo, }); if (!result.delivered) { const errorText = result.reason ?? "Session may not exist or has disconnected."; return { content: [{ type: "text", text: `Message to "${to}" was not delivered: ${errorText}` }], details: deliveryResultDetails(result), }; } pi.appendEntry("intercom_sent", { to, message: { text: message, attachments, replyTo }, messageId: result.id, timestamp: Date.now(), }); if (replyTo) { replyTracker.markReplied(replyTo, sendTo); inboundInbox?.dismissPendingAsk(replyTo, sendTo); } return { content: [{ type: "text", text: `Message sent to ${to}` }], details: deliveryResultDetails(result), }; } catch (error) { return { content: [{ type: "text", text: `Failed to send: ${getErrorMessage(error)}` }], details: toolErrorDetails(error), }; } } case "ask": { if (!to && replyTo) { return { content: [{ type: "text", text: `Missing required 'to' for action 'ask'. 'replyTo' is a message ID, not a recipient. Retry with { action: "ask", to: "${replyTo}", message: "..." } if that value is the intended session target.` }], details: { error: true, missing: ["to"], invalidRecipientField: "replyTo" }, }; } if (!to || !message) { return { content: [{ type: "text", text: "Missing 'to' or 'message' parameter" }], details: { error: true }, }; } if (_signal?.aborted) { return { content: [{ type: "text", text: "Cancelled" }], details: { error: true }, }; } let replyPromise: Promise | null = null; let questionId: string | null = null; try { const sendTo = await resolveAuthorizedTarget(connectedClient, to); if (_signal?.aborted) { return { content: [{ type: "text", text: "Cancelled" }], details: { error: true }, }; } if (sendTo === connectedClient.sessionId) { return { content: [{ type: "text", text: "Cannot message the current session" }], details: { error: true }, }; } if (hasReplyWaiterForTarget(sendTo)) { return { content: [{ type: "text", text: `Already waiting for a reply from "${to}"` }], details: { error: true }, }; } questionId = randomUUID(); replyPromise = waitForReply(sendTo, questionId, _signal, () => { void connectedClient.cancelAsk(questionId!); }); replyPromise.catch(() => undefined); const sendResult = await connectedClient.send(sendTo, { messageId: questionId, text: message, attachments, replyTo, expectsReply: true, }); if (!sendResult.delivered) { const errorText = sendResult.reason ?? "Session may not exist or has disconnected."; rejectReplyWaiter(questionId, new Error(`Message to "${to}" was not delivered: ${errorText}`)); if (replyPromise) { try { await replyPromise; } catch { // The waiter was already rejected above. Keep the delivery failure as the only error here. } } return { content: [{ type: "text", text: `Message to "${to}" was not delivered: ${errorText}` }], details: { ...deliveryResultDetails(sendResult), error: true }, }; } pi.appendEntry("intercom_sent", { to, message: { text: message, attachments, replyTo }, messageId: sendResult.id, timestamp: Date.now(), }); const replyMessage = await replyPromise; const replyText = replyMessage.content.text; const replyAttachments = replyMessage.content.attachments?.length ? formatAttachments(replyMessage.content.attachments) : ""; pi.appendEntry("intercom_received", { from: to, message: { text: replyText, attachments: replyMessage.content.attachments }, messageId: replyMessage.id, timestamp: replyMessage.timestamp, }); return { content: [{ type: "text", text: `**Reply from ${to}:**\n${replyText}${replyAttachments}` }], details: {}, }; } catch (error) { if (error instanceof AskWaitElapsedError) { return deferredAskResult(to, error, await connectedClient.deferAsk(error.replyTo)); } if (questionId) rejectReplyWaiter(questionId, toError(error)); if (replyPromise) { try { await replyPromise; } catch { // The waiter is cleanup-only on this path. The real failure is the one from the outer catch. } } return { content: [{ type: "text", text: `Failed: ${getErrorMessage(error)}` }], details: toolErrorDetails(error), }; } } case "reply": { if (!message) { return { content: [{ type: "text", text: "Missing 'message' parameter" }], details: { error: true }, }; } try { let exactReplyTarget = to; if (bossTeamScope.present && to) { const requested = resolveBossLiveTarget(bossTeamScope, to, await connectedClient.listSessions(), connectedClient.sessionId); if ("code" in requested) throw new BossTeamScopeError(requested.code, requested.error); exactReplyTarget = requested.targetId; } const target = replyTracker.resolveReplyTarget({ to: exactReplyTarget, replyTo, askId, which }); if (bossTeamScope.present) { const resolution = resolveBossLiveTarget(bossTeamScope, target.from.id, await connectedClient.listSessions(), connectedClient.sessionId); if ("code" in resolution) throw new BossTeamScopeError(resolution.code, resolution.error); } if (target.from.id === connectedClient.sessionId) { return { content: [{ type: "text", text: "Cannot message the current session" }], details: { error: true }, }; } const threadedReplyTo = target.message.expectsReply ? target.message.id : undefined; const result = await connectedClient.send(target.from.id, { text: message, ...(threadedReplyTo ? { replyTo: threadedReplyTo } : {}), }); if (!result.delivered) { const errorText = result.reason ?? "Session may not exist or has disconnected."; if (threadedReplyTo && result.code === "INVALID_REPLY_TARGET") { replyTracker.dismissPendingAsk(target.message.id, target.from.id); inboundInbox?.dismissPendingAsk(target.message.id, target.from.id); } return { content: [{ type: "text", text: `Reply to "${bossTeamScope.present ? target.from.id : target.from.name || target.from.id}" was not delivered: ${errorText}` }], details: deliveryResultDetails(result, threadedReplyTo ? { replyTo: threadedReplyTo } : {}), }; } if (threadedReplyTo) { replyTracker.markReplied(threadedReplyTo, target.from.id); inboundInbox?.dismissPendingAsk(threadedReplyTo, target.from.id); } else { replyTracker.dismissOrdinarySender(target.from.id); } pi.appendEntry("intercom_sent", { to: bossTeamScope.present ? target.from.id : target.from.name || target.from.id, message: { text: message, ...(threadedReplyTo ? { replyTo: threadedReplyTo } : {}) }, messageId: result.id, timestamp: Date.now(), }); return { content: [{ type: "text", text: `Reply sent to ${bossTeamScope.present ? target.from.id : target.from.name || target.from.id}` }], details: deliveryResultDetails(result, threadedReplyTo ? { replyTo: threadedReplyTo } : {}), }; } catch (error) { return { content: [{ type: "text", text: `Failed to reply: ${getErrorMessage(error)}` }], details: toolErrorDetails(error), }; } } case "pending": { let inboxSessionId: string; let pendingAsks: IntercomContext[]; try { ({ inboxSessionId, contexts: pendingAsks } = await loadPendingAskContexts(connectedClient, session)); } catch (error) { return { content: [{ type: "text", text: `Failed to inspect pending asks: ${getErrorMessage(error)}` }], details: toolErrorDetails(error), }; } if (pendingAsks.length === 0) { if (askId) { return { content: [{ type: "text", text: `No unresolved pending ask with ask ID "${askId}" in inbox "${inboxSessionId}".` }], details: { error: true, inboxSessionId }, }; } return { content: [{ type: "text", text: "No unresolved inbound asks. Outbound asks are not listed here." }], details: { inboxSessionId, asks: [] }, }; } const now = Date.now(); const asks = pendingAsks.map(({ from, message, receivedAt, deferredAt }) => { const sameSender = pendingAsks.filter((entry) => entry.from.id === from.id); const senderIndex = sameSender.findIndex((entry) => entry.message.id === message.id); const selector = sameSender.length <= 1 ? "" : senderIndex === 0 ? " Β· oldest" : senderIndex === sameSender.length - 1 ? " Β· latest" : " Β· queued"; const preview = message.content.text.replace(/\s+/g, " ").slice(0, 80); const elapsedSeconds = Math.max(0, Math.floor((now - receivedAt) / 1000)); return { id: pendingAskId(from.id, message.id), from, receivedAt, ...(deferredAt ? { deferredAt } : {}), preview, selector, elapsedSeconds, message, }; }); if (askId) { const ask = asks.find((entry) => entry.id === askId); if (!ask) { return { content: [{ type: "text", text: `No unresolved pending ask with ask ID "${askId}" in inbox "${inboxSessionId}".` }], details: { error: true, inboxSessionId }, }; } const attachmentText = ask.message.content.attachments?.length ? formatAttachments(ask.message.content.attachments) : ""; const sender = bossTeamScope.present ? ask.from.id : ask.from.name || ask.from.id; return { content: [{ type: "text", text: `**Pending ask ${ask.id}**\nInbox: ${inboxSessionId}\nFrom: ${sender}\n\n${ask.message.content.text}${attachmentText}` }], details: { inboxSessionId, ask: { id: ask.id, from: ask.from, receivedAt: ask.receivedAt, ...(ask.deferredAt ? { deferredAt: ask.deferredAt } : {}), preview: ask.preview, body: ask.message.content.text, attachments: ask.message.content.attachments ?? [], }, }, }; } const lines = asks.map((ask) => { const state = ask.deferredAt ? " Β· async" : ""; const sender = bossTeamScope.present ? ask.from.id : ask.from.name || ask.from.id; return `- ${ask.id} Β· ${sender}${ask.selector} Β· ${ask.elapsedSeconds}s ago${state} Β· ${ask.preview}`; }); return { content: [{ type: "text", text: `**Pending asks:**\n${lines.join("\n")}` }], details: { inboxSessionId, asks: asks.map((ask) => ({ id: ask.id, from: ask.from, receivedAt: ask.receivedAt, ...(ask.deferredAt ? { deferredAt: ask.deferredAt } : {}), preview: ask.preview, })), }, }; } case "status": { try { const mySessionId = connectedClient.sessionId; const sessions = filterBossSessions(bossTeamScope, await connectedClient.listSessions(), mySessionId); return { content: [{ type: "text", text: `**Intercom Status:**\nConnected: Yes\nSession ID: ${mySessionId}\nActive sessions: ${sessions.length}\nQueued inbound messages: ${inboundInbox?.size ?? 0}\nQueued outbound messages: ${connectedClient.outboxSize}\nPending inbound asks: ${replyTracker.listPending().length}`, }], details: {}, }; } catch (error) { return { content: [{ type: "text", text: `Failed to get status: ${getErrorMessage(error)}` }], details: { error: true }, }; } } default: return { content: [{ type: "text", text: `Unknown action: ${action}` }], details: { error: true }, }; } }, renderCall(args, theme) { const action = typeof args.action === "string" ? args.action : "intercom"; const target = typeof args.to === "string" && args.to.trim() ? args.to.trim() : undefined; const messagePreview = previewText(args.message, 96); const attachmentCount = Array.isArray(args.attachments) ? args.attachments.length : 0; let text = theme.fg("toolTitle", theme.bold("intercom ")); text += theme.fg(action === "ask" ? "warning" : action === "reply" ? "success" : "accent", action); if (target) { text += " " + theme.fg("muted", "β†’") + " " + theme.fg("accent", target); } if (attachmentCount > 0) { text += " " + theme.fg("dim", `(${attachmentCount} attachment${attachmentCount === 1 ? "" : "s"})`); } if (messagePreview) { text += "\n " + theme.fg("dim", messagePreview); } return new Text(text, 0, 0); }, renderResult(result, { isPartial }, theme, context) { if (isPartial) { return new Text(theme.fg("warning", "Intercom working..."), 0, 0); } const details = result.details as { delivered?: boolean; error?: boolean; messageId?: string; reason?: string; sentAt?: number; deliveredAt?: number } | undefined; const failed = Boolean(context.isError || details?.error === true || details?.delivered === false); let text = failed ? theme.fg("error", "βœ— ") : theme.fg("success", "βœ“ "); text += theme.fg(failed ? "error" : "text", firstTextContent(result)); if (details?.messageId && !context.expanded) { text += theme.fg("dim", ` (${details.messageId.slice(0, 8)})`); } const timing = formatMessageTiming({ sentAt: details?.sentAt, deliveredAt: details?.deliveredAt }); if (timing) { text += "\n" + theme.fg("dim", ` ${timing}`); } if (details?.reason && context.expanded) { text += "\n" + theme.fg("dim", `Reason: ${details.reason}`); } return new Text(text, 0, 0); }, } as any; const attachmentParameters = Type.Optional(Type.Array(Type.Object({ type: StringEnum(["file", "snippet", "context"] as const), name: Type.String(), content: Type.String(), language: Type.Optional(Type.String()), }))); type IntercomAction = "list" | "send" | "ask" | "reply" | "pending" | "status"; const executeSplitAction = (action: IntercomAction) => async (toolCallId: string, params: Record, signal: AbortSignal | undefined, onUpdate: unknown, ctx: ExtensionContext) => legacyIntercomTool.execute(toolCallId, { ...params, action }, signal, onUpdate, ctx); const renderSplitCall = (action: IntercomAction) => (args: Record, theme: unknown, context: unknown) => legacyIntercomTool.renderCall({ ...args, action }, theme, context); const renderSplitResult = legacyIntercomTool.renderResult; pi.registerTool({ name: "intercom_send", label: "Intercom Send", description: "Send a message to another local Pi session. Both the recipient and message are required.", promptSnippet: "Send a fire-and-forget message to another local Pi session.", promptGuidelines: ["Use intercom_send for progress updates, notifications, and other messages that do not require a conversational reply."], parameters: Type.Object({ to: Type.String({ description: "Required recipient session name or stable session ID" }), message: Type.String({ description: "Required message to send" }), attachments: attachmentParameters, }), execute: executeSplitAction("send"), renderCall: renderSplitCall("send"), renderResult: renderSplitResult, } as any); pi.registerTool({ name: "intercom_ask", label: "Intercom Ask", description: "Ask another local Pi session a blocking question, waiting briefly before continuing asynchronously. Do not use this for progress or status checkpoints.", promptSnippet: "Ask another local Pi session a question only when the next step depends on its answer.", promptGuidelines: ["Use intercom_ask only when work genuinely depends on another session's answer. Use intercom_send for progress checks, status requests, assignments, notifications, and follow-ups. Different recipients may be asked concurrently, but keep only one unresolved ask per recipient."], parameters: Type.Object({ to: Type.String({ description: "Required recipient session name or stable session ID" }), message: Type.String({ description: "Required question to ask" }), attachments: attachmentParameters, }), execute: executeSplitAction("ask"), renderCall: renderSplitCall("ask"), renderResult: renderSplitResult, } as any); pi.registerTool({ name: "intercom_reply", label: "Intercom Reply", description: "Reply to an inbound intercom message or ask. Exact protocol threading is resolved internally.", promptSnippet: "Reply to the active or pending inbound intercom message.", promptGuidelines: ["Use intercom_reply to answer an inbound intercom message. Prefer the stable `askId` returned by intercom_pending when selecting an exact ask; `to` plus `which` remains available for compatibility."], parameters: Type.Object({ message: Type.String({ description: "Required reply text" }), to: Type.Optional(Type.String({ description: "Optional sender/session selector when multiple senders or asks are pending; never a message or thread ID" })), which: Type.Optional(StringEnum(["oldest", "latest"] as const, { description: "Select the oldest or latest ask when the chosen sender has multiple unresolved asks" })), askId: Type.Optional(Type.String({ description: "Stable ask selector returned by intercom_pending" })), }), execute: executeSplitAction("reply"), renderCall: renderSplitCall("reply"), renderResult: renderSplitResult, } as any); pi.registerTool({ name: "intercom_team", label: "Intercom Team", description: "Show your current manager and the live coworkers owned by that manager. No arguments are required.", promptSnippet: "Find your manager and managed coworkers without searching the global peer list.", promptGuidelines: ["Use intercom_team whenever you need your manager's target or the other coworkers in your managed group."], parameters: Type.Object({}), async execute(_toolCallId, _params, _signal, _onUpdate, ctx) { try { const connectedClient = await ensureConnected("tool"); syncPresenceIdentity(ctx.sessionManager.getSessionId()); if (bossTeamScope.present) { const selfError = bossSelfSessionError(bossTeamScope, connectedClient.sessionId); if (selfError) throw new BossTeamScopeError("BOSS_TEAM_METADATA_INVALID", selfError); } const sessions = await connectedClient.listSessions(); const team = bossTeamScope.restricted ? resolveBossIntercomTeam({ selfId: connectedClient.sessionId, sessions, scope: bossTeamScope }) : await resolveIntercomTeam({ selfId: connectedClient.sessionId, sessions }); return { content: [{ type: "text", text: formatIntercomTeam(team) }], details: team }; } catch (error) { return { content: [{ type: "text", text: `Failed to resolve intercom team: ${getErrorMessage(error)}` }], details: toolErrorDetails(error) }; } }, renderCall(_args, theme) { return new Text(theme.fg("toolTitle", theme.bold("intercom_team")), 0, 0); }, renderResult(result, { isPartial }, theme, context) { if (isPartial) return new Text(theme.fg("warning", "Resolving team..."), 0, 0); const details = result.details as { error?: boolean } | undefined; const failed = Boolean(context.isError || details?.error === true); return new Text(`${failed ? theme.fg("error", "βœ— ") : theme.fg("success", "βœ“ ")}${theme.fg(failed ? "error" : "text", firstTextContent(result))}`, 0, 0); }, } as any); for (const definition of [ { name: "intercom_list", label: "Intercom List", action: "list", description: "List active local intercom sessions.", promptSnippet: "List active local intercom sessions." }, { name: "intercom_pending", label: "Intercom Pending", action: "pending", description: "List unresolved inbound intercom asks with stable IDs, or retrieve one ask's full body. Managers may inspect an owned coworker's inbox. This does not list questions you sent to other sessions.", promptSnippet: "List unresolved inbound asks or retrieve one full ask by its stable ID." }, { name: "intercom_status", label: "Intercom Status", action: "status", description: "Show this session's intercom connection status.", promptSnippet: "Show intercom connection status." }, ] as const) { if (definition.name === "intercom_list" && bossTeamScope.restricted) continue; pi.registerTool({ name: definition.name, label: definition.label, description: definition.description, promptSnippet: definition.promptSnippet, parameters: definition.name === "intercom_pending" ? Type.Object({ askId: Type.Optional(Type.String({ description: "Stable ask ID to retrieve with its untruncated body" })), session: Type.Optional(Type.String({ description: "Owned coworker session ID; available only to that coworker's manager" })), }) : Type.Object({}), execute: executeSplitAction(definition.action), renderCall: renderSplitCall(definition.action), renderResult: renderSplitResult, } as any); } if (config.legacyTool) { pi.registerTool(legacyIntercomTool); } async function resolveCurrentContact(ctx: ExtensionContext, generation = runtimeGeneration): Promise<{ target: string; name?: string; id: string; duplicateName: boolean } | undefined> { let contactClient: IntercomClient; try { contactClient = await ensureConnected("overlay"); } catch (error) { notifyIfLive(ctx, `Intercom unavailable: ${getErrorMessage(error)}`, "error", generation); return undefined; } if (!getLiveContext(ctx, generation)) return undefined; syncPresenceIdentity(ctx.sessionManager.getSessionId()); try { if (bossTeamScope.present) { const selfError = bossSelfSessionError(bossTeamScope, contactClient.sessionId); if (selfError) throw new BossTeamScopeError("BOSS_TEAM_METADATA_INVALID", selfError); } const sessions = await contactClient.listSessions(); const currentSession = sessions.find(s => s.id === contactClient.sessionId); if (!currentSession || bossTeamScope.present) { return { target: contactClient.sessionId, id: contactClient.sessionId, duplicateName: false }; } return chooseContactTarget(currentSession, sessions); } catch (error) { notifyIfLive(ctx, `Failed to read intercom id: ${getErrorMessage(error)}`, "error", generation); return undefined; } } function insertIntoEditor(ctx: ExtensionContext, text: string): void { const existing = ctx.ui.getEditorText?.() ?? ""; const next = existing.trim() ? `${existing.trimEnd()}\n\n${text}` : text; ctx.ui.setEditorText(next); } async function showIntercomId(ctx: ExtensionContext, mode: "copy" | "insert" = "copy"): Promise { const generation = runtimeGeneration; const liveContext = getLiveContext(ctx, generation); if (!liveContext) return; const contact = await resolveCurrentContact(liveContext, generation); if (!contact || !getLiveContext(liveContext, generation)) return; const instruction = formatContactInstruction(contact); if (mode === "insert") { if (!liveContext.hasUI) { notifyIfLive(liveContext, `Intercom target: ${contact.target}`, "info", generation); return; } insertIntoEditor(liveContext, instruction); notifyIfLive(liveContext, `Inserted intercom contact target for another agent: ${contact.target}`, "info", generation); return; } const copied = copyTextToClipboard(instruction); if (copied.ok) { notifyIfLive(liveContext, `Copied intercom contact target for another agent: ${contact.target}`, "info", generation); return; } if (liveContext.hasUI) { insertIntoEditor(liveContext, instruction); notifyIfLive(liveContext, `Clipboard unavailable; inserted intercom contact target for another agent: ${contact.target}`, "warning", generation); return; } notifyIfLive(liveContext, `Intercom target: ${contact.target}`, "info", generation); } async function openIntercomOverlay(ctx: ExtensionContext): Promise { const overlayGeneration = runtimeGeneration; const liveContext = getLiveContext(ctx, overlayGeneration); if (!liveContext?.hasUI || (liveContext as ExtensionContext & { mode?: string }).mode !== "tui") return; let overlayClient: IntercomClient; try { overlayClient = await ensureConnected("overlay"); } catch (error) { notifyIfLive(ctx, `Intercom unavailable: ${getErrorMessage(error)}`, "error", overlayGeneration); return; } if (!getLiveContext(ctx, overlayGeneration)) return; syncPresenceIdentity(ctx.sessionManager.getSessionId()); if (bossTeamScope.present) { const selfError = bossSelfSessionError(bossTeamScope, overlayClient.sessionId); if (selfError) { notifyIfLive(ctx, `Intercom unavailable: ${selfError}`, "error", overlayGeneration); return; } } let currentSession: SessionInfo; let sessions: SessionInfo[]; let duplicates: Set; try { const mySessionId = overlayClient.sessionId; const listedSessions = await overlayClient.listSessions(); if (!getLiveContext(ctx, overlayGeneration)) return; const allSessions = filterBossSessions(bossTeamScope, listedSessions, mySessionId); const foundCurrentSession = allSessions.find(s => s.id === mySessionId); if (!foundCurrentSession) { notifyIfLive(ctx, "Current session is missing from intercom session list", "error", overlayGeneration); return; } currentSession = foundCurrentSession; duplicates = duplicateSessionNames(allSessions); sessions = allSessions.filter(s => s.id !== mySessionId); } catch (error) { notifyIfLive(ctx, `Failed to list sessions: ${getErrorMessage(error)}`, "error", overlayGeneration); return; } const selectedSession = await ctx.ui.custom( (_tui, theme, keybindings, done) => new SessionListOverlay(theme, keybindings, currentSession, sessions, done), { overlay: true, overlayOptions: { width: 88 } } ).catch(() => undefined); if (!selectedSession || !getLiveContext(ctx, overlayGeneration)) return; try { overlayClient = await ensureConnected("overlay"); } catch (error) { notifyIfLive(ctx, `Intercom unavailable: ${getErrorMessage(error)}`, "error", overlayGeneration); return; } if (!getLiveContext(ctx, overlayGeneration)) return; const idPrefixes = shortestUniqueIdPrefixes([currentSession.id, ...sessions.map((session) => session.id)], 8); const targetLabel = formatSessionLabel(selectedSession, duplicates, idPrefixes.get(selectedSession.id) ?? selectedSession.id); const result = await ctx.ui.custom( (tui, theme, keybindings, done) => new ComposeOverlay(tui, theme, keybindings, selectedSession, targetLabel, overlayClient, done), { overlay: true, overlayOptions: { width: 72 } } ).catch(() => undefined); if (result?.sent && result.messageId && result.text && getLiveContext(ctx, overlayGeneration)) { pi.appendEntry("intercom_sent", { to: selectedSession.name || selectedSession.id, message: { text: result.text }, messageId: result.messageId, timestamp: Date.now(), }); notifyIfLive(ctx, `Message sent to ${targetLabel}`, "info", overlayGeneration); } } pi.registerCommand("intercom", { description: "Open session intercom overlay", handler: async (_args, ctx) => openIntercomOverlay(ctx), }); pi.registerCommand("intercom-id", { description: "Copy this session's intercom contact target for another agent. Use /intercom-id insert to put the handoff snippet in the editor.", handler: async (args, ctx) => { const mode = args.trim().toLowerCase(); await showIntercomId(ctx, mode === "insert" || mode === "editor" ? "insert" : "copy"); }, }); pi.registerCommand("intercom-join", { description: "Join an existing TmuxDeck workspace intercom circle. This does not enroll you as a Team Worker.", handler: async (args, ctx) => { const zh = isZhLocale(); const fail = (workspace?: string) => { notifyIfLive(ctx, workspace ? (zh ? `ζ— ζ³•εŠ ε…₯ε·₯作区 ${workspace} ηš„ι€šθ―εœˆγ€‚` : `Could not join the intercom circle for workspace ${workspace}.`) : (zh ? "ζ— ζ³•εŠ ε…₯θ―₯ε·₯δ½œεŒΊι€šθ―εœˆγ€‚" : "Could not join that workspace intercom circle."), "error"); }; try { const blocked = rejectManagedJoin(classifyMembership(), zh); if (blocked) { notifyIfLive(ctx, blocked, "error"); return; } const parsed = parseJoinArgs(args); const workspaces = await listScopedWorkspaces(); let workspace: string | undefined; if (parsed.kind === "list") { notifyIfLive(ctx, formatJoinableWorkspaceList({ workspaces, zh }), "info"); return; } if (parsed.kind === "index" && parsed.index) { const selected = workspaces[parsed.index - 1]; if (!selected) { notifyIfLive(ctx, zh ? "ζ²‘ζœ‰θΏ™δΈͺηΌ–ε·ηš„ε·₯δ½œεŒΊγ€‚" : "No workspace uses that number.", "error"); return; } workspace = selected.sessionName; } else if (parsed.kind === "workspace" && parsed.workspace) { workspace = parsed.workspace; } else if (parsed.kind === "scope" && parsed.scope) { workspace = await workspaceNameForScope(parsed.scope); if (!workspace) { fail(); return; } } if (!workspace) { fail(); return; } const scope = await readSessionScope(workspace); if (!scope) { fail(workspace); return; } if (!getLiveContext(ctx)) { startSessionRuntime(ctx); } await switchRuntimeScope(scope); syncPresenceIdentity(currentSessionId ?? ctx.sessionManager.getSessionId()); const displayName = currentSessionId ? buildPresenceIdentity(pi, currentSessionId).name : (pi.getSessionName()?.trim() || "unnamed"); notifyIfLive(ctx, formatJoinSuccess({ workspace, name: displayName, zh, }), "info"); } catch (error) { notifyIfLive(ctx, getErrorMessage(error), "error"); } }, }); pi.registerCommand("intercom-status", { description: "Show whether this session is standalone, same-scope, tmuxdeck-team, or orchestrator.", handler: async (_args, ctx) => { const zh = isZhLocale(); try { const membership = classifyMembership(); const workspace = await currentTmuxWorkspace() ?? (runtimeScopeId ? await workspaceNameForScope(runtimeScopeId) : undefined); const displayName = currentSessionId ? buildPresenceIdentity(pi, currentSessionId).name : (pi.getSessionName()?.trim() || "unnamed"); let peers: string[] = []; if (client?.isConnected()) { const sessions = await client.listSessions(); peers = sessions .filter((session) => session.id !== client?.sessionId && session.model !== "human") .map((session) => session.name || session.id); } notifyIfLive(ctx, formatJoinStatus({ membership, workspace, name: displayName, peers, zh, }), "info"); } catch (error) { notifyIfLive(ctx, getErrorMessage(error), "error"); } }, }); pi.registerShortcut("alt+m", { description: "Open session intercom", handler: async (ctx) => openIntercomOverlay(ctx), }); pi.registerShortcut("alt+i", { description: "Copy this session's intercom contact target for another agent, falling back to editor insert", handler: async (ctx) => showIntercomId(ctx, "copy"), }); }