@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.
- package/README.md +118 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-2jNv4tol.d.ts +34 -0
- package/dist/config-2jNv4tol.d.ts.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +47 -0
- package/dist/config.js.map +1 -0
- package/dist/extension.d.ts +19 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +265 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonl-tail.d.ts +13 -0
- package/dist/jsonl-tail.d.ts.map +1 -0
- package/dist/jsonl-tail.js +83 -0
- package/dist/jsonl-tail.js.map +1 -0
- package/dist/live-events-ClhFOMGW.js +497 -0
- package/dist/live-events-ClhFOMGW.js.map +1 -0
- package/dist/live-events-DAmf6RRx.d.ts +107 -0
- package/dist/live-events-DAmf6RRx.d.ts.map +1 -0
- package/dist/live-events.d.ts +2 -0
- package/dist/live-events.js +2 -0
- package/dist/notification-DzOd9cRc.d.ts +20 -0
- package/dist/notification-DzOd9cRc.d.ts.map +1 -0
- package/dist/notification.d.ts +2 -0
- package/dist/notification.js +54 -0
- package/dist/notification.js.map +1 -0
- package/dist/plugin.d.ts +90 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +213 -0
- package/dist/plugin.js.map +1 -0
- package/dist/projection.d.ts +101 -0
- package/dist/projection.d.ts.map +1 -0
- package/dist/projection.js +550 -0
- package/dist/projection.js.map +1 -0
- package/dist/protocol-CsrnSPOv.d.ts +115 -0
- package/dist/protocol-CsrnSPOv.d.ts.map +1 -0
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.js +365 -0
- package/dist/protocol.js.map +1 -0
- package/dist/react.d.ts +139 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +796 -0
- package/dist/react.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +4 -0
- package/dist/session-status-CZLTo8Km.d.ts +71 -0
- package/dist/session-status-CZLTo8Km.d.ts.map +1 -0
- package/dist/styles.css +619 -0
- package/docs/index.md +10 -0
- package/package.json +115 -0
- package/src/cli.ts +59 -0
- package/src/config.ts +87 -0
- package/src/extension.ts +486 -0
- package/src/index.ts +14 -0
- package/src/jsonl-tail.ts +110 -0
- package/src/live-events.ts +578 -0
- package/src/notification.ts +109 -0
- package/src/plugin.ts +369 -0
- package/src/projection.ts +995 -0
- package/src/protocol.ts +805 -0
- package/src/react.tsx +1293 -0
- package/src/server.ts +15 -0
- package/src/session-status.ts +379 -0
- package/src/styles.css +619 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport {\n defineOperation,\n type HandlerContext,\n type StreamHandlerDefinition,\n} from \"overmux\";\nimport {\n defineResourceContract,\n defineStreamContract,\n noInputSchema,\n} from \"overmux\";\nimport { z } from \"zod\";\n\nimport {\n sendAbort,\n sendSetModel,\n sendSetThinkingLevel,\n sendUserMessage,\n type AbortResponse,\n type UserMessageResponse,\n} from \"./protocol.js\";\nimport {\n createPiAgentConversationService,\n piConversationSnapshotSchema,\n piSessionMetadataSchema,\n type PiAgentSession,\n type PiConversationSnapshot,\n} from \"./projection.js\";\n\ntype Awaitable<T> = T | Promise<T>;\ntype Dispose = () => void;\nexport type PiSessionSource = {\n list: () => Awaitable<readonly PiAgentSession[]>;\n subscribe: (\n invalidate: () => void,\n options: { signal: AbortSignal },\n ) => Dispose | void;\n};\ntype PiAgentListener = () => void;\n\nexport type PiAgents = {\n get: (agentId: string) => Promise<PiAgentSession | undefined>;\n list: () => Promise<readonly PiAgentSession[]>;\n subscribe: (listener: PiAgentListener) => Dispose;\n};\n\nconst piAgentSessionSchema = z\n .object({\n id: z.string().min(1),\n liveEventsDir: z.string().min(1).optional(),\n sessionFile: z.string().min(1),\n sessionMetadata: piSessionMetadataSchema.optional(),\n })\n .strict();\n\nconst piSessionSchema = z.object({ agentId: z.string().min(1) }).strict();\nconst piMessageInputSchema = z\n .object({\n agentId: z.string().min(1),\n deliverAs: z.enum([\"steer\", \"followUp\"]),\n message: z.string().trim().min(1),\n })\n .strict();\nconst piMessageResultSchema = z.object({\n delivery: z.enum([\"immediate\", \"steer\", \"followUp\"]),\n requestId: z.string().min(1),\n});\nconst piStopInputSchema = z.object({ agentId: z.string().min(1) }).strict();\nconst piStopResultSchema = z.object({ requestId: z.string().min(1) });\nconst piModelInputSchema = z\n .object({\n agentId: z.string().min(1),\n provider: z.string().min(1),\n id: z.string().min(1),\n })\n .strict();\nconst piThinkingInputSchema = z\n .object({\n agentId: z.string().min(1),\n level: z.enum([\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]),\n })\n .strict();\nconst piControlResultSchema = z.object({ requestId: z.string().min(1) });\n\nconst once = (dispose: Dispose): Dispose => {\n let disposed = false;\n return () => {\n if (disposed) {\n return;\n }\n disposed = true;\n dispose();\n };\n};\n\nconst sameSessions = (\n left: readonly PiAgentSession[],\n right: readonly PiAgentSession[],\n): boolean =>\n left.length === right.length &&\n left.every(\n (session, index) =>\n session.id === right[index]?.id &&\n session.sessionFile === right[index]?.sessionFile &&\n session.liveEventsDir === right[index]?.liveEventsDir &&\n JSON.stringify(session.sessionMetadata) ===\n JSON.stringify(right[index]?.sessionMetadata),\n );\n\nexport const definePiAgents = ({\n liveEventsDir,\n sessions,\n}: {\n liveEventsDir: string;\n sessions: PiSessionSource;\n}): PiAgents => {\n const listeners = new Set<PiAgentListener>();\n let cached: PiAgentSession[] = [];\n let sourceDispose: Dispose | undefined;\n let refreshQueue = Promise.resolve();\n\n const refresh = async () => {\n const result = refreshQueue.then(async () => {\n const next = (await sessions.list())\n .map((session) =>\n piAgentSessionSchema.parse({ ...session, liveEventsDir }),\n )\n .sort((left, right) => left.id.localeCompare(right.id));\n if (new Set(next.map(({ id }) => id)).size !== next.length) {\n throw new Error(\"Pi agent discovery returned duplicate IDs\");\n }\n if (!sameSessions(cached, next)) {\n cached = next;\n listeners.forEach((listener) => listener());\n }\n });\n refreshQueue = result.catch(() => undefined);\n await result;\n return cached;\n };\n\n const stopSource = () => {\n sourceDispose?.();\n sourceDispose = undefined;\n };\n\n const startSource = () => {\n if (sourceDispose) {\n return;\n }\n const controller = new AbortController();\n const dispose = sessions.subscribe(\n () => void refresh().catch(() => undefined),\n { signal: controller.signal },\n );\n sourceDispose = once(() => {\n controller.abort();\n dispose?.();\n });\n };\n\n return {\n get: async (agentId) => (await refresh()).find(({ id }) => id === agentId),\n list: refresh,\n subscribe: (listener) => {\n listeners.add(listener);\n startSource();\n return () => {\n listeners.delete(listener);\n if (!listeners.size) {\n stopSource();\n }\n };\n },\n };\n};\n\nconst piSessionsContract = defineResourceContract({\n input: noInputSchema,\n output: z.array(piSessionSchema),\n});\n\nexport const piSessionsResource = ({ agents }: { agents: PiAgents }) => ({\n contract: piSessionsContract,\n kind: \"subscription\" as const,\n read: async (_input: void, _context: HandlerContext) =>\n (await agents.list()).map(({ id }) => ({ agentId: id })),\n subscribe: (\n _input: void,\n invalidate: () => void,\n context: HandlerContext,\n ) => {\n const dispose = once(agents.subscribe(invalidate));\n context.signal.addEventListener(\"abort\", dispose, { once: true });\n return once(() => {\n context.signal.removeEventListener(\"abort\", dispose);\n dispose();\n });\n },\n});\n\nconst piConversationContract = defineStreamContract({\n clientMessage: z.never(),\n input: z.object({ agentId: z.string().min(1) }).strict(),\n serverMessage: piConversationSnapshotSchema,\n});\n\nconst limitConversationEntries = (\n snapshot: PiConversationSnapshot,\n maxEntries: number | undefined,\n): PiConversationSnapshot =>\n maxEntries === undefined\n ? snapshot\n : { ...snapshot, entries: snapshot.entries.slice(-maxEntries) };\n\nexport const piConversationStream = ({\n agents,\n maxEntries,\n}: {\n agents: PiAgents;\n maxEntries?: number;\n}): StreamHandlerDefinition<\n typeof piConversationContract.input,\n typeof piConversationContract.clientMessage,\n typeof piConversationContract.serverMessage\n> => {\n if (\n maxEntries !== undefined &&\n (!Number.isSafeInteger(maxEntries) || maxEntries < 1)\n ) {\n throw new Error(\"Pi conversation maxEntries must be a positive integer\");\n }\n return {\n contract: piConversationContract,\n open: async ({ agentId }, context) => {\n const service = createPiAgentConversationService({\n resolveAgent: async (id) => {\n const agent = await agents.get(id);\n if (!agent) {\n throw new Error(`Pi agent not found: ${id}`);\n }\n return { ...agent, sessionId: agent.id };\n },\n });\n try {\n context.emit(\n limitConversationEntries(\n await service.getSnapshot(agentId),\n maxEntries,\n ),\n );\n const unsubscribe = service.subscribe(agentId, (snapshot) =>\n context.emit(limitConversationEntries(snapshot, maxEntries)),\n );\n return {\n dispose: once(() => {\n unsubscribe();\n service.dispose();\n }),\n };\n } catch (error) {\n service.dispose();\n throw error;\n }\n },\n };\n};\n\nconst piMessageResult = async (\n agent: PiAgentSession | undefined,\n input: z.infer<typeof piMessageInputSchema>,\n): Promise<z.infer<typeof piMessageResultSchema>> => {\n if (!agent) {\n throw new Error(`Pi agent not found: ${input.agentId}`);\n }\n const response = await sendUserMessage(agent.id, {\n deliverAs: input.deliverAs,\n message: input.message,\n requestId: randomUUID(),\n });\n if (!response?.ok) {\n throw new Error(\"Pi agent is unavailable\");\n }\n return response;\n};\n\nconst piStopResult = async (\n agent: PiAgentSession | undefined,\n input: z.infer<typeof piStopInputSchema>,\n): Promise<z.infer<typeof piStopResultSchema>> => {\n if (!agent) {\n throw new Error(`Pi agent not found: ${input.agentId}`);\n }\n const requestId = randomUUID();\n const response = await sendAbort(agent.id, { requestId });\n if (!response?.ok) {\n throw new Error(\"Pi agent is unavailable\");\n }\n return { requestId };\n};\n\nconst piControlResult = async (\n agent: PiAgentSession | undefined,\n input: { agentId: string },\n send: (\n agentId: string,\n requestId: string,\n ) => Promise<{ ok: boolean; error?: string } | undefined>,\n): Promise<z.infer<typeof piControlResultSchema>> => {\n if (!agent) {\n throw new Error(`Pi agent not found: ${input.agentId}`);\n }\n const requestId = randomUUID();\n const response = await send(agent.id, requestId);\n if (response?.ok) {\n return { requestId };\n }\n if (response?.error === \"not_found\") {\n throw new Error(\"Pi model was not found\");\n }\n if (response?.error === \"no_key\") {\n throw new Error(\"No API key is available for this Pi model\");\n }\n throw new Error(\"Pi agent is unavailable\");\n};\n\nexport const piOperationHandlers = ({ agents }: { agents: PiAgents }) => ({\n sendPiMessage: defineOperation({\n handle: async (input) =>\n piMessageResult(await agents.get(input.agentId), input),\n input: piMessageInputSchema,\n output: piMessageResultSchema,\n }),\n stopPiAgent: defineOperation({\n handle: async (input) =>\n piStopResult(await agents.get(input.agentId), input),\n input: piStopInputSchema,\n output: piStopResultSchema,\n }),\n setPiModel: defineOperation({\n handle: async (input) =>\n piControlResult(\n await agents.get(input.agentId),\n input,\n (agentId, requestId) =>\n sendSetModel(agentId, {\n id: input.id,\n provider: input.provider,\n requestId,\n }),\n ),\n input: piModelInputSchema,\n output: piControlResultSchema,\n }),\n setPiThinkingLevel: defineOperation({\n handle: async (input) =>\n piControlResult(\n await agents.get(input.agentId),\n input,\n (agentId, requestId) =>\n sendSetThinkingLevel(agentId, { level: input.level, requestId }),\n ),\n input: piThinkingInputSchema,\n output: piControlResultSchema,\n }),\n});\n\nexport type { AbortResponse, UserMessageResponse };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { S as UserMessageResponse, b as UserMessageInput, c as ProbeOptions } from "./protocol-CsrnSPOv.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/projection.d.ts
|
|
4
|
+
type PiContentBlock = {
|
|
5
|
+
arguments?: unknown;
|
|
6
|
+
data?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
mimeType?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
text?: string;
|
|
11
|
+
thinking?: string;
|
|
12
|
+
tool?: PiToolProjection;
|
|
13
|
+
type: string;
|
|
14
|
+
};
|
|
15
|
+
type PiToolResultProjection = {
|
|
16
|
+
content: PiContentBlock[];
|
|
17
|
+
details?: unknown;
|
|
18
|
+
isError: boolean;
|
|
19
|
+
toolCallId: string;
|
|
20
|
+
toolName: string;
|
|
21
|
+
};
|
|
22
|
+
type PiToolProjection = {
|
|
23
|
+
result?: PiToolResultProjection;
|
|
24
|
+
status: "error" | "pending" | "success";
|
|
25
|
+
};
|
|
26
|
+
type PiConversationEntry = {
|
|
27
|
+
content: PiContentBlock[];
|
|
28
|
+
details?: unknown;
|
|
29
|
+
errorMessage?: string;
|
|
30
|
+
id: string;
|
|
31
|
+
isError?: boolean;
|
|
32
|
+
role: "assistant" | "toolResult" | "user";
|
|
33
|
+
source: "canonical" | "live";
|
|
34
|
+
status: "complete" | "error" | "pending";
|
|
35
|
+
stopReason?: string;
|
|
36
|
+
timestamp?: number;
|
|
37
|
+
toolCallId?: string;
|
|
38
|
+
toolName?: string;
|
|
39
|
+
};
|
|
40
|
+
declare const piContentBlockSchema: z.ZodType<PiContentBlock>;
|
|
41
|
+
declare const piToolResultProjectionSchema: z.ZodType<PiToolResultProjection>;
|
|
42
|
+
declare const piToolProjectionSchema: z.ZodType<PiToolProjection>;
|
|
43
|
+
declare const piSessionMetadataSchema: z.ZodObject<{
|
|
44
|
+
contextUsage: z.ZodOptional<z.ZodObject<{
|
|
45
|
+
tokens: z.ZodNumber;
|
|
46
|
+
contextWindow: z.ZodNumber;
|
|
47
|
+
percent: z.ZodNumber;
|
|
48
|
+
}, z.core.$strict>>;
|
|
49
|
+
model: z.ZodOptional<z.ZodObject<{
|
|
50
|
+
provider: z.ZodString;
|
|
51
|
+
id: z.ZodString;
|
|
52
|
+
name: z.ZodString;
|
|
53
|
+
}, z.core.$strict>>;
|
|
54
|
+
thinkingLevel: z.ZodString;
|
|
55
|
+
modelOptions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
56
|
+
provider: z.ZodString;
|
|
57
|
+
id: z.ZodString;
|
|
58
|
+
name: z.ZodString;
|
|
59
|
+
}, z.core.$strict>>>;
|
|
60
|
+
}, z.core.$strict>;
|
|
61
|
+
type PiSessionMetadata = z.infer<typeof piSessionMetadataSchema>;
|
|
62
|
+
type PiConversationSnapshot = {
|
|
63
|
+
agentAvailable: boolean;
|
|
64
|
+
entries: PiConversationEntry[];
|
|
65
|
+
sessionMetadata?: PiSessionMetadata;
|
|
66
|
+
status: "busy" | "degraded" | "idle" | "offline";
|
|
67
|
+
};
|
|
68
|
+
declare const piConversationSnapshotSchema: z.ZodType<PiConversationSnapshot>;
|
|
69
|
+
type PiAgentSession = {
|
|
70
|
+
id: string;
|
|
71
|
+
liveEventsDir?: string;
|
|
72
|
+
sessionFile: string;
|
|
73
|
+
sessionMetadata?: PiSessionMetadata;
|
|
74
|
+
};
|
|
75
|
+
type PiAgentResource = PiAgentSession & {
|
|
76
|
+
sessionId: string;
|
|
77
|
+
};
|
|
78
|
+
type SnapshotListener = (snapshot: PiConversationSnapshot) => void;
|
|
79
|
+
type PiAgentConversationService = {
|
|
80
|
+
dispose: () => void;
|
|
81
|
+
getSnapshot: (agentId: string) => Promise<PiConversationSnapshot>;
|
|
82
|
+
subscribe: (agentId: string, listener: SnapshotListener) => () => void;
|
|
83
|
+
};
|
|
84
|
+
type PiMessageResponse = Extract<UserMessageResponse, {
|
|
85
|
+
ok: true;
|
|
86
|
+
}>;
|
|
87
|
+
type PiMessageSender = (sessionId: string, request: UserMessageInput) => Promise<UserMessageResponse | undefined>;
|
|
88
|
+
declare const probePiAgent: (sessionId: string, options?: ProbeOptions) => Promise<boolean>;
|
|
89
|
+
declare const createPiAgentConversationService: ({ resolveAgent, pollIntervalMs, probe }: {
|
|
90
|
+
resolveAgent: (agentId: string) => Promise<PiAgentResource>;
|
|
91
|
+
pollIntervalMs?: number;
|
|
92
|
+
probe?: (sessionId: string) => Promise<boolean>;
|
|
93
|
+
}) => PiAgentConversationService;
|
|
94
|
+
declare const sendPiAgentMessage: ({ deliverAs, message, sessionId }: {
|
|
95
|
+
deliverAs: "followUp" | "steer";
|
|
96
|
+
message: string;
|
|
97
|
+
sessionId: string;
|
|
98
|
+
}, send?: PiMessageSender) => Promise<PiMessageResponse>;
|
|
99
|
+
//#endregion
|
|
100
|
+
export { PiAgentConversationService, PiAgentSession, PiContentBlock, PiConversationEntry, PiConversationSnapshot, PiMessageSender, PiSessionMetadata, PiToolProjection, PiToolResultProjection, createPiAgentConversationService, piContentBlockSchema, piConversationSnapshotSchema, piSessionMetadataSchema, piToolProjectionSchema, piToolResultProjectionSchema, probePiAgent, sendPiAgentMessage };
|
|
101
|
+
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","names":[],"sources":["../src/projection.ts"],"mappings":";;;KAiBY;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;;KAEU;EACV,SAAS;EACT;EACA;EACA;EACA;;KAEU;EACV,SAAS;EACT;;KAEU;EACV,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;cAGW,sBAAsB,EAAE,QAAQ;cAchC,8BAA8B,EAAE,QAAQ;cASxC,wBAAwB,EAAE,QAAQ;cAMlC,yBAAuB,EAAA;;;;;;;;;;;;;;;;;GAgCzB,EAAA,KAAA;KAEC,oBAAoB,EAAE,aAAa;KACnC;EACV;EACA,SAAS;EACT,kBAAkB;EAClB;;cAGW,8BAA8B,EAAE,QAAQ;KAuBzC;EACV;EACA;EACA;EACA,kBAAkB;;KAGf,kBAAkB;EAAmB;;KA+CrC,oBAAoB,UAAU;KAEvB;EACV;EACA,cAAc,oBAAoB,QAAQ;EAC1C,YAAY,iBAAiB,UAAU;;KAGpC,oBAAoB,QAAQ;EAAuB;;KAE5C,mBACV,mBACA,SAAS,qBACN,QAAQ;cA6nBA,eAAY,mBAAA,UAAZ,iBAAY;cAEZ,qCAAoC,cAAA,gBAAA;EAK/C,eAAe,oBAAoB,QAAQ;EAC3C;EACA,SAAS,sBAAsB;MAC7B;cA+GS,uBACX,WAAA,SAAA;EAKE;EACA;EACA;GAEF,OAAM,oBACL,QAAQ"}
|
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { probePiSession, sendUserMessage } from "./protocol.js";
|
|
2
|
+
import { o as parseLiveEventRecord } from "./live-events-ClhFOMGW.js";
|
|
3
|
+
import { createJsonlTail } from "./jsonl-tail.js";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { readdir } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
//#region src/projection.ts
|
|
9
|
+
const piContentBlockSchema = z.lazy(() => z.object({
|
|
10
|
+
arguments: z.unknown().optional(),
|
|
11
|
+
data: z.string().optional(),
|
|
12
|
+
id: z.string().optional(),
|
|
13
|
+
mimeType: z.string().optional(),
|
|
14
|
+
name: z.string().optional(),
|
|
15
|
+
text: z.string().optional(),
|
|
16
|
+
thinking: z.string().optional(),
|
|
17
|
+
tool: piToolProjectionSchema.optional(),
|
|
18
|
+
type: z.string()
|
|
19
|
+
}));
|
|
20
|
+
const piToolResultProjectionSchema = z.object({
|
|
21
|
+
content: z.array(piContentBlockSchema),
|
|
22
|
+
details: z.unknown().optional(),
|
|
23
|
+
isError: z.boolean(),
|
|
24
|
+
toolCallId: z.string(),
|
|
25
|
+
toolName: z.string()
|
|
26
|
+
});
|
|
27
|
+
const piToolProjectionSchema = z.lazy(() => z.object({
|
|
28
|
+
result: piToolResultProjectionSchema.optional(),
|
|
29
|
+
status: z.enum([
|
|
30
|
+
"error",
|
|
31
|
+
"pending",
|
|
32
|
+
"success"
|
|
33
|
+
])
|
|
34
|
+
}));
|
|
35
|
+
const piSessionMetadataSchema = z.object({
|
|
36
|
+
contextUsage: z.object({
|
|
37
|
+
tokens: z.number().nonnegative(),
|
|
38
|
+
contextWindow: z.number().positive(),
|
|
39
|
+
percent: z.number().nonnegative()
|
|
40
|
+
}).strict().optional(),
|
|
41
|
+
model: z.object({
|
|
42
|
+
provider: z.string().min(1),
|
|
43
|
+
id: z.string().min(1),
|
|
44
|
+
name: z.string().min(1)
|
|
45
|
+
}).strict().optional(),
|
|
46
|
+
thinkingLevel: z.string().min(1),
|
|
47
|
+
modelOptions: z.array(z.object({
|
|
48
|
+
provider: z.string().min(1),
|
|
49
|
+
id: z.string().min(1),
|
|
50
|
+
name: z.string().min(1)
|
|
51
|
+
}).strict()).max(256).optional()
|
|
52
|
+
}).strict();
|
|
53
|
+
const piConversationSnapshotSchema = z.object({
|
|
54
|
+
agentAvailable: z.boolean(),
|
|
55
|
+
entries: z.array(z.object({
|
|
56
|
+
content: z.array(piContentBlockSchema),
|
|
57
|
+
details: z.unknown().optional(),
|
|
58
|
+
errorMessage: z.string().optional(),
|
|
59
|
+
id: z.string(),
|
|
60
|
+
isError: z.boolean().optional(),
|
|
61
|
+
role: z.enum([
|
|
62
|
+
"assistant",
|
|
63
|
+
"toolResult",
|
|
64
|
+
"user"
|
|
65
|
+
]),
|
|
66
|
+
source: z.enum(["canonical", "live"]),
|
|
67
|
+
status: z.enum([
|
|
68
|
+
"complete",
|
|
69
|
+
"error",
|
|
70
|
+
"pending"
|
|
71
|
+
]),
|
|
72
|
+
stopReason: z.string().optional(),
|
|
73
|
+
timestamp: z.number().optional(),
|
|
74
|
+
toolCallId: z.string().optional(),
|
|
75
|
+
toolName: z.string().optional()
|
|
76
|
+
})),
|
|
77
|
+
sessionMetadata: piSessionMetadataSchema.optional(),
|
|
78
|
+
status: z.enum([
|
|
79
|
+
"busy",
|
|
80
|
+
"degraded",
|
|
81
|
+
"idle",
|
|
82
|
+
"offline"
|
|
83
|
+
])
|
|
84
|
+
});
|
|
85
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
86
|
+
const timestampOf = (value) => {
|
|
87
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
88
|
+
if (typeof value !== "string") return;
|
|
89
|
+
const timestamp = Date.parse(value);
|
|
90
|
+
return Number.isNaN(timestamp) ? void 0 : timestamp;
|
|
91
|
+
};
|
|
92
|
+
const contentBlocksOf = (content) => {
|
|
93
|
+
if (typeof content === "string") return [{
|
|
94
|
+
text: content,
|
|
95
|
+
type: "text"
|
|
96
|
+
}];
|
|
97
|
+
if (!Array.isArray(content)) return [];
|
|
98
|
+
return content.flatMap((value) => {
|
|
99
|
+
if (!isObject(value) || typeof value.type !== "string") return [];
|
|
100
|
+
return [{
|
|
101
|
+
...value,
|
|
102
|
+
type: value.type
|
|
103
|
+
}];
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const browserToolDetails = (details) => {
|
|
107
|
+
if (!isObject(details)) return details;
|
|
108
|
+
const { fullOutputPath: _fullOutputPath, ...safeDetails } = details;
|
|
109
|
+
return safeDetails;
|
|
110
|
+
};
|
|
111
|
+
const toolResultOf = (message) => {
|
|
112
|
+
if (message.role !== "toolResult" || typeof message.toolCallId !== "string" || typeof message.toolName !== "string") return;
|
|
113
|
+
return {
|
|
114
|
+
content: contentBlocksOf(message.content),
|
|
115
|
+
details: browserToolDetails(message.details),
|
|
116
|
+
isError: message.isError === true,
|
|
117
|
+
toolCallId: message.toolCallId,
|
|
118
|
+
toolName: message.toolName
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
const entryOf = (value, source) => {
|
|
122
|
+
if (!isObject(value) || value.type !== "message" || !isObject(value.message)) return;
|
|
123
|
+
if (typeof value.id !== "string") return;
|
|
124
|
+
const message = value.message;
|
|
125
|
+
if (message.role !== "assistant" && message.role !== "toolResult" && message.role !== "user") return;
|
|
126
|
+
return {
|
|
127
|
+
content: contentBlocksOf(message.content),
|
|
128
|
+
details: message.details,
|
|
129
|
+
errorMessage: message.errorMessage,
|
|
130
|
+
id: value.id,
|
|
131
|
+
isError: message.isError,
|
|
132
|
+
role: message.role,
|
|
133
|
+
source,
|
|
134
|
+
status: message.isError === true || message.stopReason === "error" ? "error" : "complete",
|
|
135
|
+
stopReason: message.stopReason,
|
|
136
|
+
timestamp: timestampOf(value.timestamp) ?? timestampOf(message.timestamp),
|
|
137
|
+
toolCallId: message.toolCallId,
|
|
138
|
+
toolName: message.toolName
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
const appendDelta = (blocks, event) => {
|
|
142
|
+
const update = event.assistantMessageEvent;
|
|
143
|
+
if (!isObject(update) || !Number.isSafeInteger(update.contentIndex)) return;
|
|
144
|
+
const index = update.contentIndex;
|
|
145
|
+
if (index < 0) return;
|
|
146
|
+
const current = blocks[index] ?? { type: "text" };
|
|
147
|
+
if (update.type === "text_start") blocks[index] = { type: "text" };
|
|
148
|
+
if (update.type === "thinking_start") blocks[index] = { type: "thinking" };
|
|
149
|
+
if (update.type === "toolcall_start") blocks[index] = { type: "toolCall" };
|
|
150
|
+
if (update.type === "text_delta") blocks[index] = {
|
|
151
|
+
...current,
|
|
152
|
+
text: `${current.text ?? ""}${typeof update.delta === "string" ? update.delta : ""}`,
|
|
153
|
+
type: "text"
|
|
154
|
+
};
|
|
155
|
+
if (update.type === "thinking_delta") blocks[index] = {
|
|
156
|
+
...current,
|
|
157
|
+
thinking: `${current.thinking ?? ""}${typeof update.delta === "string" ? update.delta : ""}`,
|
|
158
|
+
type: "thinking"
|
|
159
|
+
};
|
|
160
|
+
if (update.type === "toolcall_delta") blocks[index] = {
|
|
161
|
+
...current,
|
|
162
|
+
arguments: `${typeof current.arguments === "string" ? current.arguments : ""}${typeof update.delta === "string" ? update.delta : ""}`,
|
|
163
|
+
type: "toolCall"
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
const updateMessage = (messages, record, sourceOrder) => {
|
|
167
|
+
const event = record.event;
|
|
168
|
+
if (typeof event.messageId !== "string") return;
|
|
169
|
+
const key = `${record.streamId}\0${event.messageId}`;
|
|
170
|
+
const current = messages.get(key) ?? {
|
|
171
|
+
blocks: [],
|
|
172
|
+
ended: false,
|
|
173
|
+
id: key,
|
|
174
|
+
messageSequence: typeof event.messageSequence === "number" ? event.messageSequence : sourceOrder,
|
|
175
|
+
sourceOrder,
|
|
176
|
+
status: "pending",
|
|
177
|
+
streamId: record.streamId
|
|
178
|
+
};
|
|
179
|
+
current.timestamp = record.timestamp;
|
|
180
|
+
if (event.type === "message_start" && isObject(event.message)) current.message = event.message;
|
|
181
|
+
if (event.type === "message_update") {
|
|
182
|
+
appendDelta(current.blocks, event);
|
|
183
|
+
const update = event.assistantMessageEvent;
|
|
184
|
+
if (isObject(update) && update.type === "done") current.status = "complete";
|
|
185
|
+
if (isObject(update) && update.type === "error") {
|
|
186
|
+
current.status = "error";
|
|
187
|
+
current.message = {
|
|
188
|
+
...current.message,
|
|
189
|
+
errorMessage: typeof update.errorMessage === "string" ? update.errorMessage : void 0
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (event.type === "message_end" && isObject(event.message)) {
|
|
194
|
+
current.ended = true;
|
|
195
|
+
current.message = event.message;
|
|
196
|
+
current.blocks = contentBlocksOf(current.message.content);
|
|
197
|
+
current.status = current.message.isError === true || current.message.stopReason === "error" ? "error" : "complete";
|
|
198
|
+
}
|
|
199
|
+
messages.set(key, current);
|
|
200
|
+
};
|
|
201
|
+
const updateTool = (tools, event) => {
|
|
202
|
+
if (event.type !== "tool_execution_start" && event.type !== "tool_execution_end" || typeof event.toolCallId !== "string" || typeof event.toolName !== "string") return;
|
|
203
|
+
tools.set(event.toolCallId, {
|
|
204
|
+
isError: event.type === "tool_execution_end" && event.isError === true,
|
|
205
|
+
result: event.type === "tool_execution_end" ? event.result : void 0,
|
|
206
|
+
status: event.type === "tool_execution_start" ? "pending" : event.isError === true ? "error" : "success",
|
|
207
|
+
toolCallId: event.toolCallId,
|
|
208
|
+
toolName: event.toolName
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
const liveEntryOf = (state) => {
|
|
212
|
+
const message = state.message;
|
|
213
|
+
if (!message) return;
|
|
214
|
+
const role = message.role;
|
|
215
|
+
if (role !== "assistant" && role !== "toolResult" && role !== "user") return;
|
|
216
|
+
return {
|
|
217
|
+
content: state.ended ? contentBlocksOf(message.content) : state.blocks.length ? state.blocks : contentBlocksOf(message.content),
|
|
218
|
+
details: message.details,
|
|
219
|
+
errorMessage: message.errorMessage,
|
|
220
|
+
id: state.id,
|
|
221
|
+
isError: message.isError,
|
|
222
|
+
role,
|
|
223
|
+
source: "live",
|
|
224
|
+
status: state.status,
|
|
225
|
+
stopReason: message.stopReason,
|
|
226
|
+
timestamp: state.timestamp ?? timestampOf(message.timestamp),
|
|
227
|
+
toolCallId: message.toolCallId,
|
|
228
|
+
toolName: message.toolName
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
const projectLive = (records) => {
|
|
232
|
+
const messages = /* @__PURE__ */ new Map();
|
|
233
|
+
const tools = /* @__PURE__ */ new Map();
|
|
234
|
+
let active = false;
|
|
235
|
+
let shutdown = false;
|
|
236
|
+
records.forEach((record, index) => {
|
|
237
|
+
updateMessage(messages, record, index);
|
|
238
|
+
updateTool(tools, record.event);
|
|
239
|
+
if (record.event.type === "agent_start") active = true;
|
|
240
|
+
if (record.event.type === "agent_settled") active = false;
|
|
241
|
+
if (record.event.type === "session_shutdown") shutdown = true;
|
|
242
|
+
if (record.event.type === "session_start") shutdown = false;
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
active,
|
|
246
|
+
entries: [...messages.values()].sort((left, right) => (left.timestamp ?? 0) - (right.timestamp ?? 0) || left.streamId.localeCompare(right.streamId) || left.messageSequence - right.messageSequence).flatMap((message) => {
|
|
247
|
+
const entry = liveEntryOf(message);
|
|
248
|
+
return entry ? [entry] : [];
|
|
249
|
+
}),
|
|
250
|
+
shutdown,
|
|
251
|
+
tools
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
const fingerprint = (entry) => JSON.stringify({
|
|
255
|
+
content: entry.content.map(({ tool: _tool, ...block }) => block),
|
|
256
|
+
details: entry.details,
|
|
257
|
+
isError: entry.isError,
|
|
258
|
+
role: entry.role,
|
|
259
|
+
toolCallId: entry.toolCallId,
|
|
260
|
+
toolName: entry.toolName
|
|
261
|
+
});
|
|
262
|
+
const matchedLiveIndexes = (canonical, live) => {
|
|
263
|
+
const liveByFingerprint = /* @__PURE__ */ new Map();
|
|
264
|
+
live.forEach((entry, index) => {
|
|
265
|
+
if (entry.status === "pending") return;
|
|
266
|
+
const key = fingerprint(entry);
|
|
267
|
+
const indexes = liveByFingerprint.get(key) ?? [];
|
|
268
|
+
indexes.push(index);
|
|
269
|
+
liveByFingerprint.set(key, indexes);
|
|
270
|
+
});
|
|
271
|
+
const consumed = /* @__PURE__ */ new Map();
|
|
272
|
+
const matched = /* @__PURE__ */ new Set();
|
|
273
|
+
canonical.forEach((entry) => {
|
|
274
|
+
const key = fingerprint(entry);
|
|
275
|
+
const offset = consumed.get(key) ?? 0;
|
|
276
|
+
const index = liveByFingerprint.get(key)?.[offset];
|
|
277
|
+
if (index !== void 0) {
|
|
278
|
+
matched.add(index);
|
|
279
|
+
consumed.set(key, offset + 1);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return matched;
|
|
283
|
+
};
|
|
284
|
+
const contentFromToolEnd = (result) => {
|
|
285
|
+
if (isObject(result) && "content" in result) return contentBlocksOf(result.content);
|
|
286
|
+
if (typeof result === "string") return [{
|
|
287
|
+
text: result,
|
|
288
|
+
type: "text"
|
|
289
|
+
}];
|
|
290
|
+
return result === void 0 ? [] : [{
|
|
291
|
+
text: JSON.stringify(result, null, 2),
|
|
292
|
+
type: "text"
|
|
293
|
+
}];
|
|
294
|
+
};
|
|
295
|
+
const pairTools = (entries, liveTools) => {
|
|
296
|
+
const callIds = new Set(entries.flatMap((entry) => entry.content.flatMap((block) => block.type === "toolCall" && typeof block.id === "string" ? [block.id] : [])));
|
|
297
|
+
const results = /* @__PURE__ */ new Map();
|
|
298
|
+
entries.forEach((entry) => {
|
|
299
|
+
if (entry.role !== "toolResult") return;
|
|
300
|
+
const result = toolResultOf({
|
|
301
|
+
content: entry.content,
|
|
302
|
+
details: entry.details,
|
|
303
|
+
isError: entry.isError,
|
|
304
|
+
role: entry.role,
|
|
305
|
+
toolCallId: entry.toolCallId,
|
|
306
|
+
toolName: entry.toolName
|
|
307
|
+
});
|
|
308
|
+
if (result) results.set(result.toolCallId, result);
|
|
309
|
+
});
|
|
310
|
+
liveTools.forEach((tool) => {
|
|
311
|
+
if (results.has(tool.toolCallId) || tool.result === void 0) return;
|
|
312
|
+
results.set(tool.toolCallId, {
|
|
313
|
+
content: contentFromToolEnd(tool.result),
|
|
314
|
+
details: isObject(tool.result) ? browserToolDetails(tool.result.details) : void 0,
|
|
315
|
+
isError: tool.isError === true,
|
|
316
|
+
toolCallId: tool.toolCallId,
|
|
317
|
+
toolName: tool.toolName
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
return entries.map((entry) => ({
|
|
321
|
+
...entry,
|
|
322
|
+
content: entry.content.map((block) => {
|
|
323
|
+
if (block.type !== "toolCall" || typeof block.id !== "string") return block;
|
|
324
|
+
const result = results.get(block.id);
|
|
325
|
+
const live = liveTools.get(block.id);
|
|
326
|
+
return {
|
|
327
|
+
...block,
|
|
328
|
+
tool: {
|
|
329
|
+
result,
|
|
330
|
+
status: result ? result.isError ? "error" : "success" : live?.status ?? "pending"
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
})
|
|
334
|
+
})).filter((entry) => entry.role !== "toolResult" || !entry.toolCallId || !callIds.has(entry.toolCallId));
|
|
335
|
+
};
|
|
336
|
+
const recordsSafeToProject = (stream) => {
|
|
337
|
+
const records = [...stream.records.values()].sort((left, right) => left.sequence - right.sequence);
|
|
338
|
+
const gapIndex = records.findIndex((record, index) => record.sequence !== index + 1);
|
|
339
|
+
if (gapIndex < 0) return records;
|
|
340
|
+
return [...records.slice(0, gapIndex), ...records.slice(gapIndex).filter((record) => record.event.type === "message_end")];
|
|
341
|
+
};
|
|
342
|
+
const sortedStreamRecords = (streams) => [...streams.values()].flatMap(recordsSafeToProject).sort((left, right) => left.timestamp - right.timestamp || left.streamId.localeCompare(right.streamId) || left.sequence - right.sequence);
|
|
343
|
+
const streamHasIssue = (stream) => {
|
|
344
|
+
if (stream.conflict) return true;
|
|
345
|
+
return [...stream.records.keys()].sort((left, right) => left - right).some((sequence, index) => sequence !== index + 1);
|
|
346
|
+
};
|
|
347
|
+
const refreshCanonical = async (state, sessionFile) => {
|
|
348
|
+
if (!sessionFile) {
|
|
349
|
+
const changed = state.canonicalRecords.length > 0;
|
|
350
|
+
state.canonicalRecords = [];
|
|
351
|
+
return changed;
|
|
352
|
+
}
|
|
353
|
+
const update = await state.canonicalTail.read(sessionFile);
|
|
354
|
+
if (update.reset) state.canonicalRecords = [];
|
|
355
|
+
state.canonicalRecords.push(...update.records);
|
|
356
|
+
return update.reset || update.records.length > 0;
|
|
357
|
+
};
|
|
358
|
+
const refreshStreams = async (state, data) => {
|
|
359
|
+
if (!data.liveEventsDir) {
|
|
360
|
+
const changed = state.streams.size > 0;
|
|
361
|
+
state.streams.clear();
|
|
362
|
+
return changed;
|
|
363
|
+
}
|
|
364
|
+
const directory = join(data.liveEventsDir, data.sessionId);
|
|
365
|
+
const files = (await readdir(directory).catch(() => [])).filter((file) => file.endsWith(".jsonl"));
|
|
366
|
+
const present = new Set(files);
|
|
367
|
+
let removed = false;
|
|
368
|
+
[...state.streams.keys()].forEach((file) => {
|
|
369
|
+
if (!present.has(file)) {
|
|
370
|
+
const stream = state.streams.get(file);
|
|
371
|
+
removed = removed || Boolean(stream?.conflict || stream?.records.size);
|
|
372
|
+
state.streams.delete(file);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
const updates = await Promise.all(files.map(async (file) => {
|
|
376
|
+
const stream = state.streams.get(file) ?? {
|
|
377
|
+
conflict: false,
|
|
378
|
+
records: /* @__PURE__ */ new Map(),
|
|
379
|
+
tail: createJsonlTail()
|
|
380
|
+
};
|
|
381
|
+
state.streams.set(file, stream);
|
|
382
|
+
const update = await stream.tail.read(join(directory, file));
|
|
383
|
+
let changed = update.reset && Boolean(stream.conflict || stream.records.size);
|
|
384
|
+
if (update.reset) {
|
|
385
|
+
stream.conflict = false;
|
|
386
|
+
stream.records.clear();
|
|
387
|
+
}
|
|
388
|
+
const expectedStreamId = file.slice(0, -6);
|
|
389
|
+
update.records.forEach((value) => {
|
|
390
|
+
const record = parseLiveEventRecord(value, data.sessionId);
|
|
391
|
+
if (!record) return;
|
|
392
|
+
if (record.streamId !== expectedStreamId) {
|
|
393
|
+
changed = changed || !stream.conflict;
|
|
394
|
+
stream.conflict = true;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const previous = stream.records.get(record.sequence);
|
|
398
|
+
if (previous && JSON.stringify(previous) !== JSON.stringify(record)) {
|
|
399
|
+
changed = changed || !stream.conflict;
|
|
400
|
+
stream.conflict = true;
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
changed = changed || !previous;
|
|
404
|
+
stream.records.set(record.sequence, record);
|
|
405
|
+
});
|
|
406
|
+
return changed;
|
|
407
|
+
}));
|
|
408
|
+
return removed || updates.some(Boolean);
|
|
409
|
+
};
|
|
410
|
+
const activeCanonicalBranch = (records) => {
|
|
411
|
+
const treeEntries = records.flatMap((record) => {
|
|
412
|
+
if (!isObject(record) || typeof record.id !== "string" || record.parentId !== null && typeof record.parentId !== "string") return [];
|
|
413
|
+
return [record];
|
|
414
|
+
});
|
|
415
|
+
const leaf = treeEntries.at(-1);
|
|
416
|
+
if (!leaf) return records;
|
|
417
|
+
const byId = new Map(treeEntries.map((entry) => [entry.id, entry]));
|
|
418
|
+
const ancestry = /* @__PURE__ */ new Set();
|
|
419
|
+
for (let current = leaf; current;) {
|
|
420
|
+
if (ancestry.has(current.id)) return [];
|
|
421
|
+
ancestry.add(current.id);
|
|
422
|
+
current = typeof current.parentId === "string" ? byId.get(current.parentId) : void 0;
|
|
423
|
+
}
|
|
424
|
+
return records.filter((record) => isObject(record) && typeof record.id === "string" && ancestry.has(record.id));
|
|
425
|
+
};
|
|
426
|
+
const projectSnapshot = (state, agentAvailable) => {
|
|
427
|
+
const canonical = activeCanonicalBranch(state.canonicalRecords).flatMap((value) => {
|
|
428
|
+
const entry = entryOf(value, "canonical");
|
|
429
|
+
return entry ? [entry] : [];
|
|
430
|
+
});
|
|
431
|
+
const live = projectLive(sortedStreamRecords(state.streams));
|
|
432
|
+
const matched = matchedLiveIndexes(canonical, live.entries);
|
|
433
|
+
const entries = pairTools([...canonical, ...live.entries.filter((_entry, index) => !matched.has(index))].sort((left, right) => (left.timestamp ?? 0) - (right.timestamp ?? 0) || left.id.localeCompare(right.id)), live.tools);
|
|
434
|
+
const degraded = [...state.streams.values()].some(streamHasIssue);
|
|
435
|
+
return piConversationSnapshotSchema.parse({
|
|
436
|
+
entries,
|
|
437
|
+
agentAvailable,
|
|
438
|
+
...state.sessionMetadata ? { sessionMetadata: state.sessionMetadata } : {},
|
|
439
|
+
status: degraded ? "degraded" : live.shutdown || !agentAvailable && !live.active ? "offline" : live.active || entries.some((entry) => entry.status === "pending") ? "busy" : "idle"
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
const configKeyOf = (data) => JSON.stringify([
|
|
443
|
+
data.sessionId,
|
|
444
|
+
data.sessionFile,
|
|
445
|
+
data.liveEventsDir
|
|
446
|
+
]);
|
|
447
|
+
const createProjectionState = (data) => ({
|
|
448
|
+
canonicalRecords: [],
|
|
449
|
+
canonicalTail: createJsonlTail(),
|
|
450
|
+
configKey: configKeyOf(data),
|
|
451
|
+
refreshQueue: Promise.resolve(),
|
|
452
|
+
sessionMetadata: data.sessionMetadata,
|
|
453
|
+
streams: /* @__PURE__ */ new Map()
|
|
454
|
+
});
|
|
455
|
+
const queueProjectionRefresh = (state, operation) => {
|
|
456
|
+
const result = state.refreshQueue.then(operation);
|
|
457
|
+
state.refreshQueue = result.then(() => void 0, () => void 0);
|
|
458
|
+
return result;
|
|
459
|
+
};
|
|
460
|
+
const probePiAgent = probePiSession;
|
|
461
|
+
const createPiAgentConversationService = ({ resolveAgent, pollIntervalMs = 250, probe = probePiAgent }) => {
|
|
462
|
+
const projections = /* @__PURE__ */ new Map();
|
|
463
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
464
|
+
let timer;
|
|
465
|
+
let polling = false;
|
|
466
|
+
const refresh = async (agentId) => {
|
|
467
|
+
const data = await resolveAgent(agentId);
|
|
468
|
+
const key = agentId;
|
|
469
|
+
const previous = projections.get(key);
|
|
470
|
+
const state = previous?.configKey === configKeyOf(data) ? previous : createProjectionState(data);
|
|
471
|
+
projections.set(key, state);
|
|
472
|
+
return queueProjectionRefresh(state, async () => {
|
|
473
|
+
const metadataChanged = JSON.stringify(state.sessionMetadata) !== JSON.stringify(data.sessionMetadata);
|
|
474
|
+
state.sessionMetadata = data.sessionMetadata;
|
|
475
|
+
const [canonicalChanged, streamsChanged, agentAvailable] = await Promise.all([
|
|
476
|
+
refreshCanonical(state, data.sessionFile),
|
|
477
|
+
refreshStreams(state, data),
|
|
478
|
+
probe(data.sessionId)
|
|
479
|
+
]);
|
|
480
|
+
if (state.snapshot && !canonicalChanged && !streamsChanged && !metadataChanged && state.agentAvailable === agentAvailable) return state.snapshot;
|
|
481
|
+
const snapshot = projectSnapshot(state, agentAvailable);
|
|
482
|
+
state.agentAvailable = agentAvailable;
|
|
483
|
+
state.snapshot = snapshot;
|
|
484
|
+
return snapshot;
|
|
485
|
+
});
|
|
486
|
+
};
|
|
487
|
+
const poll = async () => {
|
|
488
|
+
if (polling) return;
|
|
489
|
+
polling = true;
|
|
490
|
+
try {
|
|
491
|
+
await Promise.all([...subscriptions.entries()].map(async ([key, listeners]) => {
|
|
492
|
+
const id = key;
|
|
493
|
+
try {
|
|
494
|
+
const before = projections.get(key)?.snapshot;
|
|
495
|
+
const snapshot = await refresh(id);
|
|
496
|
+
if (snapshot !== before) listeners.forEach((listener) => listener(snapshot));
|
|
497
|
+
} catch {
|
|
498
|
+
const state = projections.get(key);
|
|
499
|
+
if (!state || state.agentAvailable === false) return;
|
|
500
|
+
const snapshot = projectSnapshot(state, false);
|
|
501
|
+
state.agentAvailable = false;
|
|
502
|
+
state.snapshot = snapshot;
|
|
503
|
+
listeners.forEach((listener) => listener(snapshot));
|
|
504
|
+
}
|
|
505
|
+
}));
|
|
506
|
+
} finally {
|
|
507
|
+
polling = false;
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
const startPolling = () => {
|
|
511
|
+
if (!timer) timer = setInterval(() => void poll(), pollIntervalMs);
|
|
512
|
+
};
|
|
513
|
+
return {
|
|
514
|
+
dispose: () => {
|
|
515
|
+
if (timer) clearInterval(timer);
|
|
516
|
+
timer = void 0;
|
|
517
|
+
subscriptions.clear();
|
|
518
|
+
projections.clear();
|
|
519
|
+
},
|
|
520
|
+
getSnapshot: (agentId) => refresh(agentId),
|
|
521
|
+
subscribe: (agentId, listener) => {
|
|
522
|
+
const key = agentId;
|
|
523
|
+
const listeners = subscriptions.get(key) ?? /* @__PURE__ */ new Set();
|
|
524
|
+
listeners.add(listener);
|
|
525
|
+
subscriptions.set(key, listeners);
|
|
526
|
+
startPolling();
|
|
527
|
+
return () => {
|
|
528
|
+
listeners.delete(listener);
|
|
529
|
+
if (!listeners.size) subscriptions.delete(key);
|
|
530
|
+
if (!subscriptions.size && timer) {
|
|
531
|
+
clearInterval(timer);
|
|
532
|
+
timer = void 0;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
const sendPiAgentMessage = async ({ deliverAs, message, sessionId }, send = sendUserMessage) => {
|
|
539
|
+
const response = await send(sessionId, {
|
|
540
|
+
deliverAs,
|
|
541
|
+
message,
|
|
542
|
+
requestId: randomUUID()
|
|
543
|
+
});
|
|
544
|
+
if (!response?.ok) throw new Error("Pi agent is unavailable");
|
|
545
|
+
return response;
|
|
546
|
+
};
|
|
547
|
+
//#endregion
|
|
548
|
+
export { createPiAgentConversationService, piContentBlockSchema, piConversationSnapshotSchema, piSessionMetadataSchema, piToolProjectionSchema, piToolResultProjectionSchema, probePiAgent, sendPiAgentMessage };
|
|
549
|
+
|
|
550
|
+
//# sourceMappingURL=projection.js.map
|