@overmux/pi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +118 -0
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.js +40 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/config-2jNv4tol.d.ts +34 -0
  6. package/dist/config-2jNv4tol.d.ts.map +1 -0
  7. package/dist/config.d.ts +2 -0
  8. package/dist/config.js +47 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/extension.d.ts +19 -0
  11. package/dist/extension.d.ts.map +1 -0
  12. package/dist/extension.js +265 -0
  13. package/dist/extension.js.map +1 -0
  14. package/dist/index.d.ts +10 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +16 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/jsonl-tail.d.ts +13 -0
  19. package/dist/jsonl-tail.d.ts.map +1 -0
  20. package/dist/jsonl-tail.js +83 -0
  21. package/dist/jsonl-tail.js.map +1 -0
  22. package/dist/live-events-ClhFOMGW.js +497 -0
  23. package/dist/live-events-ClhFOMGW.js.map +1 -0
  24. package/dist/live-events-DAmf6RRx.d.ts +107 -0
  25. package/dist/live-events-DAmf6RRx.d.ts.map +1 -0
  26. package/dist/live-events.d.ts +2 -0
  27. package/dist/live-events.js +2 -0
  28. package/dist/notification-DzOd9cRc.d.ts +20 -0
  29. package/dist/notification-DzOd9cRc.d.ts.map +1 -0
  30. package/dist/notification.d.ts +2 -0
  31. package/dist/notification.js +54 -0
  32. package/dist/notification.js.map +1 -0
  33. package/dist/plugin.d.ts +90 -0
  34. package/dist/plugin.d.ts.map +1 -0
  35. package/dist/plugin.js +213 -0
  36. package/dist/plugin.js.map +1 -0
  37. package/dist/projection.d.ts +101 -0
  38. package/dist/projection.d.ts.map +1 -0
  39. package/dist/projection.js +550 -0
  40. package/dist/projection.js.map +1 -0
  41. package/dist/protocol-CsrnSPOv.d.ts +115 -0
  42. package/dist/protocol-CsrnSPOv.d.ts.map +1 -0
  43. package/dist/protocol.d.ts +2 -0
  44. package/dist/protocol.js +365 -0
  45. package/dist/protocol.js.map +1 -0
  46. package/dist/react.d.ts +139 -0
  47. package/dist/react.d.ts.map +1 -0
  48. package/dist/react.js +796 -0
  49. package/dist/react.js.map +1 -0
  50. package/dist/server.d.ts +5 -0
  51. package/dist/server.js +4 -0
  52. package/dist/session-status-CZLTo8Km.d.ts +71 -0
  53. package/dist/session-status-CZLTo8Km.d.ts.map +1 -0
  54. package/dist/styles.css +619 -0
  55. package/docs/index.md +10 -0
  56. package/package.json +115 -0
  57. package/src/cli.ts +59 -0
  58. package/src/config.ts +87 -0
  59. package/src/extension.ts +486 -0
  60. package/src/index.ts +14 -0
  61. package/src/jsonl-tail.ts +110 -0
  62. package/src/live-events.ts +578 -0
  63. package/src/notification.ts +109 -0
  64. package/src/plugin.ts +369 -0
  65. package/src/projection.ts +995 -0
  66. package/src/protocol.ts +805 -0
  67. package/src/react.tsx +1293 -0
  68. package/src/server.ts +15 -0
  69. package/src/session-status.ts +379 -0
  70. package/src/styles.css +619 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection.js","names":[],"sources":["../src/projection.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { z } from \"zod\";\nimport {\n parseLiveEventRecord,\n type LiveEventWireRecord as LiveRecord,\n} from \"./live-events.js\";\nimport {\n probePiSession,\n sendUserMessage,\n type UserMessageInput,\n type UserMessageResponse,\n} from \"./protocol.js\";\nimport { createJsonlTail, type JsonlTail } from \"./jsonl-tail.js\";\n\nexport type PiContentBlock = {\n arguments?: unknown;\n data?: string;\n id?: string;\n mimeType?: string;\n name?: string;\n text?: string;\n thinking?: string;\n tool?: PiToolProjection;\n type: string;\n};\nexport type PiToolResultProjection = {\n content: PiContentBlock[];\n details?: unknown;\n isError: boolean;\n toolCallId: string;\n toolName: string;\n};\nexport type PiToolProjection = {\n result?: PiToolResultProjection;\n status: \"error\" | \"pending\" | \"success\";\n};\nexport type PiConversationEntry = {\n content: PiContentBlock[];\n details?: unknown;\n errorMessage?: string;\n id: string;\n isError?: boolean;\n role: \"assistant\" | \"toolResult\" | \"user\";\n source: \"canonical\" | \"live\";\n status: \"complete\" | \"error\" | \"pending\";\n stopReason?: string;\n timestamp?: number;\n toolCallId?: string;\n toolName?: string;\n};\n\nexport const piContentBlockSchema: z.ZodType<PiContentBlock> = z.lazy(() =>\n z.object({\n arguments: z.unknown().optional(),\n data: z.string().optional(),\n id: z.string().optional(),\n mimeType: z.string().optional(),\n name: z.string().optional(),\n text: z.string().optional(),\n thinking: z.string().optional(),\n tool: piToolProjectionSchema.optional(),\n type: z.string(),\n }),\n);\n\nexport const piToolResultProjectionSchema: z.ZodType<PiToolResultProjection> =\n z.object({\n content: z.array(piContentBlockSchema),\n details: z.unknown().optional(),\n isError: z.boolean(),\n toolCallId: z.string(),\n toolName: z.string(),\n });\n\nexport const piToolProjectionSchema: z.ZodType<PiToolProjection> = z.lazy(() =>\n z.object({\n result: piToolResultProjectionSchema.optional(),\n status: z.enum([\"error\", \"pending\", \"success\"]),\n }),\n);\nexport const piSessionMetadataSchema = z\n .object({\n contextUsage: z\n .object({\n tokens: z.number().nonnegative(),\n contextWindow: z.number().positive(),\n percent: z.number().nonnegative(),\n })\n .strict()\n .optional(),\n model: z\n .object({\n provider: z.string().min(1),\n id: z.string().min(1),\n name: z.string().min(1),\n })\n .strict()\n .optional(),\n thinkingLevel: z.string().min(1),\n modelOptions: z\n .array(\n z\n .object({\n provider: z.string().min(1),\n id: z.string().min(1),\n name: z.string().min(1),\n })\n .strict(),\n )\n .max(256)\n .optional(),\n })\n .strict();\n\nexport type PiSessionMetadata = z.infer<typeof piSessionMetadataSchema>;\nexport type PiConversationSnapshot = {\n agentAvailable: boolean;\n entries: PiConversationEntry[];\n sessionMetadata?: PiSessionMetadata;\n status: \"busy\" | \"degraded\" | \"idle\" | \"offline\";\n};\n\nexport const piConversationSnapshotSchema: z.ZodType<PiConversationSnapshot> =\n z.object({\n agentAvailable: z.boolean(),\n entries: z.array(\n z.object({\n content: z.array(piContentBlockSchema),\n details: z.unknown().optional(),\n errorMessage: z.string().optional(),\n id: z.string(),\n isError: z.boolean().optional(),\n role: z.enum([\"assistant\", \"toolResult\", \"user\"]),\n source: z.enum([\"canonical\", \"live\"]),\n status: z.enum([\"complete\", \"error\", \"pending\"]),\n stopReason: z.string().optional(),\n timestamp: z.number().optional(),\n toolCallId: z.string().optional(),\n toolName: z.string().optional(),\n }),\n ),\n sessionMetadata: piSessionMetadataSchema.optional(),\n status: z.enum([\"busy\", \"degraded\", \"idle\", \"offline\"]),\n });\n\nexport type PiAgentSession = {\n id: string;\n liveEventsDir?: string;\n sessionFile: string;\n sessionMetadata?: PiSessionMetadata;\n};\n\ntype PiAgentResource = PiAgentSession & { sessionId: string };\n\ntype PiMessage = {\n content?: string | unknown[];\n details?: unknown;\n errorMessage?: string;\n isError?: boolean;\n role?: string;\n stopReason?: string;\n timestamp?: number | string;\n toolCallId?: string;\n toolName?: string;\n};\n\ntype StreamState = {\n conflict: boolean;\n records: Map<number, LiveRecord>;\n tail: JsonlTail;\n};\ntype LiveMessageState = {\n blocks: PiContentBlock[];\n ended: boolean;\n id: string;\n message?: PiMessage;\n messageSequence: number;\n sourceOrder: number;\n status: PiConversationEntry[\"status\"];\n streamId: string;\n timestamp?: number;\n};\ntype ToolState = {\n isError?: boolean;\n result?: unknown;\n status: PiToolProjection[\"status\"];\n toolCallId: string;\n toolName: string;\n};\ntype ProjectionState = {\n agentAvailable?: boolean;\n canonicalRecords: unknown[];\n canonicalTail: JsonlTail;\n configKey: string;\n refreshQueue: Promise<void>;\n sessionMetadata?: PiSessionMetadata;\n snapshot?: PiConversationSnapshot;\n streams: Map<string, StreamState>;\n};\ntype SnapshotListener = (snapshot: PiConversationSnapshot) => void;\n\nexport type PiAgentConversationService = {\n dispose: () => void;\n getSnapshot: (agentId: string) => Promise<PiConversationSnapshot>;\n subscribe: (agentId: string, listener: SnapshotListener) => () => void;\n};\n\ntype PiMessageResponse = Extract<UserMessageResponse, { ok: true }>;\n\nexport type PiMessageSender = (\n sessionId: string,\n request: UserMessageInput,\n) => Promise<UserMessageResponse | undefined>;\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst timestampOf = (value: unknown): number | undefined => {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n if (typeof value !== \"string\") {\n return undefined;\n }\n const timestamp = Date.parse(value);\n return Number.isNaN(timestamp) ? undefined : timestamp;\n};\n\nconst contentBlocksOf = (content: PiMessage[\"content\"]): PiContentBlock[] => {\n if (typeof content === \"string\") {\n return [{ text: content, type: \"text\" }];\n }\n if (!Array.isArray(content)) {\n return [];\n }\n return content.flatMap((value) => {\n if (!isObject(value) || typeof value.type !== \"string\") {\n return [];\n }\n return [{ ...value, type: value.type } as PiContentBlock];\n });\n};\n\nconst browserToolDetails = (details: unknown) => {\n if (!isObject(details)) {\n return details;\n }\n const { fullOutputPath: _fullOutputPath, ...safeDetails } = details;\n return safeDetails;\n};\n\nconst toolResultOf = (\n message: PiMessage,\n): PiToolResultProjection | undefined => {\n if (\n message.role !== \"toolResult\" ||\n typeof message.toolCallId !== \"string\" ||\n typeof message.toolName !== \"string\"\n ) {\n return undefined;\n }\n return {\n content: contentBlocksOf(message.content),\n details: browserToolDetails(message.details),\n isError: message.isError === true,\n toolCallId: message.toolCallId,\n toolName: message.toolName,\n };\n};\n\nconst entryOf = (\n value: unknown,\n source: PiConversationEntry[\"source\"],\n): PiConversationEntry | undefined => {\n if (\n !isObject(value) ||\n value.type !== \"message\" ||\n !isObject(value.message)\n ) {\n return undefined;\n }\n if (typeof value.id !== \"string\") {\n return undefined;\n }\n const message = value.message as PiMessage;\n if (\n message.role !== \"assistant\" &&\n message.role !== \"toolResult\" &&\n message.role !== \"user\"\n ) {\n return undefined;\n }\n return {\n content: contentBlocksOf(message.content),\n details: message.details,\n errorMessage: message.errorMessage,\n id: value.id,\n isError: message.isError,\n role: message.role,\n source,\n status:\n message.isError === true || message.stopReason === \"error\"\n ? \"error\"\n : \"complete\",\n stopReason: message.stopReason,\n timestamp: timestampOf(value.timestamp) ?? timestampOf(message.timestamp),\n toolCallId: message.toolCallId,\n toolName: message.toolName,\n };\n};\n\nconst appendDelta = (\n blocks: PiContentBlock[],\n event: Record<string, unknown>,\n) => {\n const update = event.assistantMessageEvent;\n if (!isObject(update) || !Number.isSafeInteger(update.contentIndex)) {\n return;\n }\n const index = update.contentIndex as number;\n if (index < 0) {\n return;\n }\n const current = blocks[index] ?? { type: \"text\" };\n if (update.type === \"text_start\") {\n blocks[index] = { type: \"text\" };\n }\n if (update.type === \"thinking_start\") {\n blocks[index] = { type: \"thinking\" };\n }\n if (update.type === \"toolcall_start\") {\n blocks[index] = { type: \"toolCall\" };\n }\n if (update.type === \"text_delta\") {\n blocks[index] = {\n ...current,\n text: `${current.text ?? \"\"}${typeof update.delta === \"string\" ? update.delta : \"\"}`,\n type: \"text\",\n };\n }\n if (update.type === \"thinking_delta\") {\n blocks[index] = {\n ...current,\n thinking: `${current.thinking ?? \"\"}${typeof update.delta === \"string\" ? update.delta : \"\"}`,\n type: \"thinking\",\n };\n }\n if (update.type === \"toolcall_delta\") {\n blocks[index] = {\n ...current,\n arguments: `${typeof current.arguments === \"string\" ? current.arguments : \"\"}${typeof update.delta === \"string\" ? update.delta : \"\"}`,\n type: \"toolCall\",\n };\n }\n};\n\nconst updateMessage = (\n messages: Map<string, LiveMessageState>,\n record: LiveRecord,\n sourceOrder: number,\n) => {\n const event = record.event;\n if (typeof event.messageId !== \"string\") {\n return;\n }\n const key = `${record.streamId}\\0${event.messageId}`;\n const current = messages.get(key) ?? {\n blocks: [],\n ended: false,\n id: key,\n messageSequence:\n typeof event.messageSequence === \"number\"\n ? event.messageSequence\n : sourceOrder,\n sourceOrder,\n status: \"pending\" as const,\n streamId: record.streamId,\n };\n current.timestamp = record.timestamp;\n if (event.type === \"message_start\" && isObject(event.message)) {\n current.message = event.message as PiMessage;\n }\n if (event.type === \"message_update\") {\n appendDelta(current.blocks, event);\n const update = event.assistantMessageEvent;\n if (isObject(update) && update.type === \"done\") {\n current.status = \"complete\";\n }\n if (isObject(update) && update.type === \"error\") {\n current.status = \"error\";\n current.message = {\n ...current.message,\n errorMessage:\n typeof update.errorMessage === \"string\"\n ? update.errorMessage\n : undefined,\n };\n }\n }\n if (event.type === \"message_end\" && isObject(event.message)) {\n current.ended = true;\n current.message = event.message as PiMessage;\n current.blocks = contentBlocksOf(current.message.content);\n current.status =\n current.message.isError === true || current.message.stopReason === \"error\"\n ? \"error\"\n : \"complete\";\n }\n messages.set(key, current);\n};\n\nconst updateTool = (\n tools: Map<string, ToolState>,\n event: Record<string, unknown>,\n) => {\n if (\n (event.type !== \"tool_execution_start\" &&\n event.type !== \"tool_execution_end\") ||\n typeof event.toolCallId !== \"string\" ||\n typeof event.toolName !== \"string\"\n ) {\n return;\n }\n tools.set(event.toolCallId, {\n isError: event.type === \"tool_execution_end\" && event.isError === true,\n result: event.type === \"tool_execution_end\" ? event.result : undefined,\n status:\n event.type === \"tool_execution_start\"\n ? \"pending\"\n : event.isError === true\n ? \"error\"\n : \"success\",\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n });\n};\n\nconst liveEntryOf = (\n state: LiveMessageState,\n): PiConversationEntry | undefined => {\n const message = state.message;\n if (!message) {\n return undefined;\n }\n const role = message.role;\n if (role !== \"assistant\" && role !== \"toolResult\" && role !== \"user\") {\n return undefined;\n }\n return {\n content: state.ended\n ? contentBlocksOf(message.content)\n : state.blocks.length\n ? state.blocks\n : contentBlocksOf(message.content),\n details: message.details,\n errorMessage: message.errorMessage,\n id: state.id,\n isError: message.isError,\n role,\n source: \"live\",\n status: state.status,\n stopReason: message.stopReason,\n timestamp: state.timestamp ?? timestampOf(message.timestamp),\n toolCallId: message.toolCallId,\n toolName: message.toolName,\n };\n};\n\nconst projectLive = (records: LiveRecord[]) => {\n const messages = new Map<string, LiveMessageState>();\n const tools = new Map<string, ToolState>();\n let active = false;\n let shutdown = false;\n records.forEach((record, index) => {\n updateMessage(messages, record, index);\n updateTool(tools, record.event);\n if (record.event.type === \"agent_start\") {\n active = true;\n }\n if (record.event.type === \"agent_settled\") {\n active = false;\n }\n if (record.event.type === \"session_shutdown\") {\n shutdown = true;\n }\n if (record.event.type === \"session_start\") {\n shutdown = false;\n }\n });\n return {\n active,\n entries: [...messages.values()]\n .sort(\n (left, right) =>\n (left.timestamp ?? 0) - (right.timestamp ?? 0) ||\n left.streamId.localeCompare(right.streamId) ||\n left.messageSequence - right.messageSequence,\n )\n .flatMap((message) => {\n const entry = liveEntryOf(message);\n return entry ? [entry] : [];\n }),\n shutdown,\n tools,\n };\n};\n\nconst fingerprint = (entry: PiConversationEntry) =>\n JSON.stringify({\n content: entry.content.map(({ tool: _tool, ...block }) => block),\n details: entry.details,\n isError: entry.isError,\n role: entry.role,\n toolCallId: entry.toolCallId,\n toolName: entry.toolName,\n });\n\nconst matchedLiveIndexes = (\n canonical: PiConversationEntry[],\n live: PiConversationEntry[],\n) => {\n const liveByFingerprint = new Map<string, number[]>();\n live.forEach((entry, index) => {\n if (entry.status === \"pending\") {\n return;\n }\n const key = fingerprint(entry);\n const indexes = liveByFingerprint.get(key) ?? [];\n indexes.push(index);\n liveByFingerprint.set(key, indexes);\n });\n const consumed = new Map<string, number>();\n const matched = new Set<number>();\n canonical.forEach((entry) => {\n const key = fingerprint(entry);\n const offset = consumed.get(key) ?? 0;\n const index = liveByFingerprint.get(key)?.[offset];\n if (index !== undefined) {\n matched.add(index);\n consumed.set(key, offset + 1);\n }\n });\n return matched;\n};\n\nconst contentFromToolEnd = (result: unknown): PiContentBlock[] => {\n if (isObject(result) && \"content\" in result) {\n return contentBlocksOf(result.content as PiMessage[\"content\"]);\n }\n if (typeof result === \"string\") {\n return [{ text: result, type: \"text\" }];\n }\n return result === undefined\n ? []\n : [{ text: JSON.stringify(result, null, 2), type: \"text\" }];\n};\n\nconst pairTools = (\n entries: PiConversationEntry[],\n liveTools: Map<string, ToolState>,\n) => {\n const callIds = new Set(\n entries.flatMap((entry) =>\n entry.content.flatMap((block) =>\n block.type === \"toolCall\" && typeof block.id === \"string\"\n ? [block.id]\n : [],\n ),\n ),\n );\n const results = new Map<string, PiToolResultProjection>();\n entries.forEach((entry) => {\n if (entry.role !== \"toolResult\") {\n return;\n }\n const result = toolResultOf({\n content: entry.content,\n details: entry.details,\n isError: entry.isError,\n role: entry.role,\n toolCallId: entry.toolCallId,\n toolName: entry.toolName,\n });\n if (result) {\n results.set(result.toolCallId, result);\n }\n });\n liveTools.forEach((tool) => {\n if (results.has(tool.toolCallId) || tool.result === undefined) {\n return;\n }\n results.set(tool.toolCallId, {\n content: contentFromToolEnd(tool.result),\n details: isObject(tool.result)\n ? browserToolDetails(tool.result.details)\n : undefined,\n isError: tool.isError === true,\n toolCallId: tool.toolCallId,\n toolName: tool.toolName,\n });\n });\n\n return entries\n .map((entry) => ({\n ...entry,\n content: entry.content.map((block) => {\n if (block.type !== \"toolCall\" || typeof block.id !== \"string\") {\n return block;\n }\n const result = results.get(block.id);\n const live = liveTools.get(block.id);\n return {\n ...block,\n tool: {\n result,\n status: result\n ? result.isError\n ? \"error\"\n : \"success\"\n : (live?.status ?? \"pending\"),\n },\n };\n }),\n }))\n .filter(\n (entry) =>\n entry.role !== \"toolResult\" ||\n !entry.toolCallId ||\n !callIds.has(entry.toolCallId),\n );\n};\n\nconst recordsSafeToProject = (stream: StreamState) => {\n const records = [...stream.records.values()].sort(\n (left, right) => left.sequence - right.sequence,\n );\n const gapIndex = records.findIndex(\n (record, index) => record.sequence !== index + 1,\n );\n if (gapIndex < 0) {\n return records;\n }\n return [\n ...records.slice(0, gapIndex),\n ...records\n .slice(gapIndex)\n .filter((record) => record.event.type === \"message_end\"),\n ];\n};\n\nconst sortedStreamRecords = (streams: Map<string, StreamState>) =>\n [...streams.values()]\n .flatMap(recordsSafeToProject)\n .sort(\n (left, right) =>\n left.timestamp - right.timestamp ||\n left.streamId.localeCompare(right.streamId) ||\n left.sequence - right.sequence,\n );\n\nconst streamHasIssue = (stream: StreamState) => {\n if (stream.conflict) {\n return true;\n }\n const sequences = [...stream.records.keys()].sort(\n (left, right) => left - right,\n );\n return sequences.some((sequence, index) => sequence !== index + 1);\n};\n\nconst refreshCanonical = async (\n state: ProjectionState,\n sessionFile: string | undefined,\n) => {\n if (!sessionFile) {\n const changed = state.canonicalRecords.length > 0;\n state.canonicalRecords = [];\n return changed;\n }\n const update = await state.canonicalTail.read(sessionFile);\n if (update.reset) {\n state.canonicalRecords = [];\n }\n state.canonicalRecords.push(...update.records);\n return update.reset || update.records.length > 0;\n};\n\nconst refreshStreams = async (\n state: ProjectionState,\n data: PiAgentResource,\n) => {\n if (!data.liveEventsDir) {\n const changed = state.streams.size > 0;\n state.streams.clear();\n return changed;\n }\n const directory = join(data.liveEventsDir, data.sessionId);\n const files = (await readdir(directory).catch(() => [] as string[])).filter(\n (file) => file.endsWith(\".jsonl\"),\n );\n const present = new Set(files);\n let removed = false;\n [...state.streams.keys()].forEach((file) => {\n if (!present.has(file)) {\n const stream = state.streams.get(file);\n removed = removed || Boolean(stream?.conflict || stream?.records.size);\n state.streams.delete(file);\n }\n });\n const updates = await Promise.all(\n files.map(async (file) => {\n const stream = state.streams.get(file) ?? {\n conflict: false,\n records: new Map<number, LiveRecord>(),\n tail: createJsonlTail(),\n };\n state.streams.set(file, stream);\n const update = await stream.tail.read(join(directory, file));\n let changed =\n update.reset && Boolean(stream.conflict || stream.records.size);\n if (update.reset) {\n stream.conflict = false;\n stream.records.clear();\n }\n const expectedStreamId = file.slice(0, -\".jsonl\".length);\n update.records.forEach((value) => {\n const record = parseLiveEventRecord(value, data.sessionId);\n if (!record) {\n return;\n }\n if (record.streamId !== expectedStreamId) {\n changed = changed || !stream.conflict;\n stream.conflict = true;\n return;\n }\n const previous = stream.records.get(record.sequence);\n if (previous && JSON.stringify(previous) !== JSON.stringify(record)) {\n changed = changed || !stream.conflict;\n stream.conflict = true;\n return;\n }\n changed = changed || !previous;\n stream.records.set(record.sequence, record);\n });\n return changed;\n }),\n );\n return removed || updates.some(Boolean);\n};\n\nconst activeCanonicalBranch = (records: unknown[]) => {\n const treeEntries = records.flatMap((record) => {\n if (\n !isObject(record) ||\n typeof record.id !== \"string\" ||\n (record.parentId !== null && typeof record.parentId !== \"string\")\n ) {\n return [];\n }\n return [record as Record<string, unknown> & { id: string }];\n });\n const leaf = treeEntries.at(-1);\n if (!leaf) {\n return records;\n }\n const byId = new Map(treeEntries.map((entry) => [entry.id, entry]));\n const ancestry = new Set<string>();\n for (let current: (typeof treeEntries)[number] | undefined = leaf; current;) {\n if (ancestry.has(current.id)) {\n return [];\n }\n ancestry.add(current.id);\n current =\n typeof current.parentId === \"string\"\n ? byId.get(current.parentId)\n : undefined;\n }\n return records.filter(\n (record) =>\n isObject(record) &&\n typeof record.id === \"string\" &&\n ancestry.has(record.id),\n );\n};\n\nconst projectSnapshot = (\n state: ProjectionState,\n agentAvailable: boolean,\n): PiConversationSnapshot => {\n const canonical = activeCanonicalBranch(state.canonicalRecords).flatMap(\n (value) => {\n const entry = entryOf(value, \"canonical\");\n return entry ? [entry] : [];\n },\n );\n const live = projectLive(sortedStreamRecords(state.streams));\n const matched = matchedLiveIndexes(canonical, live.entries);\n const entries = pairTools(\n [\n ...canonical,\n ...live.entries.filter((_entry, index) => !matched.has(index)),\n ].sort(\n (left, right) =>\n (left.timestamp ?? 0) - (right.timestamp ?? 0) ||\n left.id.localeCompare(right.id),\n ),\n live.tools,\n );\n const degraded = [...state.streams.values()].some(streamHasIssue);\n return piConversationSnapshotSchema.parse({\n entries,\n agentAvailable,\n ...(state.sessionMetadata\n ? { sessionMetadata: state.sessionMetadata }\n : {}),\n status: degraded\n ? \"degraded\"\n : live.shutdown || (!agentAvailable && !live.active)\n ? \"offline\"\n : live.active || entries.some((entry) => entry.status === \"pending\")\n ? \"busy\"\n : \"idle\",\n });\n};\n\nconst configKeyOf = (data: PiAgentResource) =>\n JSON.stringify([data.sessionId, data.sessionFile, data.liveEventsDir]);\n\nconst createProjectionState = (data: PiAgentResource): ProjectionState => ({\n canonicalRecords: [],\n canonicalTail: createJsonlTail(),\n configKey: configKeyOf(data),\n refreshQueue: Promise.resolve(),\n sessionMetadata: data.sessionMetadata,\n streams: new Map(),\n});\n\nconst queueProjectionRefresh = <Result>(\n state: ProjectionState,\n operation: () => Promise<Result>,\n) => {\n const result = state.refreshQueue.then(operation);\n state.refreshQueue = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n};\n\nexport const probePiAgent = probePiSession;\n\nexport const createPiAgentConversationService = ({\n resolveAgent,\n pollIntervalMs = 250,\n probe = probePiAgent,\n}: {\n resolveAgent: (agentId: string) => Promise<PiAgentResource>;\n pollIntervalMs?: number;\n probe?: (sessionId: string) => Promise<boolean>;\n}): PiAgentConversationService => {\n const projections = new Map<string, ProjectionState>();\n const subscriptions = new Map<string, Set<SnapshotListener>>();\n let timer: ReturnType<typeof setInterval> | undefined;\n let polling = false;\n\n const refresh = async (agentId: string) => {\n const data = await resolveAgent(agentId);\n const key = agentId;\n const previous = projections.get(key);\n const state =\n previous?.configKey === configKeyOf(data)\n ? previous\n : createProjectionState(data);\n projections.set(key, state);\n return queueProjectionRefresh(state, async () => {\n const metadataChanged =\n JSON.stringify(state.sessionMetadata) !==\n JSON.stringify(data.sessionMetadata);\n state.sessionMetadata = data.sessionMetadata;\n const [canonicalChanged, streamsChanged, agentAvailable] =\n await Promise.all([\n refreshCanonical(state, data.sessionFile),\n refreshStreams(state, data),\n probe(data.sessionId),\n ]);\n if (\n state.snapshot &&\n !canonicalChanged &&\n !streamsChanged &&\n !metadataChanged &&\n state.agentAvailable === agentAvailable\n ) {\n return state.snapshot;\n }\n const snapshot = projectSnapshot(state, agentAvailable);\n state.agentAvailable = agentAvailable;\n state.snapshot = snapshot;\n return snapshot;\n });\n };\n\n const poll = async () => {\n if (polling) {\n return;\n }\n polling = true;\n try {\n await Promise.all(\n [...subscriptions.entries()].map(async ([key, listeners]) => {\n const id = key;\n try {\n const state = projections.get(key);\n const before = state?.snapshot;\n const snapshot = await refresh(id);\n if (snapshot !== before) {\n listeners.forEach((listener) => listener(snapshot));\n }\n } catch {\n const state = projections.get(key);\n if (!state || state.agentAvailable === false) {\n return;\n }\n const snapshot = projectSnapshot(state, false);\n state.agentAvailable = false;\n state.snapshot = snapshot;\n listeners.forEach((listener) => listener(snapshot));\n }\n }),\n );\n } finally {\n polling = false;\n }\n };\n\n const startPolling = () => {\n if (!timer) {\n timer = setInterval(() => void poll(), pollIntervalMs);\n }\n };\n\n return {\n dispose: () => {\n if (timer) {\n clearInterval(timer);\n }\n timer = undefined;\n subscriptions.clear();\n projections.clear();\n },\n getSnapshot: (agentId) => refresh(agentId),\n subscribe: (agentId, listener) => {\n const key = agentId;\n const listeners = subscriptions.get(key) ?? new Set<SnapshotListener>();\n listeners.add(listener);\n subscriptions.set(key, listeners);\n startPolling();\n return () => {\n listeners.delete(listener);\n if (!listeners.size) {\n subscriptions.delete(key);\n }\n if (!subscriptions.size && timer) {\n clearInterval(timer);\n timer = undefined;\n }\n };\n },\n };\n};\n\nexport const sendPiAgentMessage = async (\n {\n deliverAs,\n message,\n sessionId,\n }: {\n deliverAs: \"followUp\" | \"steer\";\n message: string;\n sessionId: string;\n },\n send: PiMessageSender = sendUserMessage,\n): Promise<PiMessageResponse> => {\n const response = await send(sessionId, {\n deliverAs,\n message,\n requestId: randomUUID(),\n });\n if (!response?.ok) {\n throw new Error(\"Pi agent is unavailable\");\n }\n return response;\n};\n"],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ //#region src/protocol.d.ts
2
+ declare const CHILD_ENV = "PI_CHILD";
3
+ declare const PARENT_SESSION_ENV = "PI_PARENT_SESSION_ID";
4
+ declare const TASK_SLUG_ENV = "PI_TASK_SLUG";
5
+ declare const TASK_SLUG_PATTERN: RegExp;
6
+ declare const DEFAULT_OVERMUX_PI_RUNTIME_DIR: string;
7
+ type DelegateSettledEnvelope = {
8
+ childSessionId: string;
9
+ taskSlug: string;
10
+ leafId: string;
11
+ cwd: string;
12
+ timestamp: number;
13
+ };
14
+ type UserMessageRequest = {
15
+ version: 1;
16
+ requestId: string;
17
+ type: "user_message";
18
+ message: string;
19
+ deliverAs: "steer" | "followUp";
20
+ expandPromptTemplates: boolean;
21
+ };
22
+ type UserMessageResponse = {
23
+ version: 1;
24
+ requestId: string;
25
+ ok: true;
26
+ delivery: "immediate" | "steer" | "followUp";
27
+ } | {
28
+ version: 1;
29
+ requestId: string;
30
+ ok: false;
31
+ error: "shutting_down" | "unavailable";
32
+ };
33
+ type UserMessageInput = Omit<UserMessageRequest, "version" | "type" | "expandPromptTemplates"> & {
34
+ expandPromptTemplates?: boolean;
35
+ };
36
+ type AbortRequest = {
37
+ version: 1;
38
+ requestId: string;
39
+ type: "abort";
40
+ };
41
+ type AbortResponse = {
42
+ version: 1;
43
+ requestId: string;
44
+ ok: true;
45
+ } | {
46
+ version: 1;
47
+ requestId: string;
48
+ ok: false;
49
+ error: "shutting_down" | "unavailable";
50
+ };
51
+ type AbortInput = Omit<AbortRequest, "version" | "type">;
52
+ type PiControlError = "no_key" | "not_found" | "shutting_down" | "unavailable";
53
+ type PiControlResponse = {
54
+ version: 1;
55
+ requestId: string;
56
+ ok: true;
57
+ } | {
58
+ version: 1;
59
+ requestId: string;
60
+ ok: false;
61
+ error: PiControlError;
62
+ };
63
+ type SetModelRequest = {
64
+ version: 1;
65
+ requestId: string;
66
+ type: "set_model";
67
+ provider: string;
68
+ id: string;
69
+ };
70
+ type SetModelInput = Omit<SetModelRequest, "version" | "type">;
71
+ type SetModelResponse = PiControlResponse;
72
+ type SetThinkingLevelRequest = {
73
+ version: 1;
74
+ requestId: string;
75
+ type: "set_thinking_level";
76
+ level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
77
+ };
78
+ type SetThinkingLevelInput = Omit<SetThinkingLevelRequest, "version" | "type">;
79
+ type SetThinkingLevelResponse = PiControlResponse;
80
+ type Receiver = {
81
+ close: () => Promise<void>;
82
+ };
83
+ type ReceiverOptions = {
84
+ onAbort?: (request: AbortRequest) => AbortResponse;
85
+ onSetModel?: (request: SetModelRequest) => Promise<SetModelResponse>;
86
+ onSetThinkingLevel?: (request: SetThinkingLevelRequest) => SetThinkingLevelResponse;
87
+ onUserMessage?: (request: UserMessageRequest) => UserMessageResponse;
88
+ };
89
+ type SenderOptions = {
90
+ runtimeDir?: string;
91
+ timeoutMs?: number;
92
+ retryDelaysMs?: readonly number[];
93
+ };
94
+ type ProbeOptions = Pick<SenderOptions, "runtimeDir" | "timeoutMs">;
95
+ declare const socketPathForSession: (sessionId: string, runtimeDir?: string) => string;
96
+ declare const parseEnvelope: (input: string) => DelegateSettledEnvelope | undefined;
97
+ declare const parseAbortRequest: (input: string) => AbortRequest | undefined;
98
+ declare const parseSetModelRequest: (input: string) => SetModelRequest | undefined;
99
+ declare const parseSetThinkingLevelRequest: (input: string) => SetThinkingLevelRequest | undefined;
100
+ declare const parseUserMessageRequest: (input: string) => UserMessageRequest | undefined;
101
+ declare const eventKey: ({ childSessionId, leafId }: DelegateSettledEnvelope) => string;
102
+ declare const startReceiver: (socketPath: string, onEnvelope: (envelope: DelegateSettledEnvelope) => void, options?: ReceiverOptions | number, timeoutMs?: number) => Promise<Receiver>;
103
+ declare const probePiSession: (sessionId: string, options?: ProbeOptions) => Promise<boolean>;
104
+ declare const sendEnvelope: (socketPath: string, envelope: DelegateSettledEnvelope, options?: {
105
+ ackTimeoutMs?: number;
106
+ retryDelaysMs?: readonly number[];
107
+ }) => Promise<boolean>;
108
+ declare const sendUserMessage: (sessionId: string, request: UserMessageInput, options?: SenderOptions) => Promise<UserMessageResponse | undefined>;
109
+ declare const sendAbort: (sessionId: string, request: AbortInput, options?: SenderOptions) => Promise<AbortResponse | undefined>;
110
+ declare const sendSetModel: (sessionId: string, request: SetModelInput, options?: SenderOptions) => Promise<SetModelResponse | undefined>;
111
+ declare const sendSetThinkingLevel: (sessionId: string, request: SetThinkingLevelInput, options?: SenderOptions) => Promise<SetThinkingLevelResponse | undefined>;
112
+ declare const ensureRuntimeDir: (runtimeDir: string) => Promise<void>;
113
+ //#endregion
114
+ export { probePiSession as A, ensureRuntimeDir as C, parseSetModelRequest as D, parseEnvelope as E, sendUserMessage as F, socketPathForSession as I, startReceiver as L, sendEnvelope as M, sendSetModel as N, parseSetThinkingLevelRequest as O, sendSetThinkingLevel as P, UserMessageResponse as S, parseAbortRequest as T, SetThinkingLevelResponse as _, DEFAULT_OVERMUX_PI_RUNTIME_DIR as a, UserMessageInput as b, ProbeOptions as c, SenderOptions as d, SetModelInput as f, SetThinkingLevelRequest as g, SetThinkingLevelInput as h, CHILD_ENV as i, sendAbort as j, parseUserMessageRequest as k, Receiver as l, SetModelResponse as m, AbortRequest as n, DelegateSettledEnvelope as o, SetModelRequest as p, AbortResponse as r, PARENT_SESSION_ENV as s, AbortInput as t, ReceiverOptions as u, TASK_SLUG_ENV as v, eventKey as w, UserMessageRequest as x, TASK_SLUG_PATTERN as y };
115
+ //# sourceMappingURL=protocol-CsrnSPOv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-CsrnSPOv.d.ts","names":[],"sources":["../src/protocol.ts"],"mappings":";cASa;cACA;cACA;cAaA,mBAAiB;cACjB;KAED;EACV;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA;;KAGU;EAEN;EACA;EACA;EACA;;EAGA;EACA;EACA;EACA;;KAGM,mBAAmB,KAC7B;EAGA;;KAGU;EACV;EACA;EACA;;KAGU;EACN;EAAY;EAAmB;;EAE/B;EACA;EACA;EACA;;KAGM,aAAa,KAAK;KAEzB;KACA;EACC;EAAY;EAAmB;;EAC/B;EAAY;EAAmB;EAAW,OAAO;;KAE3C;EACV;EACA;EACA;EACA;EACA;;KAEU,gBAAgB,KAAK;KACrB,mBAAmB;KAEnB;EACV;EACA;EACA;EACA;;KAEU,wBAAwB,KAClC;KAGU,2BAA2B;KAE3B;EAAa,aAAa;;KAC1B;EACV,WAAW,SAAS,iBAAiB;EACrC,cAAc,SAAS,oBAAoB,QAAQ;EACnD,sBACE,SAAS,4BACN;EACL,iBAAiB,SAAS,uBAAuB;;KAEvC;EACV;EACA;EACA;;KAGU,eAAe,KAAK;cAQnB,uBACX,mBACA;cAcW,gBACX,kBACC;cAyCU,oBAAqB,kBAAgB;cAqBrC,uBACX,kBACC;cAsBU,+BACX,kBACC;cAqBU,0BACX,kBACC;cAgGU,aAAY,gBAAA,UAGtB;cAuEU,gBACX,oBACA,aAAa,UAAU,kCACvB,UAAS,0BACT,uBACC,QAAQ;cAwLE,iBACX,mBACA,UAAS,iBACR;cAgBU,eACX,oBACA,UAAU,yBACV;EAAW;EAAuB;MACjC;cAsBU,kBACX,mBACA,SAAS,kBACT,UAAS,kBACR,QAAQ;cA+BE,YACX,mBACA,SAAS,YACT,UAAS,kBACR,QAAQ;cAqDE,eACX,mBACA,SAAS,eACT,UAAS,kBACR,QAAQ;cAQE,uBACX,mBACA,SAAS,uBACT,UAAS,kBACR,QAAQ;cAQE,mBAA0B,uBAAqB"}
@@ -0,0 +1,2 @@
1
+ import { A as probePiSession, C as ensureRuntimeDir, D as parseSetModelRequest, E as parseEnvelope, F as sendUserMessage, I as socketPathForSession, L as startReceiver, M as sendEnvelope, N as sendSetModel, O as parseSetThinkingLevelRequest, P as sendSetThinkingLevel, S as UserMessageResponse, T as parseAbortRequest, _ as SetThinkingLevelResponse, a as DEFAULT_OVERMUX_PI_RUNTIME_DIR, b as UserMessageInput, c as ProbeOptions, d as SenderOptions, f as SetModelInput, g as SetThinkingLevelRequest, h as SetThinkingLevelInput, i as CHILD_ENV, j as sendAbort, k as parseUserMessageRequest, l as Receiver, m as SetModelResponse, n as AbortRequest, o as DelegateSettledEnvelope, p as SetModelRequest, r as AbortResponse, s as PARENT_SESSION_ENV, t as AbortInput, u as ReceiverOptions, v as TASK_SLUG_ENV, w as eventKey, x as UserMessageRequest, y as TASK_SLUG_PATTERN } from "./protocol-CsrnSPOv.js";
2
+ export { AbortInput, AbortRequest, AbortResponse, CHILD_ENV, DEFAULT_OVERMUX_PI_RUNTIME_DIR, DelegateSettledEnvelope, PARENT_SESSION_ENV, ProbeOptions, Receiver, ReceiverOptions, SenderOptions, SetModelInput, SetModelRequest, SetModelResponse, SetThinkingLevelInput, SetThinkingLevelRequest, SetThinkingLevelResponse, TASK_SLUG_ENV, TASK_SLUG_PATTERN, UserMessageInput, UserMessageRequest, UserMessageResponse, ensureRuntimeDir, eventKey, parseAbortRequest, parseEnvelope, parseSetModelRequest, parseSetThinkingLevelRequest, parseUserMessageRequest, probePiSession, sendAbort, sendEnvelope, sendSetModel, sendSetThinkingLevel, sendUserMessage, socketPathForSession, startReceiver };
@@ -0,0 +1,365 @@
1
+ import { lstat, mkdir, unlink } from "node:fs/promises";
2
+ import { createConnection, createServer } from "node:net";
3
+ import { join } from "node:path";
4
+ //#region src/protocol.ts
5
+ const CHILD_ENV = "PI_CHILD";
6
+ const PARENT_SESSION_ENV = "PI_PARENT_SESSION_ID";
7
+ const TASK_SLUG_ENV = "PI_TASK_SLUG";
8
+ const MAX_MESSAGE_BYTES = 4096;
9
+ const MAX_USER_MESSAGE_BYTES = 3072;
10
+ const ACK = "ACK";
11
+ const MAX_RESPONSE_BYTES = 512;
12
+ const SOCKET_PATH_LIMIT = 100;
13
+ const ACK_TIMEOUT_MS = 400;
14
+ const PROBE_TIMEOUT_MS = 150;
15
+ const INBOUND_TIMEOUT_MS = 1e3;
16
+ const MAX_CONNECTIONS = 32;
17
+ const RETRY_DELAYS_MS = [
18
+ 0,
19
+ 80,
20
+ 160
21
+ ];
22
+ const ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
23
+ const TASK_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,11}[a-z0-9])?$/;
24
+ const DEFAULT_OVERMUX_PI_RUNTIME_DIR = `/tmp/overmux-pi-${process.getuid?.() ?? "u"}`;
25
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26
+ const isId = (value) => typeof value === "string" && ID_PATTERN.test(value);
27
+ const socketPathForSession = (sessionId, runtimeDir = DEFAULT_OVERMUX_PI_RUNTIME_DIR) => {
28
+ if (!isId(sessionId)) throw new Error("Invalid Pi session ID for delegate notification socket");
29
+ const socketPath = join(runtimeDir, `${sessionId}.sock`);
30
+ if (Buffer.byteLength(socketPath) > SOCKET_PATH_LIMIT) throw new Error(`Delegate notification socket path exceeds ${SOCKET_PATH_LIMIT} bytes`);
31
+ return socketPath;
32
+ };
33
+ const parseEnvelope = (input) => {
34
+ if (Buffer.byteLength(input) > MAX_MESSAGE_BYTES) return;
35
+ try {
36
+ const value = JSON.parse(input);
37
+ if (!isRecord(value)) return;
38
+ if (Object.keys(value).length !== 5 || !isId(value.childSessionId) || !isId(value.leafId)) return;
39
+ if (typeof value.taskSlug !== "string" || !TASK_SLUG_PATTERN.test(value.taskSlug)) return;
40
+ if (typeof value.cwd !== "string" || value.cwd.length < 1 || value.cwd.length > 2048) return;
41
+ if (!Number.isSafeInteger(value.timestamp) || value.timestamp < 0) return;
42
+ return value;
43
+ } catch {
44
+ return;
45
+ }
46
+ };
47
+ const parseAbortRequest = (input) => {
48
+ if (Buffer.byteLength(input) > MAX_MESSAGE_BYTES) return;
49
+ try {
50
+ const value = JSON.parse(input);
51
+ if (!isRecord(value) || Object.keys(value).length !== 3 || value.version !== 1 || value.type !== "abort" || !isId(value.requestId)) return;
52
+ return value;
53
+ } catch {
54
+ return;
55
+ }
56
+ };
57
+ const parseSetModelRequest = (input) => {
58
+ try {
59
+ const value = JSON.parse(input);
60
+ if (!isRecord(value) || Object.keys(value).length !== 5 || value.version !== 1 || value.type !== "set_model" || !isId(value.requestId) || typeof value.provider !== "string" || !value.provider || typeof value.id !== "string" || !value.id) return;
61
+ return value;
62
+ } catch {
63
+ return;
64
+ }
65
+ };
66
+ const parseSetThinkingLevelRequest = (input) => {
67
+ try {
68
+ const value = JSON.parse(input);
69
+ if (!isRecord(value) || Object.keys(value).length !== 4 || value.version !== 1 || value.type !== "set_thinking_level" || !isId(value.requestId) || typeof value.level !== "string" || ![
70
+ "off",
71
+ "minimal",
72
+ "low",
73
+ "medium",
74
+ "high",
75
+ "xhigh",
76
+ "max"
77
+ ].includes(value.level)) return;
78
+ return value;
79
+ } catch {
80
+ return;
81
+ }
82
+ };
83
+ const parseUserMessageRequest = (input) => {
84
+ if (Buffer.byteLength(input) > MAX_MESSAGE_BYTES) return;
85
+ try {
86
+ const value = JSON.parse(input);
87
+ if (!isRecord(value)) return;
88
+ const keys = Object.keys(value);
89
+ const allowedKeys = /* @__PURE__ */ new Set([
90
+ "version",
91
+ "requestId",
92
+ "type",
93
+ "message",
94
+ "deliverAs",
95
+ "expandPromptTemplates"
96
+ ]);
97
+ if (keys.length < 5 || keys.length > 6 || keys.some((key) => !allowedKeys.has(key))) return;
98
+ if (value.version !== 1 || value.type !== "user_message" || !isId(value.requestId)) return;
99
+ if (typeof value.message !== "string" || !value.message.trim()) return;
100
+ if (Buffer.byteLength(value.message) > MAX_USER_MESSAGE_BYTES) return;
101
+ if (value.deliverAs !== "steer" && value.deliverAs !== "followUp") return;
102
+ if (value.expandPromptTemplates !== void 0 && typeof value.expandPromptTemplates !== "boolean") return;
103
+ return {
104
+ ...value,
105
+ expandPromptTemplates: value.expandPromptTemplates ?? true
106
+ };
107
+ } catch {
108
+ return;
109
+ }
110
+ };
111
+ const parseUserMessageResponse = (input, requestId) => {
112
+ try {
113
+ const value = JSON.parse(input);
114
+ if (!isRecord(value) || value.version !== 1 || value.requestId !== requestId) return;
115
+ const keys = Object.keys(value);
116
+ const successKeys = [
117
+ "version",
118
+ "requestId",
119
+ "ok",
120
+ "delivery"
121
+ ];
122
+ if (value.ok === true && keys.length === successKeys.length && keys.every((key) => successKeys.includes(key)) && (value.delivery === "immediate" || value.delivery === "steer" || value.delivery === "followUp")) return value;
123
+ const errorKeys = [
124
+ "version",
125
+ "requestId",
126
+ "ok",
127
+ "error"
128
+ ];
129
+ if (value.ok === false && keys.length === errorKeys.length && keys.every((key) => errorKeys.includes(key)) && (value.error === "shutting_down" || value.error === "unavailable")) return value;
130
+ return;
131
+ } catch {
132
+ return;
133
+ }
134
+ };
135
+ const eventKey = ({ childSessionId, leafId }) => `${childSessionId}\0${leafId}`;
136
+ const responsePayload = (response) => JSON.stringify(response);
137
+ const parseControlResponse = (input, requestId) => {
138
+ try {
139
+ const value = JSON.parse(input);
140
+ if (!isRecord(value) || value.version !== 1 || value.requestId !== requestId) return;
141
+ if (value.ok === true && Object.keys(value).length === 3) return value;
142
+ if (value.ok === false && Object.keys(value).length === 4 && (value.error === "no_key" || value.error === "not_found" || value.error === "shutting_down" || value.error === "unavailable")) return value;
143
+ return;
144
+ } catch {
145
+ return;
146
+ }
147
+ };
148
+ const parseAbortResponse = (input, requestId) => {
149
+ try {
150
+ const value = JSON.parse(input);
151
+ if (!isRecord(value) || value.version !== 1 || value.requestId !== requestId) return;
152
+ if (value.ok === true && Object.keys(value).length === 3) return value;
153
+ if (value.ok === false && Object.keys(value).length === 4 && (value.error === "shutting_down" || value.error === "unavailable")) return value;
154
+ return;
155
+ } catch {
156
+ return;
157
+ }
158
+ };
159
+ const startReceiver = async (socketPath, onEnvelope, options = {}, timeoutMs = INBOUND_TIMEOUT_MS) => {
160
+ const receiverOptions = typeof options === "number" ? {} : options;
161
+ const resolvedTimeoutMs = typeof options === "number" ? options : timeoutMs;
162
+ await removeStaleSocket(socketPath);
163
+ const sockets = /* @__PURE__ */ new Set();
164
+ const server = createServer({ allowHalfOpen: true }, (socket) => {
165
+ sockets.add(socket);
166
+ socket.setEncoding("utf8");
167
+ socket.setTimeout(resolvedTimeoutMs, () => socket.destroy());
168
+ let input = "";
169
+ socket.on("data", (chunk) => {
170
+ input += chunk;
171
+ if (Buffer.byteLength(input) > MAX_MESSAGE_BYTES) socket.destroy();
172
+ });
173
+ socket.on("end", () => {
174
+ const envelope = parseEnvelope(input);
175
+ if (envelope) {
176
+ try {
177
+ onEnvelope(envelope);
178
+ socket.end(ACK);
179
+ } catch {
180
+ socket.destroy();
181
+ }
182
+ return;
183
+ }
184
+ const setModelRequest = parseSetModelRequest(input);
185
+ if (setModelRequest && receiverOptions.onSetModel) {
186
+ receiverOptions.onSetModel(setModelRequest).then((response) => socket.end(responsePayload(response))).catch(() => socket.destroy());
187
+ return;
188
+ }
189
+ const setThinkingLevelRequest = parseSetThinkingLevelRequest(input);
190
+ if (setThinkingLevelRequest && receiverOptions.onSetThinkingLevel) {
191
+ try {
192
+ socket.end(responsePayload(receiverOptions.onSetThinkingLevel(setThinkingLevelRequest)));
193
+ } catch {
194
+ socket.destroy();
195
+ }
196
+ return;
197
+ }
198
+ const abortRequest = parseAbortRequest(input);
199
+ if (abortRequest && receiverOptions.onAbort) {
200
+ try {
201
+ socket.end(responsePayload(receiverOptions.onAbort(abortRequest)));
202
+ } catch {
203
+ socket.destroy();
204
+ }
205
+ return;
206
+ }
207
+ const userMessageRequest = parseUserMessageRequest(input);
208
+ if (!userMessageRequest || !receiverOptions.onUserMessage) {
209
+ socket.destroy();
210
+ return;
211
+ }
212
+ try {
213
+ socket.end(responsePayload(receiverOptions.onUserMessage(userMessageRequest)));
214
+ } catch {
215
+ socket.destroy();
216
+ }
217
+ });
218
+ socket.on("close", () => sockets.delete(socket));
219
+ socket.on("error", () => void 0);
220
+ });
221
+ server.maxConnections = MAX_CONNECTIONS;
222
+ await listen(server, socketPath);
223
+ let closed = false;
224
+ return { close: async () => {
225
+ if (closed) return;
226
+ closed = true;
227
+ sockets.forEach((socket) => socket.destroy());
228
+ await closeServer(server);
229
+ await unlink(socketPath).catch((error) => {
230
+ if (error.code !== "ENOENT") throw error;
231
+ });
232
+ } };
233
+ };
234
+ const probeSocket = (socketPath) => new Promise((resolve) => {
235
+ const socket = createConnection(socketPath);
236
+ const finish = (result) => {
237
+ socket.destroy();
238
+ resolve(result);
239
+ };
240
+ socket.setTimeout(100, () => finish("active"));
241
+ socket.on("connect", () => finish("active"));
242
+ socket.on("error", (error) => finish(error.code === "ECONNREFUSED" || error.code === "ENOENT" ? "stale" : "active"));
243
+ });
244
+ const removeStaleSocket = async (socketPath) => {
245
+ const stats = await lstat(socketPath).catch((error) => {
246
+ if (error.code === "ENOENT") return;
247
+ throw error;
248
+ });
249
+ if (!stats) return;
250
+ if (!stats.isSocket()) throw new Error(`Delegate notification path is not a socket: ${socketPath}`);
251
+ if (await probeSocket(socketPath) === "active") throw new Error(`Delegate notification socket is already active: ${socketPath}`);
252
+ await unlink(socketPath);
253
+ };
254
+ const listen = (server, socketPath) => new Promise((resolve, reject) => {
255
+ const onError = (error) => reject(error);
256
+ server.once("error", onError);
257
+ server.listen(socketPath, () => {
258
+ server.off("error", onError);
259
+ resolve();
260
+ });
261
+ });
262
+ const closeServer = (server) => new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
263
+ const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
264
+ const sendAttempt = (socketPath, payload, timeoutMs) => new Promise((resolve) => {
265
+ const socket = createConnection(socketPath);
266
+ socket.setEncoding("utf8");
267
+ let response = "";
268
+ let finished = false;
269
+ const finish = (result) => {
270
+ if (finished) return;
271
+ finished = true;
272
+ socket.destroy();
273
+ resolve(result);
274
+ };
275
+ socket.setTimeout(timeoutMs, () => finish(void 0));
276
+ socket.on("connect", () => socket.end(payload));
277
+ socket.on("data", (chunk) => {
278
+ response += chunk;
279
+ if (Buffer.byteLength(response) > MAX_RESPONSE_BYTES) finish(void 0);
280
+ });
281
+ socket.on("end", () => finish(response));
282
+ socket.on("error", () => finish(void 0));
283
+ });
284
+ const probePiSession = async (sessionId, options = {}) => {
285
+ let socketPath;
286
+ try {
287
+ socketPath = socketPathForSession(sessionId, options.runtimeDir);
288
+ } catch {
289
+ return false;
290
+ }
291
+ return await sendAttempt(socketPath, "", options.timeoutMs ?? PROBE_TIMEOUT_MS) !== void 0;
292
+ };
293
+ const sendEnvelope = async (socketPath, envelope, options = {}) => {
294
+ const payload = JSON.stringify(envelope);
295
+ if (!parseEnvelope(payload)) return false;
296
+ for (const delay of options.retryDelaysMs ?? RETRY_DELAYS_MS) {
297
+ if (delay > 0) await wait(delay);
298
+ if (await sendAttempt(socketPath, payload, options.ackTimeoutMs ?? ACK_TIMEOUT_MS) === ACK) return true;
299
+ }
300
+ return false;
301
+ };
302
+ const sendUserMessage = async (sessionId, request, options = {}) => {
303
+ const payload = JSON.stringify({
304
+ version: 1,
305
+ type: "user_message",
306
+ ...request
307
+ });
308
+ const parsed = parseUserMessageRequest(payload);
309
+ if (!parsed) return;
310
+ const socketPath = socketPathForSession(sessionId, options.runtimeDir);
311
+ for (const delay of options.retryDelaysMs ?? RETRY_DELAYS_MS) {
312
+ if (delay > 0) await wait(delay);
313
+ const response = await sendAttempt(socketPath, payload, options.timeoutMs ?? ACK_TIMEOUT_MS);
314
+ const parsedResponse = response ? parseUserMessageResponse(response, parsed.requestId) : void 0;
315
+ if (parsedResponse) return parsedResponse;
316
+ }
317
+ };
318
+ const sendAbort = async (sessionId, request, options = {}) => {
319
+ const payload = JSON.stringify({
320
+ version: 1,
321
+ type: "abort",
322
+ ...request
323
+ });
324
+ const parsed = parseAbortRequest(payload);
325
+ if (!parsed) return;
326
+ const socketPath = socketPathForSession(sessionId, options.runtimeDir);
327
+ for (const delay of options.retryDelaysMs ?? RETRY_DELAYS_MS) {
328
+ if (delay > 0) await wait(delay);
329
+ const response = await sendAttempt(socketPath, payload, options.timeoutMs ?? ACK_TIMEOUT_MS);
330
+ const parsedResponse = response ? parseAbortResponse(response, parsed.requestId) : void 0;
331
+ if (parsedResponse) return parsedResponse;
332
+ }
333
+ };
334
+ const sendControl = async (sessionId, payload, request, options) => {
335
+ const socketPath = socketPathForSession(sessionId, options.runtimeDir);
336
+ for (const delay of options.retryDelaysMs ?? RETRY_DELAYS_MS) {
337
+ if (delay > 0) await wait(delay);
338
+ const response = await sendAttempt(socketPath, JSON.stringify(payload), options.timeoutMs ?? ACK_TIMEOUT_MS);
339
+ const parsed = response ? parseControlResponse(response, request.requestId) : void 0;
340
+ if (parsed) return parsed;
341
+ }
342
+ };
343
+ const sendSetModel = (sessionId, request, options = {}) => sendControl(sessionId, {
344
+ version: 1,
345
+ type: "set_model",
346
+ ...request
347
+ }, request, options);
348
+ const sendSetThinkingLevel = (sessionId, request, options = {}) => sendControl(sessionId, {
349
+ version: 1,
350
+ type: "set_thinking_level",
351
+ ...request
352
+ }, request, options);
353
+ const ensureRuntimeDir = async (runtimeDir) => {
354
+ await mkdir(runtimeDir, { mode: 448 }).catch((error) => {
355
+ if (error.code !== "EEXIST") throw error;
356
+ });
357
+ const stats = await lstat(runtimeDir);
358
+ const expectedUid = process.getuid?.();
359
+ if (!stats.isDirectory() || expectedUid !== void 0 && stats.uid !== expectedUid) throw new Error(`Delegate notification runtime directory is not private: ${runtimeDir}`);
360
+ if ((stats.mode & 511) !== 448) throw new Error(`Delegate notification runtime directory must have mode 0700: ${runtimeDir}`);
361
+ };
362
+ //#endregion
363
+ export { CHILD_ENV, DEFAULT_OVERMUX_PI_RUNTIME_DIR, PARENT_SESSION_ENV, TASK_SLUG_ENV, TASK_SLUG_PATTERN, ensureRuntimeDir, eventKey, parseAbortRequest, parseEnvelope, parseSetModelRequest, parseSetThinkingLevelRequest, parseUserMessageRequest, probePiSession, sendAbort, sendEnvelope, sendSetModel, sendSetThinkingLevel, sendUserMessage, socketPathForSession, startReceiver };
364
+
365
+ //# sourceMappingURL=protocol.js.map