@mariozechner/pi-agent-core 0.16.0 → 0.17.0
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/dist/agent.d.ts +9 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +54 -15
- package/dist/agent.js.map +1 -1
- package/dist/transports/AppTransport.d.ts +4 -0
- package/dist/transports/AppTransport.d.ts.map +1 -1
- package/dist/transports/AppTransport.js +30 -13
- package/dist/transports/AppTransport.js.map +1 -1
- package/dist/transports/ProviderTransport.d.ts +4 -0
- package/dist/transports/ProviderTransport.d.ts.map +1 -1
- package/dist/transports/ProviderTransport.js +22 -8
- package/dist/transports/ProviderTransport.js.map +1 -1
- package/dist/transports/types.d.ts +3 -0
- package/dist/transports/types.d.ts.map +1 -1
- package/dist/transports/types.js.map +1 -1
- package/package.json +3 -3
package/dist/agent.d.ts
CHANGED
|
@@ -42,6 +42,15 @@ export declare class Agent {
|
|
|
42
42
|
*/
|
|
43
43
|
reset(): void;
|
|
44
44
|
prompt(input: string, attachments?: Attachment[]): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Continue from the current context without adding a new user message.
|
|
47
|
+
* Used for retry after overflow recovery when context already has user message or tool results.
|
|
48
|
+
*/
|
|
49
|
+
continue(): Promise<void>;
|
|
50
|
+
private _runAgentLoop;
|
|
51
|
+
private _runAgentLoopContinue;
|
|
52
|
+
private _prepareRun;
|
|
53
|
+
private _processEvents;
|
|
45
54
|
private emit;
|
|
46
55
|
}
|
|
47
56
|
//# sourceMappingURL=agent.d.ts.map
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,OAAO,EAA+C,MAAM,qBAAqB,CAAC;AAE9G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiDhG,MAAM,WAAW,YAAY;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACnC,SAAS,EAAE,cAAc,CAAC;IAE1B,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAEhF,SAAS,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC;CACpC;AAED,qBAAa,KAAK;IACjB,OAAO,CAAC,MAAM,CAUZ;IACF,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,kBAAkB,CAA6D;IACvF,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,oBAAoB,CAAC,CAAa;IAE1C,YAAY,IAAI,EAAE,YAAY,EAK7B;IAED,IAAI,KAAK,IAAI,UAAU,CAEtB;IAED,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAGjD;IAGD,eAAe,CAAC,CAAC,EAAE,MAAM,QAExB;IAED,QAAQ,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAEnC;IAED,gBAAgB,CAAC,CAAC,EAAE,aAAa,QAEhC;IAED,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,eAAe,QAEzC;IAED,YAAY,IAAI,KAAK,GAAG,eAAe,CAEtC;IAED,QAAQ,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAEnC;IAED,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,QAE/B;IAED,aAAa,CAAC,CAAC,EAAE,UAAU,QAE1B;IAEK,YAAY,CAAC,CAAC,EAAE,UAAU,iBAO/B;IAED,iBAAiB,SAEhB;IAED,aAAa,SAEZ;IAED,KAAK,SAEJ;IAED;;;OAGG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED;;OAEG;IACH,KAAK,SAOJ;IAEK,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,iBAqLrD;IAED,OAAO,CAAC,IAAI;CAKZ","sourcesContent":["import type { ImageContent, Message, QueuedMessage, ReasoningEffort, TextContent } from \"@mariozechner/pi-ai\";\nimport { getModel } from \"@mariozechner/pi-ai\";\nimport type { AgentTransport } from \"./transports/types.js\";\nimport type { AgentEvent, AgentState, AppMessage, Attachment, ThinkingLevel } from \"./types.js\";\n\n/**\n * Default message transformer: Keep only LLM-compatible messages, strip app-specific fields.\n * Converts attachments to proper content blocks (images → ImageContent, documents → TextContent).\n */\nfunction defaultMessageTransformer(messages: AppMessage[]): Message[] {\n\treturn messages\n\t\t.filter((m) => {\n\t\t\t// Only keep standard LLM message roles\n\t\t\treturn m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\";\n\t\t})\n\t\t.map((m) => {\n\t\t\tif (m.role === \"user\") {\n\t\t\t\tconst { attachments, ...rest } = m as any;\n\n\t\t\t\t// If no attachments, return as-is\n\t\t\t\tif (!attachments || attachments.length === 0) {\n\t\t\t\t\treturn rest as Message;\n\t\t\t\t}\n\n\t\t\t\t// Convert attachments to content blocks\n\t\t\t\tconst content = Array.isArray(rest.content) ? [...rest.content] : [{ type: \"text\", text: rest.content }];\n\n\t\t\t\tfor (const attachment of attachments as Attachment[]) {\n\t\t\t\t\t// Add image blocks for image attachments\n\t\t\t\t\tif (attachment.type === \"image\") {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"image\",\n\t\t\t\t\t\t\tdata: attachment.content,\n\t\t\t\t\t\t\tmimeType: attachment.mimeType,\n\t\t\t\t\t\t} as ImageContent);\n\t\t\t\t\t}\n\t\t\t\t\t// Add text blocks for documents with extracted text\n\t\t\t\t\telse if (attachment.type === \"document\" && attachment.extractedText) {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `\\n\\n[Document: ${attachment.fileName}]\\n${attachment.extractedText}`,\n\t\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t\t} as TextContent);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn { ...rest, content } as Message;\n\t\t\t}\n\t\t\treturn m as Message;\n\t\t});\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\ttransport: AgentTransport;\n\t// Transform app messages to LLM-compatible messages before sending to transport\n\tmessageTransformer?: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\t// Queue mode: \"all\" = send all queued messages at once, \"one-at-a-time\" = send one queued message per turn\n\tqueueMode?: \"all\" | \"one-at-a-time\";\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite-preview-06-17\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate transport: AgentTransport;\n\tprivate messageTransformer: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\tprivate messageQueue: Array<QueuedMessage<AppMessage>> = [];\n\tprivate queueMode: \"all\" | \"one-at-a-time\";\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\n\tconstructor(opts: AgentOptions) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.transport = opts.transport;\n\t\tthis.messageTransformer = opts.messageTransformer || defaultMessageTransformer;\n\t\tthis.queueMode = opts.queueMode || \"one-at-a-time\";\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators - update internal state without emitting events\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: typeof this._state.model) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetQueueMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.queueMode = mode;\n\t}\n\n\tgetQueueMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.queueMode;\n\t}\n\n\tsetTools(t: typeof this._state.tools) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AppMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AppMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\tasync queueMessage(m: AppMessage) {\n\t\t// Transform message and queue it for injection at next turn\n\t\tconst transformed = await this.messageTransformer([m]);\n\t\tthis.messageQueue.push({\n\t\t\toriginal: m,\n\t\t\tllm: transformed[0], // undefined if filtered out\n\t\t});\n\t}\n\n\tclearMessageQueue() {\n\t\tthis.messageQueue = [];\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\t/**\n\t * Returns a promise that resolves when the current prompt completes.\n\t * Returns immediately resolved promise if no prompt is running.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\t/**\n\t * Clear all messages and state. Call abort() first if a prompt is in flight.\n\t */\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.messageQueue = [];\n\t}\n\n\tasync prompt(input: string, attachments?: Attachment[]) {\n\t\tconst model = this._state.model;\n\t\tif (!model) {\n\t\t\tthrow new Error(\"No model configured\");\n\t\t}\n\n\t\t// Set up running prompt tracking\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\t// Build user message with attachments\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (attachments?.length) {\n\t\t\tfor (const a of attachments) {\n\t\t\t\tif (a.type === \"image\") {\n\t\t\t\t\tcontent.push({ type: \"image\", data: a.content, mimeType: a.mimeType });\n\t\t\t\t} else if (a.type === \"document\" && a.extractedText) {\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `\\n\\n[Document: ${a.fileName}]\\n${a.extractedText}`,\n\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t} as TextContent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst userMessage: AppMessage = {\n\t\t\trole: \"user\",\n\t\t\tcontent,\n\t\t\tattachments: attachments?.length ? attachments : undefined,\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning: ReasoningEffort | undefined =\n\t\t\tthis._state.thinkingLevel === \"off\"\n\t\t\t\t? undefined\n\t\t\t\t: this._state.thinkingLevel === \"minimal\"\n\t\t\t\t\t? \"low\"\n\t\t\t\t\t: this._state.thinkingLevel;\n\n\t\tconst cfg = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\ttools: this._state.tools,\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tgetQueuedMessages: async <T>() => {\n\t\t\t\t// Return queued messages based on queue mode\n\t\t\t\tif (this.queueMode === \"one-at-a-time\") {\n\t\t\t\t\t// Return only first message\n\t\t\t\t\tif (this.messageQueue.length > 0) {\n\t\t\t\t\t\tconst first = this.messageQueue[0];\n\t\t\t\t\t\tthis.messageQueue = this.messageQueue.slice(1);\n\t\t\t\t\t\treturn [first] as QueuedMessage<T>[];\n\t\t\t\t\t}\n\t\t\t\t\treturn [];\n\t\t\t\t} else {\n\t\t\t\t\t// Return all queued messages at once\n\t\t\t\t\tconst queued = this.messageQueue.slice();\n\t\t\t\t\tthis.messageQueue = [];\n\t\t\t\t\treturn queued as QueuedMessage<T>[];\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\n\t\t// Track all messages generated in this prompt\n\t\tconst generatedMessages: AppMessage[] = [];\n\n\t\ttry {\n\t\t\tlet partial: Message | null = null;\n\n\t\t\t// Transform app messages to LLM-compatible messages (initial set)\n\t\t\tconst llmMessages = await this.messageTransformer(this._state.messages);\n\n\t\t\tfor await (const ev of this.transport.run(\n\t\t\t\tllmMessages,\n\t\t\t\tuserMessage as Message,\n\t\t\t\tcfg,\n\t\t\t\tthis.abortController.signal,\n\t\t\t)) {\n\t\t\t\t// Update internal state BEFORE emitting events\n\t\t\t\t// so handlers see consistent state\n\t\t\t\tswitch (ev.type) {\n\t\t\t\t\tcase \"message_start\": {\n\t\t\t\t\t\tpartial = ev.message;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_update\": {\n\t\t\t\t\t\tpartial = ev.message;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_end\": {\n\t\t\t\t\t\tpartial = null;\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tthis.appendMessage(ev.message as AppMessage);\n\t\t\t\t\t\tgeneratedMessages.push(ev.message as AppMessage);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_start\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.add(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_end\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.delete(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"turn_end\": {\n\t\t\t\t\t\tif (ev.message.role === \"assistant\" && ev.message.errorMessage) {\n\t\t\t\t\t\t\tthis._state.error = ev.message.errorMessage;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"agent_end\": {\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Emit after state is updated\n\t\t\t\tthis.emit(ev as AgentEvent);\n\t\t\t}\n\n\t\t\t// Handle any remaining partial message\n\t\t\tif (partial && partial.role === \"assistant\" && partial.content.length > 0) {\n\t\t\t\tconst onlyEmpty = !partial.content.some(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\t(c.type === \"thinking\" && c.thinking.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"text\" && c.text.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"toolCall\" && c.name.trim().length > 0),\n\t\t\t\t);\n\t\t\t\tif (!onlyEmpty) {\n\t\t\t\t\tthis.appendMessage(partial as AppMessage);\n\t\t\t\t\tgeneratedMessages.push(partial as AppMessage);\n\t\t\t\t} else {\n\t\t\t\t\tif (this.abortController?.signal.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst msg: Message = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis.appendMessage(msg as AppMessage);\n\t\t\tgeneratedMessages.push(msg as AppMessage);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,OAAO,EAA+C,MAAM,qBAAqB,CAAC;AAE9G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiDhG,MAAM,WAAW,YAAY;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACnC,SAAS,EAAE,cAAc,CAAC;IAE1B,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAEhF,SAAS,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC;CACpC;AAED,qBAAa,KAAK;IACjB,OAAO,CAAC,MAAM,CAUZ;IACF,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,kBAAkB,CAA6D;IACvF,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,oBAAoB,CAAC,CAAa;IAE1C,YAAY,IAAI,EAAE,YAAY,EAK7B;IAED,IAAI,KAAK,IAAI,UAAU,CAEtB;IAED,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAGjD;IAGD,eAAe,CAAC,CAAC,EAAE,MAAM,QAExB;IAED,QAAQ,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAEnC;IAED,gBAAgB,CAAC,CAAC,EAAE,aAAa,QAEhC;IAED,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,eAAe,QAEzC;IAED,YAAY,IAAI,KAAK,GAAG,eAAe,CAEtC;IAED,QAAQ,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAEnC;IAED,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,QAE/B;IAED,aAAa,CAAC,CAAC,EAAE,UAAU,QAE1B;IAEK,YAAY,CAAC,CAAC,EAAE,UAAU,iBAO/B;IAED,iBAAiB,SAEhB;IAED,aAAa,SAEZ;IAED,KAAK,SAEJ;IAED;;;OAGG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED;;OAEG;IACH,KAAK,SAOJ;IAEK,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,iBA8BrD;IAED;;;OAGG;IACG,QAAQ,kBAYb;YAKa,aAAa;YAWb,qBAAqB;YAWrB,WAAW;YAmDX,cAAc;IAsG5B,OAAO,CAAC,IAAI;CAKZ","sourcesContent":["import type { ImageContent, Message, QueuedMessage, ReasoningEffort, TextContent } from \"@mariozechner/pi-ai\";\nimport { getModel } from \"@mariozechner/pi-ai\";\nimport type { AgentTransport } from \"./transports/types.js\";\nimport type { AgentEvent, AgentState, AppMessage, Attachment, ThinkingLevel } from \"./types.js\";\n\n/**\n * Default message transformer: Keep only LLM-compatible messages, strip app-specific fields.\n * Converts attachments to proper content blocks (images → ImageContent, documents → TextContent).\n */\nfunction defaultMessageTransformer(messages: AppMessage[]): Message[] {\n\treturn messages\n\t\t.filter((m) => {\n\t\t\t// Only keep standard LLM message roles\n\t\t\treturn m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\";\n\t\t})\n\t\t.map((m) => {\n\t\t\tif (m.role === \"user\") {\n\t\t\t\tconst { attachments, ...rest } = m as any;\n\n\t\t\t\t// If no attachments, return as-is\n\t\t\t\tif (!attachments || attachments.length === 0) {\n\t\t\t\t\treturn rest as Message;\n\t\t\t\t}\n\n\t\t\t\t// Convert attachments to content blocks\n\t\t\t\tconst content = Array.isArray(rest.content) ? [...rest.content] : [{ type: \"text\", text: rest.content }];\n\n\t\t\t\tfor (const attachment of attachments as Attachment[]) {\n\t\t\t\t\t// Add image blocks for image attachments\n\t\t\t\t\tif (attachment.type === \"image\") {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"image\",\n\t\t\t\t\t\t\tdata: attachment.content,\n\t\t\t\t\t\t\tmimeType: attachment.mimeType,\n\t\t\t\t\t\t} as ImageContent);\n\t\t\t\t\t}\n\t\t\t\t\t// Add text blocks for documents with extracted text\n\t\t\t\t\telse if (attachment.type === \"document\" && attachment.extractedText) {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `\\n\\n[Document: ${attachment.fileName}]\\n${attachment.extractedText}`,\n\t\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t\t} as TextContent);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn { ...rest, content } as Message;\n\t\t\t}\n\t\t\treturn m as Message;\n\t\t});\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\ttransport: AgentTransport;\n\t// Transform app messages to LLM-compatible messages before sending to transport\n\tmessageTransformer?: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\t// Queue mode: \"all\" = send all queued messages at once, \"one-at-a-time\" = send one queued message per turn\n\tqueueMode?: \"all\" | \"one-at-a-time\";\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite-preview-06-17\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate transport: AgentTransport;\n\tprivate messageTransformer: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\tprivate messageQueue: Array<QueuedMessage<AppMessage>> = [];\n\tprivate queueMode: \"all\" | \"one-at-a-time\";\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\n\tconstructor(opts: AgentOptions) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.transport = opts.transport;\n\t\tthis.messageTransformer = opts.messageTransformer || defaultMessageTransformer;\n\t\tthis.queueMode = opts.queueMode || \"one-at-a-time\";\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators - update internal state without emitting events\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: typeof this._state.model) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetQueueMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.queueMode = mode;\n\t}\n\n\tgetQueueMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.queueMode;\n\t}\n\n\tsetTools(t: typeof this._state.tools) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AppMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AppMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\tasync queueMessage(m: AppMessage) {\n\t\t// Transform message and queue it for injection at next turn\n\t\tconst transformed = await this.messageTransformer([m]);\n\t\tthis.messageQueue.push({\n\t\t\toriginal: m,\n\t\t\tllm: transformed[0], // undefined if filtered out\n\t\t});\n\t}\n\n\tclearMessageQueue() {\n\t\tthis.messageQueue = [];\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\t/**\n\t * Returns a promise that resolves when the current prompt completes.\n\t * Returns immediately resolved promise if no prompt is running.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\t/**\n\t * Clear all messages and state. Call abort() first if a prompt is in flight.\n\t */\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.messageQueue = [];\n\t}\n\n\tasync prompt(input: string, attachments?: Attachment[]) {\n\t\tconst model = this._state.model;\n\t\tif (!model) {\n\t\t\tthrow new Error(\"No model configured\");\n\t\t}\n\n\t\t// Build user message with attachments\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (attachments?.length) {\n\t\t\tfor (const a of attachments) {\n\t\t\t\tif (a.type === \"image\") {\n\t\t\t\t\tcontent.push({ type: \"image\", data: a.content, mimeType: a.mimeType });\n\t\t\t\t} else if (a.type === \"document\" && a.extractedText) {\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `\\n\\n[Document: ${a.fileName}]\\n${a.extractedText}`,\n\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t} as TextContent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst userMessage: AppMessage = {\n\t\t\trole: \"user\",\n\t\t\tcontent,\n\t\t\tattachments: attachments?.length ? attachments : undefined,\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tawait this._runAgentLoop(userMessage);\n\t}\n\n\t/**\n\t * Continue from the current context without adding a new user message.\n\t * Used for retry after overflow recovery when context already has user message or tool results.\n\t */\n\tasync continue() {\n\t\tconst messages = this._state.messages;\n\t\tif (messages.length === 0) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\n\t\tconst lastMessage = messages[messages.length - 1];\n\t\tif (lastMessage.role !== \"user\" && lastMessage.role !== \"toolResult\") {\n\t\t\tthrow new Error(`Cannot continue from message role: ${lastMessage.role}`);\n\t\t}\n\n\t\tawait this._runAgentLoopContinue();\n\t}\n\n\t/**\n\t * Internal: Run the agent loop with a new user message.\n\t */\n\tprivate async _runAgentLoop(userMessage: AppMessage) {\n\t\tconst { llmMessages, cfg } = await this._prepareRun();\n\n\t\tconst events = this.transport.run(llmMessages, userMessage as Message, cfg, this.abortController!.signal);\n\n\t\tawait this._processEvents(events);\n\t}\n\n\t/**\n\t * Internal: Continue the agent loop from current context.\n\t */\n\tprivate async _runAgentLoopContinue() {\n\t\tconst { llmMessages, cfg } = await this._prepareRun();\n\n\t\tconst events = this.transport.continue(llmMessages, cfg, this.abortController!.signal);\n\n\t\tawait this._processEvents(events);\n\t}\n\n\t/**\n\t * Prepare for running the agent loop.\n\t */\n\tprivate async _prepareRun() {\n\t\tconst model = this._state.model;\n\t\tif (!model) {\n\t\t\tthrow new Error(\"No model configured\");\n\t\t}\n\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning: ReasoningEffort | undefined =\n\t\t\tthis._state.thinkingLevel === \"off\"\n\t\t\t\t? undefined\n\t\t\t\t: this._state.thinkingLevel === \"minimal\"\n\t\t\t\t\t? \"low\"\n\t\t\t\t\t: this._state.thinkingLevel;\n\n\t\tconst cfg = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\ttools: this._state.tools,\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tgetQueuedMessages: async <T>() => {\n\t\t\t\tif (this.queueMode === \"one-at-a-time\") {\n\t\t\t\t\tif (this.messageQueue.length > 0) {\n\t\t\t\t\t\tconst first = this.messageQueue[0];\n\t\t\t\t\t\tthis.messageQueue = this.messageQueue.slice(1);\n\t\t\t\t\t\treturn [first] as QueuedMessage<T>[];\n\t\t\t\t\t}\n\t\t\t\t\treturn [];\n\t\t\t\t} else {\n\t\t\t\t\tconst queued = this.messageQueue.slice();\n\t\t\t\t\tthis.messageQueue = [];\n\t\t\t\t\treturn queued as QueuedMessage<T>[];\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\n\t\tconst llmMessages = await this.messageTransformer(this._state.messages);\n\n\t\treturn { llmMessages, cfg, model };\n\t}\n\n\t/**\n\t * Process events from the transport.\n\t */\n\tprivate async _processEvents(events: AsyncIterable<AgentEvent>) {\n\t\tconst model = this._state.model!;\n\t\tconst generatedMessages: AppMessage[] = [];\n\t\tlet partial: AppMessage | null = null;\n\n\t\ttry {\n\t\t\tfor await (const ev of events) {\n\t\t\t\tswitch (ev.type) {\n\t\t\t\t\tcase \"message_start\": {\n\t\t\t\t\t\tpartial = ev.message as AppMessage;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message as Message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_update\": {\n\t\t\t\t\t\tpartial = ev.message as AppMessage;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message as Message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_end\": {\n\t\t\t\t\t\tpartial = null;\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tthis.appendMessage(ev.message as AppMessage);\n\t\t\t\t\t\tgeneratedMessages.push(ev.message as AppMessage);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_start\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.add(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_end\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.delete(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"turn_end\": {\n\t\t\t\t\t\tif (ev.message.role === \"assistant\" && ev.message.errorMessage) {\n\t\t\t\t\t\t\tthis._state.error = ev.message.errorMessage;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"agent_end\": {\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.emit(ev as AgentEvent);\n\t\t\t}\n\n\t\t\t// Handle any remaining partial message\n\t\t\tif (partial && partial.role === \"assistant\" && partial.content.length > 0) {\n\t\t\t\tconst onlyEmpty = !partial.content.some(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\t(c.type === \"thinking\" && c.thinking.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"text\" && c.text.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"toolCall\" && c.name.trim().length > 0),\n\t\t\t\t);\n\t\t\t\tif (!onlyEmpty) {\n\t\t\t\t\tthis.appendMessage(partial as AppMessage);\n\t\t\t\t\tgeneratedMessages.push(partial as AppMessage);\n\t\t\t\t} else {\n\t\t\t\t\tif (this.abortController?.signal.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst msg: Message = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis.appendMessage(msg as AppMessage);\n\t\t\tgeneratedMessages.push(msg as AppMessage);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
|
package/dist/agent.js
CHANGED
|
@@ -139,10 +139,6 @@ export class Agent {
|
|
|
139
139
|
if (!model) {
|
|
140
140
|
throw new Error("No model configured");
|
|
141
141
|
}
|
|
142
|
-
// Set up running prompt tracking
|
|
143
|
-
this.runningPrompt = new Promise((resolve) => {
|
|
144
|
-
this.resolveRunningPrompt = resolve;
|
|
145
|
-
});
|
|
146
142
|
// Build user message with attachments
|
|
147
143
|
const content = [{ type: "text", text: input }];
|
|
148
144
|
if (attachments?.length) {
|
|
@@ -165,6 +161,50 @@ export class Agent {
|
|
|
165
161
|
attachments: attachments?.length ? attachments : undefined,
|
|
166
162
|
timestamp: Date.now(),
|
|
167
163
|
};
|
|
164
|
+
await this._runAgentLoop(userMessage);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Continue from the current context without adding a new user message.
|
|
168
|
+
* Used for retry after overflow recovery when context already has user message or tool results.
|
|
169
|
+
*/
|
|
170
|
+
async continue() {
|
|
171
|
+
const messages = this._state.messages;
|
|
172
|
+
if (messages.length === 0) {
|
|
173
|
+
throw new Error("No messages to continue from");
|
|
174
|
+
}
|
|
175
|
+
const lastMessage = messages[messages.length - 1];
|
|
176
|
+
if (lastMessage.role !== "user" && lastMessage.role !== "toolResult") {
|
|
177
|
+
throw new Error(`Cannot continue from message role: ${lastMessage.role}`);
|
|
178
|
+
}
|
|
179
|
+
await this._runAgentLoopContinue();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Internal: Run the agent loop with a new user message.
|
|
183
|
+
*/
|
|
184
|
+
async _runAgentLoop(userMessage) {
|
|
185
|
+
const { llmMessages, cfg } = await this._prepareRun();
|
|
186
|
+
const events = this.transport.run(llmMessages, userMessage, cfg, this.abortController.signal);
|
|
187
|
+
await this._processEvents(events);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Internal: Continue the agent loop from current context.
|
|
191
|
+
*/
|
|
192
|
+
async _runAgentLoopContinue() {
|
|
193
|
+
const { llmMessages, cfg } = await this._prepareRun();
|
|
194
|
+
const events = this.transport.continue(llmMessages, cfg, this.abortController.signal);
|
|
195
|
+
await this._processEvents(events);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Prepare for running the agent loop.
|
|
199
|
+
*/
|
|
200
|
+
async _prepareRun() {
|
|
201
|
+
const model = this._state.model;
|
|
202
|
+
if (!model) {
|
|
203
|
+
throw new Error("No model configured");
|
|
204
|
+
}
|
|
205
|
+
this.runningPrompt = new Promise((resolve) => {
|
|
206
|
+
this.resolveRunningPrompt = resolve;
|
|
207
|
+
});
|
|
168
208
|
this.abortController = new AbortController();
|
|
169
209
|
this._state.isStreaming = true;
|
|
170
210
|
this._state.streamMessage = null;
|
|
@@ -180,9 +220,7 @@ export class Agent {
|
|
|
180
220
|
model,
|
|
181
221
|
reasoning,
|
|
182
222
|
getQueuedMessages: async () => {
|
|
183
|
-
// Return queued messages based on queue mode
|
|
184
223
|
if (this.queueMode === "one-at-a-time") {
|
|
185
|
-
// Return only first message
|
|
186
224
|
if (this.messageQueue.length > 0) {
|
|
187
225
|
const first = this.messageQueue[0];
|
|
188
226
|
this.messageQueue = this.messageQueue.slice(1);
|
|
@@ -191,22 +229,24 @@ export class Agent {
|
|
|
191
229
|
return [];
|
|
192
230
|
}
|
|
193
231
|
else {
|
|
194
|
-
// Return all queued messages at once
|
|
195
232
|
const queued = this.messageQueue.slice();
|
|
196
233
|
this.messageQueue = [];
|
|
197
234
|
return queued;
|
|
198
235
|
}
|
|
199
236
|
},
|
|
200
237
|
};
|
|
201
|
-
|
|
238
|
+
const llmMessages = await this.messageTransformer(this._state.messages);
|
|
239
|
+
return { llmMessages, cfg, model };
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Process events from the transport.
|
|
243
|
+
*/
|
|
244
|
+
async _processEvents(events) {
|
|
245
|
+
const model = this._state.model;
|
|
202
246
|
const generatedMessages = [];
|
|
247
|
+
let partial = null;
|
|
203
248
|
try {
|
|
204
|
-
|
|
205
|
-
// Transform app messages to LLM-compatible messages (initial set)
|
|
206
|
-
const llmMessages = await this.messageTransformer(this._state.messages);
|
|
207
|
-
for await (const ev of this.transport.run(llmMessages, userMessage, cfg, this.abortController.signal)) {
|
|
208
|
-
// Update internal state BEFORE emitting events
|
|
209
|
-
// so handlers see consistent state
|
|
249
|
+
for await (const ev of events) {
|
|
210
250
|
switch (ev.type) {
|
|
211
251
|
case "message_start": {
|
|
212
252
|
partial = ev.message;
|
|
@@ -248,7 +288,6 @@ export class Agent {
|
|
|
248
288
|
break;
|
|
249
289
|
}
|
|
250
290
|
}
|
|
251
|
-
// Emit after state is updated
|
|
252
291
|
this.emit(ev);
|
|
253
292
|
}
|
|
254
293
|
// Handle any remaining partial message
|
package/dist/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, Message, QueuedMessage, ReasoningEffort, TextContent } from \"@mariozechner/pi-ai\";\nimport { getModel } from \"@mariozechner/pi-ai\";\nimport type { AgentTransport } from \"./transports/types.js\";\nimport type { AgentEvent, AgentState, AppMessage, Attachment, ThinkingLevel } from \"./types.js\";\n\n/**\n * Default message transformer: Keep only LLM-compatible messages, strip app-specific fields.\n * Converts attachments to proper content blocks (images → ImageContent, documents → TextContent).\n */\nfunction defaultMessageTransformer(messages: AppMessage[]): Message[] {\n\treturn messages\n\t\t.filter((m) => {\n\t\t\t// Only keep standard LLM message roles\n\t\t\treturn m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\";\n\t\t})\n\t\t.map((m) => {\n\t\t\tif (m.role === \"user\") {\n\t\t\t\tconst { attachments, ...rest } = m as any;\n\n\t\t\t\t// If no attachments, return as-is\n\t\t\t\tif (!attachments || attachments.length === 0) {\n\t\t\t\t\treturn rest as Message;\n\t\t\t\t}\n\n\t\t\t\t// Convert attachments to content blocks\n\t\t\t\tconst content = Array.isArray(rest.content) ? [...rest.content] : [{ type: \"text\", text: rest.content }];\n\n\t\t\t\tfor (const attachment of attachments as Attachment[]) {\n\t\t\t\t\t// Add image blocks for image attachments\n\t\t\t\t\tif (attachment.type === \"image\") {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"image\",\n\t\t\t\t\t\t\tdata: attachment.content,\n\t\t\t\t\t\t\tmimeType: attachment.mimeType,\n\t\t\t\t\t\t} as ImageContent);\n\t\t\t\t\t}\n\t\t\t\t\t// Add text blocks for documents with extracted text\n\t\t\t\t\telse if (attachment.type === \"document\" && attachment.extractedText) {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `\\n\\n[Document: ${attachment.fileName}]\\n${attachment.extractedText}`,\n\t\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t\t} as TextContent);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn { ...rest, content } as Message;\n\t\t\t}\n\t\t\treturn m as Message;\n\t\t});\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\ttransport: AgentTransport;\n\t// Transform app messages to LLM-compatible messages before sending to transport\n\tmessageTransformer?: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\t// Queue mode: \"all\" = send all queued messages at once, \"one-at-a-time\" = send one queued message per turn\n\tqueueMode?: \"all\" | \"one-at-a-time\";\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite-preview-06-17\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate transport: AgentTransport;\n\tprivate messageTransformer: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\tprivate messageQueue: Array<QueuedMessage<AppMessage>> = [];\n\tprivate queueMode: \"all\" | \"one-at-a-time\";\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\n\tconstructor(opts: AgentOptions) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.transport = opts.transport;\n\t\tthis.messageTransformer = opts.messageTransformer || defaultMessageTransformer;\n\t\tthis.queueMode = opts.queueMode || \"one-at-a-time\";\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators - update internal state without emitting events\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: typeof this._state.model) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetQueueMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.queueMode = mode;\n\t}\n\n\tgetQueueMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.queueMode;\n\t}\n\n\tsetTools(t: typeof this._state.tools) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AppMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AppMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\tasync queueMessage(m: AppMessage) {\n\t\t// Transform message and queue it for injection at next turn\n\t\tconst transformed = await this.messageTransformer([m]);\n\t\tthis.messageQueue.push({\n\t\t\toriginal: m,\n\t\t\tllm: transformed[0], // undefined if filtered out\n\t\t});\n\t}\n\n\tclearMessageQueue() {\n\t\tthis.messageQueue = [];\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\t/**\n\t * Returns a promise that resolves when the current prompt completes.\n\t * Returns immediately resolved promise if no prompt is running.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\t/**\n\t * Clear all messages and state. Call abort() first if a prompt is in flight.\n\t */\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.messageQueue = [];\n\t}\n\n\tasync prompt(input: string, attachments?: Attachment[]) {\n\t\tconst model = this._state.model;\n\t\tif (!model) {\n\t\t\tthrow new Error(\"No model configured\");\n\t\t}\n\n\t\t// Set up running prompt tracking\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\t// Build user message with attachments\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (attachments?.length) {\n\t\t\tfor (const a of attachments) {\n\t\t\t\tif (a.type === \"image\") {\n\t\t\t\t\tcontent.push({ type: \"image\", data: a.content, mimeType: a.mimeType });\n\t\t\t\t} else if (a.type === \"document\" && a.extractedText) {\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `\\n\\n[Document: ${a.fileName}]\\n${a.extractedText}`,\n\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t} as TextContent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst userMessage: AppMessage = {\n\t\t\trole: \"user\",\n\t\t\tcontent,\n\t\t\tattachments: attachments?.length ? attachments : undefined,\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning: ReasoningEffort | undefined =\n\t\t\tthis._state.thinkingLevel === \"off\"\n\t\t\t\t? undefined\n\t\t\t\t: this._state.thinkingLevel === \"minimal\"\n\t\t\t\t\t? \"low\"\n\t\t\t\t\t: this._state.thinkingLevel;\n\n\t\tconst cfg = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\ttools: this._state.tools,\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tgetQueuedMessages: async <T>() => {\n\t\t\t\t// Return queued messages based on queue mode\n\t\t\t\tif (this.queueMode === \"one-at-a-time\") {\n\t\t\t\t\t// Return only first message\n\t\t\t\t\tif (this.messageQueue.length > 0) {\n\t\t\t\t\t\tconst first = this.messageQueue[0];\n\t\t\t\t\t\tthis.messageQueue = this.messageQueue.slice(1);\n\t\t\t\t\t\treturn [first] as QueuedMessage<T>[];\n\t\t\t\t\t}\n\t\t\t\t\treturn [];\n\t\t\t\t} else {\n\t\t\t\t\t// Return all queued messages at once\n\t\t\t\t\tconst queued = this.messageQueue.slice();\n\t\t\t\t\tthis.messageQueue = [];\n\t\t\t\t\treturn queued as QueuedMessage<T>[];\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\n\t\t// Track all messages generated in this prompt\n\t\tconst generatedMessages: AppMessage[] = [];\n\n\t\ttry {\n\t\t\tlet partial: Message | null = null;\n\n\t\t\t// Transform app messages to LLM-compatible messages (initial set)\n\t\t\tconst llmMessages = await this.messageTransformer(this._state.messages);\n\n\t\t\tfor await (const ev of this.transport.run(\n\t\t\t\tllmMessages,\n\t\t\t\tuserMessage as Message,\n\t\t\t\tcfg,\n\t\t\t\tthis.abortController.signal,\n\t\t\t)) {\n\t\t\t\t// Update internal state BEFORE emitting events\n\t\t\t\t// so handlers see consistent state\n\t\t\t\tswitch (ev.type) {\n\t\t\t\t\tcase \"message_start\": {\n\t\t\t\t\t\tpartial = ev.message;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_update\": {\n\t\t\t\t\t\tpartial = ev.message;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_end\": {\n\t\t\t\t\t\tpartial = null;\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tthis.appendMessage(ev.message as AppMessage);\n\t\t\t\t\t\tgeneratedMessages.push(ev.message as AppMessage);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_start\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.add(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_end\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.delete(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"turn_end\": {\n\t\t\t\t\t\tif (ev.message.role === \"assistant\" && ev.message.errorMessage) {\n\t\t\t\t\t\t\tthis._state.error = ev.message.errorMessage;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"agent_end\": {\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Emit after state is updated\n\t\t\t\tthis.emit(ev as AgentEvent);\n\t\t\t}\n\n\t\t\t// Handle any remaining partial message\n\t\t\tif (partial && partial.role === \"assistant\" && partial.content.length > 0) {\n\t\t\t\tconst onlyEmpty = !partial.content.some(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\t(c.type === \"thinking\" && c.thinking.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"text\" && c.text.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"toolCall\" && c.name.trim().length > 0),\n\t\t\t\t);\n\t\t\t\tif (!onlyEmpty) {\n\t\t\t\t\tthis.appendMessage(partial as AppMessage);\n\t\t\t\t\tgeneratedMessages.push(partial as AppMessage);\n\t\t\t\t} else {\n\t\t\t\t\tif (this.abortController?.signal.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst msg: Message = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis.appendMessage(msg as AppMessage);\n\t\t\tgeneratedMessages.push(msg as AppMessage);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAI/C;;;GAGG;AACH,SAAS,yBAAyB,CAAC,QAAsB,EAAa;IACrE,OAAO,QAAQ;SACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACd,uCAAuC;QACvC,OAAO,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC;IAAA,CAC9E,CAAC;SACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACX,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACvB,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,CAAQ,CAAC;YAE1C,kCAAkC;YAClC,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9C,OAAO,IAAe,CAAC;YACxB,CAAC;YAED,wCAAwC;YACxC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAEzG,KAAK,MAAM,UAAU,IAAI,WAA2B,EAAE,CAAC;gBACtD,yCAAyC;gBACzC,IAAI,UAAU,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACjC,OAAO,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,OAAO;wBACb,IAAI,EAAE,UAAU,CAAC,OAAO;wBACxB,QAAQ,EAAE,UAAU,CAAC,QAAQ;qBACb,CAAC,CAAC;gBACpB,CAAC;gBACD,oDAAoD;qBAC/C,IAAI,UAAU,CAAC,IAAI,KAAK,UAAU,IAAI,UAAU,CAAC,aAAa,EAAE,CAAC;oBACrE,OAAO,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,kBAAkB,UAAU,CAAC,QAAQ,MAAM,UAAU,CAAC,aAAa,EAAE;wBAC3E,UAAU,EAAE,IAAI;qBACD,CAAC,CAAC;gBACnB,CAAC;YACF,CAAC;YAED,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAa,CAAC;QACxC,CAAC;QACD,OAAO,CAAY,CAAC;IAAA,CACpB,CAAC,CAAC;AAAA,CACJ;AAWD,MAAM,OAAO,KAAK;IACT,MAAM,GAAe;QAC5B,YAAY,EAAE,EAAE;QAChB,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,qCAAqC,CAAC;QAChE,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,KAAK;QAClB,aAAa,EAAE,IAAI;QACnB,gBAAgB,EAAE,IAAI,GAAG,EAAU;QACnC,KAAK,EAAE,SAAS;KAChB,CAAC;IACM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC/C,eAAe,CAAmB;IAClC,SAAS,CAAiB;IAC1B,kBAAkB,CAA6D;IAC/E,YAAY,GAAqC,EAAE,CAAC;IACpD,SAAS,CAA0B;IACnC,aAAa,CAAiB;IAC9B,oBAAoB,CAAc;IAE1C,YAAY,IAAkB,EAAE;QAC/B,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACvD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,IAAI,yBAAyB,CAAC;QAC/E,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,eAAe,CAAC;IAAA,CACnD;IAED,IAAI,KAAK,GAAe;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC;IAAA,CACnB;IAED,SAAS,CAAC,EAA2B,EAAc;QAClD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAAA,CACvC;IAED,iEAAiE;IACjE,eAAe,CAAC,CAAS,EAAE;QAC1B,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;IAAA,CAC7B;IAED,QAAQ,CAAC,CAA2B,EAAE;QACrC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;IAAA,CACtB;IAED,gBAAgB,CAAC,CAAgB,EAAE;QAClC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC;IAAA,CAC9B;IAED,YAAY,CAAC,IAA6B,EAAE;QAC3C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAAA,CACtB;IAED,YAAY,GAA4B;QACvC,OAAO,IAAI,CAAC,SAAS,CAAC;IAAA,CACtB;IAED,QAAQ,CAAC,CAA2B,EAAE;QACrC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;IAAA,CACtB;IAED,eAAe,CAAC,EAAgB,EAAE;QACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;IAAA,CAClC;IAED,aAAa,CAAC,CAAa,EAAE;QAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAAA,CACpD;IAED,KAAK,CAAC,YAAY,CAAC,CAAa,EAAE;QACjC,4DAA4D;QAC5D,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;YACtB,QAAQ,EAAE,CAAC;YACX,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,4BAA4B;SACjD,CAAC,CAAC;IAAA,CACH;IAED,iBAAiB,GAAG;QACnB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IAAA,CACvB;IAED,aAAa,GAAG;QACf,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;IAAA,CAC1B;IAED,KAAK,GAAG;QACP,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,CAAC;IAAA,CAC9B;IAED;;;OAGG;IACH,WAAW,GAAkB;QAC5B,OAAO,IAAI,CAAC,aAAa,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAAA,CAC/C;IAED;;OAEG;IACH,KAAK,GAAG;QACP,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC;QAC9B,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IAAA,CACvB;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,WAA0B,EAAE;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACxC,CAAC;QAED,sCAAsC;QACtC,MAAM,OAAO,GAAsC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACnF,IAAI,WAAW,EAAE,MAAM,EAAE,CAAC;YACzB,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;gBAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACxB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACxE,CAAC;qBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;oBACrD,OAAO,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,kBAAkB,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,aAAa,EAAE;wBACzD,UAAU,EAAE,IAAI;qBACD,CAAC,CAAC;gBACnB,CAAC;YACF,CAAC;QACF,CAAC;QAED,MAAM,WAAW,GAAe;YAC/B,IAAI,EAAE,MAAM;YACZ,OAAO;YACP,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;YAC1D,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACrB,CAAC;QAEF,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAAA,CACtC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ,GAAG;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAClD,IAAI,WAAW,CAAC,IAAI,KAAK,MAAM,IAAI,WAAW,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,sCAAsC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;IAAA,CACnC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa,CAAC,WAAuB,EAAE;QACpD,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAEtD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,WAAsB,EAAE,GAAG,EAAE,IAAI,CAAC,eAAgB,CAAC,MAAM,CAAC,CAAC;QAE1G,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAAA,CAClC;IAED;;OAEG;IACK,KAAK,CAAC,qBAAqB,GAAG;QACrC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAEtD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,CAAC,eAAgB,CAAC,MAAM,CAAC,CAAC;QAEvF,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAAA,CAClC;IAED;;OAEG;IACK,KAAK,CAAC,WAAW,GAAG;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACxC,CAAC;QAED,IAAI,CAAC,aAAa,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACnD,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC;QAAA,CACpC,CAAC,CAAC;QAEH,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC;QAE9B,MAAM,SAAS,GACd,IAAI,CAAC,MAAM,CAAC,aAAa,KAAK,KAAK;YAClC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS;gBACxC,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;QAE/B,MAAM,GAAG,GAAG;YACX,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;YACtC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,KAAK;YACL,SAAS;YACT,iBAAiB,EAAE,KAAK,IAAO,EAAE,CAAC;gBACjC,IAAI,IAAI,CAAC,SAAS,KAAK,eAAe,EAAE,CAAC;oBACxC,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAClC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;wBACnC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;wBAC/C,OAAO,CAAC,KAAK,CAAuB,CAAC;oBACtC,CAAC;oBACD,OAAO,EAAE,CAAC;gBACX,CAAC;qBAAM,CAAC;oBACP,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;oBACzC,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;oBACvB,OAAO,MAA4B,CAAC;gBACrC,CAAC;YAAA,CACD;SACD,CAAC;QAEF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAExE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAAA,CACnC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CAAC,MAAiC,EAAE;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAM,CAAC;QACjC,MAAM,iBAAiB,GAAiB,EAAE,CAAC;QAC3C,IAAI,OAAO,GAAsB,IAAI,CAAC;QAEtC,IAAI,CAAC;YACJ,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;gBAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;oBACjB,KAAK,eAAe,EAAE,CAAC;wBACtB,OAAO,GAAG,EAAE,CAAC,OAAqB,CAAC;wBACnC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,EAAE,CAAC,OAAkB,CAAC;wBAClD,MAAM;oBACP,CAAC;oBACD,KAAK,gBAAgB,EAAE,CAAC;wBACvB,OAAO,GAAG,EAAE,CAAC,OAAqB,CAAC;wBACnC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,EAAE,CAAC,OAAkB,CAAC;wBAClD,MAAM;oBACP,CAAC;oBACD,KAAK,aAAa,EAAE,CAAC;wBACpB,OAAO,GAAG,IAAI,CAAC;wBACf,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;wBACjC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,OAAqB,CAAC,CAAC;wBAC7C,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,OAAqB,CAAC,CAAC;wBACjD,MAAM;oBACP,CAAC;oBACD,KAAK,sBAAsB,EAAE,CAAC;wBAC7B,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;wBAChD,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;wBACrB,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,CAAC,CAAC;wBACjC,MAAM;oBACP,CAAC;oBACD,KAAK,oBAAoB,EAAE,CAAC;wBAC3B,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;wBAChD,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;wBACxB,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,CAAC,CAAC;wBACjC,MAAM;oBACP,CAAC;oBACD,KAAK,UAAU,EAAE,CAAC;wBACjB,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;4BAChE,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;wBAC7C,CAAC;wBACD,MAAM;oBACP,CAAC;oBACD,KAAK,WAAW,EAAE,CAAC;wBAClB,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;wBACjC,MAAM;oBACP,CAAC;gBACF,CAAC;gBAED,IAAI,CAAC,IAAI,CAAC,EAAgB,CAAC,CAAC;YAC7B,CAAC;YAED,uCAAuC;YACvC,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3E,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,EAAE,CACL,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;oBACvD,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;oBAC/C,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CACpD,CAAC;gBACF,IAAI,CAAC,SAAS,EAAE,CAAC;oBAChB,IAAI,CAAC,aAAa,CAAC,OAAqB,CAAC,CAAC;oBAC1C,iBAAiB,CAAC,IAAI,CAAC,OAAqB,CAAC,CAAC;gBAC/C,CAAC;qBAAM,CAAC;oBACP,IAAI,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;wBAC1C,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;oBACxC,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YACnB,MAAM,GAAG,GAAY;gBACpB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;gBACrC,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,KAAK,EAAE,KAAK,CAAC,EAAE;gBACf,KAAK,EAAE;oBACN,KAAK,EAAE,CAAC;oBACR,MAAM,EAAE,CAAC;oBACT,SAAS,EAAE,CAAC;oBACZ,UAAU,EAAE,CAAC;oBACb,WAAW,EAAE,CAAC;oBACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;iBACpE;gBACD,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;gBACtE,YAAY,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;gBACzC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACrB,CAAC;YACF,IAAI,CAAC,aAAa,CAAC,GAAiB,CAAC,CAAC;YACtC,iBAAiB,CAAC,IAAI,CAAC,GAAiB,CAAC,CAAC;YAC1C,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;QACjD,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;YACjD,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YACjC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;YAC9B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;QACvC,CAAC;IAAA,CACD;IAEO,IAAI,CAAC,CAAa,EAAE;QAC3B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACvC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACb,CAAC;IAAA,CACD;CACD","sourcesContent":["import type { ImageContent, Message, QueuedMessage, ReasoningEffort, TextContent } from \"@mariozechner/pi-ai\";\nimport { getModel } from \"@mariozechner/pi-ai\";\nimport type { AgentTransport } from \"./transports/types.js\";\nimport type { AgentEvent, AgentState, AppMessage, Attachment, ThinkingLevel } from \"./types.js\";\n\n/**\n * Default message transformer: Keep only LLM-compatible messages, strip app-specific fields.\n * Converts attachments to proper content blocks (images → ImageContent, documents → TextContent).\n */\nfunction defaultMessageTransformer(messages: AppMessage[]): Message[] {\n\treturn messages\n\t\t.filter((m) => {\n\t\t\t// Only keep standard LLM message roles\n\t\t\treturn m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\";\n\t\t})\n\t\t.map((m) => {\n\t\t\tif (m.role === \"user\") {\n\t\t\t\tconst { attachments, ...rest } = m as any;\n\n\t\t\t\t// If no attachments, return as-is\n\t\t\t\tif (!attachments || attachments.length === 0) {\n\t\t\t\t\treturn rest as Message;\n\t\t\t\t}\n\n\t\t\t\t// Convert attachments to content blocks\n\t\t\t\tconst content = Array.isArray(rest.content) ? [...rest.content] : [{ type: \"text\", text: rest.content }];\n\n\t\t\t\tfor (const attachment of attachments as Attachment[]) {\n\t\t\t\t\t// Add image blocks for image attachments\n\t\t\t\t\tif (attachment.type === \"image\") {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"image\",\n\t\t\t\t\t\t\tdata: attachment.content,\n\t\t\t\t\t\t\tmimeType: attachment.mimeType,\n\t\t\t\t\t\t} as ImageContent);\n\t\t\t\t\t}\n\t\t\t\t\t// Add text blocks for documents with extracted text\n\t\t\t\t\telse if (attachment.type === \"document\" && attachment.extractedText) {\n\t\t\t\t\t\tcontent.push({\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `\\n\\n[Document: ${attachment.fileName}]\\n${attachment.extractedText}`,\n\t\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t\t} as TextContent);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn { ...rest, content } as Message;\n\t\t\t}\n\t\t\treturn m as Message;\n\t\t});\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\ttransport: AgentTransport;\n\t// Transform app messages to LLM-compatible messages before sending to transport\n\tmessageTransformer?: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\t// Queue mode: \"all\" = send all queued messages at once, \"one-at-a-time\" = send one queued message per turn\n\tqueueMode?: \"all\" | \"one-at-a-time\";\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite-preview-06-17\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate transport: AgentTransport;\n\tprivate messageTransformer: (messages: AppMessage[]) => Message[] | Promise<Message[]>;\n\tprivate messageQueue: Array<QueuedMessage<AppMessage>> = [];\n\tprivate queueMode: \"all\" | \"one-at-a-time\";\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\n\tconstructor(opts: AgentOptions) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.transport = opts.transport;\n\t\tthis.messageTransformer = opts.messageTransformer || defaultMessageTransformer;\n\t\tthis.queueMode = opts.queueMode || \"one-at-a-time\";\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators - update internal state without emitting events\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: typeof this._state.model) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetQueueMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.queueMode = mode;\n\t}\n\n\tgetQueueMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.queueMode;\n\t}\n\n\tsetTools(t: typeof this._state.tools) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AppMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AppMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\tasync queueMessage(m: AppMessage) {\n\t\t// Transform message and queue it for injection at next turn\n\t\tconst transformed = await this.messageTransformer([m]);\n\t\tthis.messageQueue.push({\n\t\t\toriginal: m,\n\t\t\tllm: transformed[0], // undefined if filtered out\n\t\t});\n\t}\n\n\tclearMessageQueue() {\n\t\tthis.messageQueue = [];\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\t/**\n\t * Returns a promise that resolves when the current prompt completes.\n\t * Returns immediately resolved promise if no prompt is running.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\t/**\n\t * Clear all messages and state. Call abort() first if a prompt is in flight.\n\t */\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.messageQueue = [];\n\t}\n\n\tasync prompt(input: string, attachments?: Attachment[]) {\n\t\tconst model = this._state.model;\n\t\tif (!model) {\n\t\t\tthrow new Error(\"No model configured\");\n\t\t}\n\n\t\t// Build user message with attachments\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (attachments?.length) {\n\t\t\tfor (const a of attachments) {\n\t\t\t\tif (a.type === \"image\") {\n\t\t\t\t\tcontent.push({ type: \"image\", data: a.content, mimeType: a.mimeType });\n\t\t\t\t} else if (a.type === \"document\" && a.extractedText) {\n\t\t\t\t\tcontent.push({\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `\\n\\n[Document: ${a.fileName}]\\n${a.extractedText}`,\n\t\t\t\t\t\tisDocument: true,\n\t\t\t\t\t} as TextContent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst userMessage: AppMessage = {\n\t\t\trole: \"user\",\n\t\t\tcontent,\n\t\t\tattachments: attachments?.length ? attachments : undefined,\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tawait this._runAgentLoop(userMessage);\n\t}\n\n\t/**\n\t * Continue from the current context without adding a new user message.\n\t * Used for retry after overflow recovery when context already has user message or tool results.\n\t */\n\tasync continue() {\n\t\tconst messages = this._state.messages;\n\t\tif (messages.length === 0) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\n\t\tconst lastMessage = messages[messages.length - 1];\n\t\tif (lastMessage.role !== \"user\" && lastMessage.role !== \"toolResult\") {\n\t\t\tthrow new Error(`Cannot continue from message role: ${lastMessage.role}`);\n\t\t}\n\n\t\tawait this._runAgentLoopContinue();\n\t}\n\n\t/**\n\t * Internal: Run the agent loop with a new user message.\n\t */\n\tprivate async _runAgentLoop(userMessage: AppMessage) {\n\t\tconst { llmMessages, cfg } = await this._prepareRun();\n\n\t\tconst events = this.transport.run(llmMessages, userMessage as Message, cfg, this.abortController!.signal);\n\n\t\tawait this._processEvents(events);\n\t}\n\n\t/**\n\t * Internal: Continue the agent loop from current context.\n\t */\n\tprivate async _runAgentLoopContinue() {\n\t\tconst { llmMessages, cfg } = await this._prepareRun();\n\n\t\tconst events = this.transport.continue(llmMessages, cfg, this.abortController!.signal);\n\n\t\tawait this._processEvents(events);\n\t}\n\n\t/**\n\t * Prepare for running the agent loop.\n\t */\n\tprivate async _prepareRun() {\n\t\tconst model = this._state.model;\n\t\tif (!model) {\n\t\t\tthrow new Error(\"No model configured\");\n\t\t}\n\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning: ReasoningEffort | undefined =\n\t\t\tthis._state.thinkingLevel === \"off\"\n\t\t\t\t? undefined\n\t\t\t\t: this._state.thinkingLevel === \"minimal\"\n\t\t\t\t\t? \"low\"\n\t\t\t\t\t: this._state.thinkingLevel;\n\n\t\tconst cfg = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\ttools: this._state.tools,\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tgetQueuedMessages: async <T>() => {\n\t\t\t\tif (this.queueMode === \"one-at-a-time\") {\n\t\t\t\t\tif (this.messageQueue.length > 0) {\n\t\t\t\t\t\tconst first = this.messageQueue[0];\n\t\t\t\t\t\tthis.messageQueue = this.messageQueue.slice(1);\n\t\t\t\t\t\treturn [first] as QueuedMessage<T>[];\n\t\t\t\t\t}\n\t\t\t\t\treturn [];\n\t\t\t\t} else {\n\t\t\t\t\tconst queued = this.messageQueue.slice();\n\t\t\t\t\tthis.messageQueue = [];\n\t\t\t\t\treturn queued as QueuedMessage<T>[];\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\n\t\tconst llmMessages = await this.messageTransformer(this._state.messages);\n\n\t\treturn { llmMessages, cfg, model };\n\t}\n\n\t/**\n\t * Process events from the transport.\n\t */\n\tprivate async _processEvents(events: AsyncIterable<AgentEvent>) {\n\t\tconst model = this._state.model!;\n\t\tconst generatedMessages: AppMessage[] = [];\n\t\tlet partial: AppMessage | null = null;\n\n\t\ttry {\n\t\t\tfor await (const ev of events) {\n\t\t\t\tswitch (ev.type) {\n\t\t\t\t\tcase \"message_start\": {\n\t\t\t\t\t\tpartial = ev.message as AppMessage;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message as Message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_update\": {\n\t\t\t\t\t\tpartial = ev.message as AppMessage;\n\t\t\t\t\t\tthis._state.streamMessage = ev.message as Message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"message_end\": {\n\t\t\t\t\t\tpartial = null;\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tthis.appendMessage(ev.message as AppMessage);\n\t\t\t\t\t\tgeneratedMessages.push(ev.message as AppMessage);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_start\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.add(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"tool_execution_end\": {\n\t\t\t\t\t\tconst s = new Set(this._state.pendingToolCalls);\n\t\t\t\t\t\ts.delete(ev.toolCallId);\n\t\t\t\t\t\tthis._state.pendingToolCalls = s;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"turn_end\": {\n\t\t\t\t\t\tif (ev.message.role === \"assistant\" && ev.message.errorMessage) {\n\t\t\t\t\t\t\tthis._state.error = ev.message.errorMessage;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"agent_end\": {\n\t\t\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.emit(ev as AgentEvent);\n\t\t\t}\n\n\t\t\t// Handle any remaining partial message\n\t\t\tif (partial && partial.role === \"assistant\" && partial.content.length > 0) {\n\t\t\t\tconst onlyEmpty = !partial.content.some(\n\t\t\t\t\t(c) =>\n\t\t\t\t\t\t(c.type === \"thinking\" && c.thinking.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"text\" && c.text.trim().length > 0) ||\n\t\t\t\t\t\t(c.type === \"toolCall\" && c.name.trim().length > 0),\n\t\t\t\t);\n\t\t\t\tif (!onlyEmpty) {\n\t\t\t\t\tthis.appendMessage(partial as AppMessage);\n\t\t\t\t\tgeneratedMessages.push(partial as AppMessage);\n\t\t\t\t} else {\n\t\t\t\t\tif (this.abortController?.signal.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst msg: Message = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis.appendMessage(msg as AppMessage);\n\t\t\tgeneratedMessages.push(msg as AppMessage);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -19,6 +19,10 @@ export interface AppTransportOptions {
|
|
|
19
19
|
export declare class AppTransport implements AgentTransport {
|
|
20
20
|
private options;
|
|
21
21
|
constructor(options: AppTransportOptions);
|
|
22
|
+
private getStreamFn;
|
|
23
|
+
private buildContext;
|
|
24
|
+
private buildLoopConfig;
|
|
22
25
|
run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal): AsyncGenerator<import("@mariozechner/pi-ai").AgentEvent, void, unknown>;
|
|
26
|
+
continue(messages: Message[], cfg: AgentRunConfig, signal?: AbortSignal): AsyncGenerator<import("@mariozechner/pi-ai").AgentEvent, void, unknown>;
|
|
23
27
|
}
|
|
24
28
|
//# sourceMappingURL=AppTransport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppTransport.d.ts","sourceRoot":"","sources":["../../src/transports/AppTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAOX,OAAO,EAKP,MAAM,qBAAqB,CAAC;AAK7B,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAuSjE,MAAM,WAAW,mBAAmB;IACnC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC7C;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,cAAc;IAClD,OAAO,CAAC,OAAO,CAAsB;IAErC,YAAY,OAAO,EAAE,mBAAmB,EAEvC;IAEM,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,WAAW,2EAqC9F;CACD","sourcesContent":["import type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tToolCall,\n\tUserMessage,\n} from \"@mariozechner/pi-ai\";\nimport { agentLoop } from \"@mariozechner/pi-ai\";\nimport { AssistantMessageEventStream } from \"@mariozechner/pi-ai/dist/utils/event-stream.js\";\nimport { parseStreamingJson } from \"@mariozechner/pi-ai/dist/utils/json-parse.js\";\nimport type { ProxyAssistantMessageEvent } from \"./proxy-types.js\";\nimport type { AgentRunConfig, AgentTransport } from \"./types.js\";\n\n/**\n * Stream function that proxies through a server instead of calling providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n */\nfunction streamSimpleProxy(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions: SimpleStreamOptions & { authToken: string },\n\tproxyUrl: string,\n): AssistantMessageEventStream {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\t// Set up abort handler to cancel the reader\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: {\n\t\t\t\t\t\ttemperature: options.temperature,\n\t\t\t\t\t\tmaxTokens: options.maxTokens,\n\t\t\t\t\t\treasoning: options.reasoning,\n\t\t\t\t\t\t// Don't send apiKey or signal - those are added server-side\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response, use default message\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\t// Parse SSE stream\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\t// Check if aborted after reading\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tlet event: AssistantMessageEvent | undefined;\n\n\t\t\t\t\t\t\t// Handle different event types\n\t\t\t\t\t\t\t// Server sends events with partial for non-delta events,\n\t\t\t\t\t\t\t// and without partial for delta events\n\t\t\t\t\t\t\tswitch (proxyEvent.type) {\n\t\t\t\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\t\t\t\tevent = { type: \"start\", partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcase \"text_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\t\t\t\tthinking: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"thinking_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\t\t\tid: proxyEvent.id,\n\t\t\t\t\t\t\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\t\t\t\t\t\t\targuments: {},\n\t\t\t\t\t\t\t\t\t\tpartialJson: \"\",\n\t\t\t\t\t\t\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\t\t\t\t\t\t\tevent = { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"toolcall_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\ttoolCall: content,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"done\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t// Exhaustive check\n\t\t\t\t\t\t\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\t\t\t\t\t\t\tconsole.warn(`Unhandled event type: ${(proxyEvent as any).type}`);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Push the event to stream\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new Error(\"Failed to create event from proxy event\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check if aborted after reading\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tpartial.stopReason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: partial.stopReason,\n\t\t\t\terror: partial,\n\t\t\t} satisfies AssistantMessageEvent);\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\t// Clean up abort handler\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nexport interface AppTransportOptions {\n\t/**\n\t * Proxy server URL. The server manages user accounts and proxies requests to LLM providers.\n\t * Example: \"https://genai.mariozechner.at\"\n\t */\n\tproxyUrl: string;\n\n\t/**\n\t * Function to retrieve auth token for the proxy server.\n\t * The token is used for user authentication and authorization.\n\t */\n\tgetAuthToken: () => Promise<string> | string;\n}\n\n/**\n * Transport that uses an app server with user authentication tokens.\n * The server manages user accounts and proxies requests to LLM providers.\n */\nexport class AppTransport implements AgentTransport {\n\tprivate options: AppTransportOptions;\n\n\tconstructor(options: AppTransportOptions) {\n\t\tthis.options = options;\n\t}\n\n\tasync *run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst authToken = await this.options.getAuthToken();\n\t\tif (!authToken) {\n\t\t\tthrow new Error(\"Auth token is required for AppTransport\");\n\t\t}\n\n\t\t// Use proxy - no local API key needed\n\t\tconst streamFn = <TApi extends Api>(model: Model<TApi>, context: Context, options?: SimpleStreamOptions) => {\n\t\t\treturn streamSimpleProxy(\n\t\t\t\tmodel,\n\t\t\t\tcontext,\n\t\t\t\t{\n\t\t\t\t\t...options,\n\t\t\t\t\tauthToken,\n\t\t\t\t},\n\t\t\t\tthis.options.proxyUrl,\n\t\t\t);\n\t\t};\n\n\t\t// Messages are already LLM-compatible (filtered by Agent)\n\t\tconst context: AgentContext = {\n\t\t\tsystemPrompt: cfg.systemPrompt,\n\t\t\tmessages,\n\t\t\ttools: cfg.tools,\n\t\t};\n\n\t\tconst pc: AgentLoopConfig = {\n\t\t\tmodel: cfg.model,\n\t\t\treasoning: cfg.reasoning,\n\t\t\tgetQueuedMessages: cfg.getQueuedMessages,\n\t\t};\n\n\t\t// Yield events from the upstream agentLoop iterator\n\t\t// Pass streamFn as the 5th parameter to use proxy\n\t\tfor await (const ev of agentLoop(userMessage as unknown as UserMessage, context, pc, signal, streamFn as any)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AppTransport.d.ts","sourceRoot":"","sources":["../../src/transports/AppTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAOX,OAAO,EAKP,MAAM,qBAAqB,CAAC;AAK7B,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAuSjE,MAAM,WAAW,mBAAmB;IACnC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CAC7C;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,cAAc;IAClD,OAAO,CAAC,OAAO,CAAsB;IAErC,YAAY,OAAO,EAAE,mBAAmB,EAEvC;YAEa,WAAW;IAczB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,eAAe;IAQhB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,WAAW,2EAa9F;IAEM,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,WAAW,2EAa7E;CACD","sourcesContent":["import type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tToolCall,\n\tUserMessage,\n} from \"@mariozechner/pi-ai\";\nimport { agentLoop, agentLoopContinue } from \"@mariozechner/pi-ai\";\nimport { AssistantMessageEventStream } from \"@mariozechner/pi-ai/dist/utils/event-stream.js\";\nimport { parseStreamingJson } from \"@mariozechner/pi-ai/dist/utils/json-parse.js\";\nimport type { ProxyAssistantMessageEvent } from \"./proxy-types.js\";\nimport type { AgentRunConfig, AgentTransport } from \"./types.js\";\n\n/**\n * Stream function that proxies through a server instead of calling providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n */\nfunction streamSimpleProxy(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions: SimpleStreamOptions & { authToken: string },\n\tproxyUrl: string,\n): AssistantMessageEventStream {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\t// Set up abort handler to cancel the reader\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: {\n\t\t\t\t\t\ttemperature: options.temperature,\n\t\t\t\t\t\tmaxTokens: options.maxTokens,\n\t\t\t\t\t\treasoning: options.reasoning,\n\t\t\t\t\t\t// Don't send apiKey or signal - those are added server-side\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response, use default message\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\t// Parse SSE stream\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\t// Check if aborted after reading\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tlet event: AssistantMessageEvent | undefined;\n\n\t\t\t\t\t\t\t// Handle different event types\n\t\t\t\t\t\t\t// Server sends events with partial for non-delta events,\n\t\t\t\t\t\t\t// and without partial for delta events\n\t\t\t\t\t\t\tswitch (proxyEvent.type) {\n\t\t\t\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\t\t\t\tevent = { type: \"start\", partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcase \"text_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\t\t\t\tthinking: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"thinking_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\t\t\tid: proxyEvent.id,\n\t\t\t\t\t\t\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\t\t\t\t\t\t\targuments: {},\n\t\t\t\t\t\t\t\t\t\tpartialJson: \"\",\n\t\t\t\t\t\t\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\t\t\t\t\t\t\tevent = { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"toolcall_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\ttoolCall: content,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"done\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t// Exhaustive check\n\t\t\t\t\t\t\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\t\t\t\t\t\t\tconsole.warn(`Unhandled event type: ${(proxyEvent as any).type}`);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Push the event to stream\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new Error(\"Failed to create event from proxy event\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check if aborted after reading\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tpartial.stopReason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: partial.stopReason,\n\t\t\t\terror: partial,\n\t\t\t} satisfies AssistantMessageEvent);\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\t// Clean up abort handler\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nexport interface AppTransportOptions {\n\t/**\n\t * Proxy server URL. The server manages user accounts and proxies requests to LLM providers.\n\t * Example: \"https://genai.mariozechner.at\"\n\t */\n\tproxyUrl: string;\n\n\t/**\n\t * Function to retrieve auth token for the proxy server.\n\t * The token is used for user authentication and authorization.\n\t */\n\tgetAuthToken: () => Promise<string> | string;\n}\n\n/**\n * Transport that uses an app server with user authentication tokens.\n * The server manages user accounts and proxies requests to LLM providers.\n */\nexport class AppTransport implements AgentTransport {\n\tprivate options: AppTransportOptions;\n\n\tconstructor(options: AppTransportOptions) {\n\t\tthis.options = options;\n\t}\n\n\tprivate async getStreamFn(authToken: string) {\n\t\treturn <TApi extends Api>(model: Model<TApi>, context: Context, options?: SimpleStreamOptions) => {\n\t\t\treturn streamSimpleProxy(\n\t\t\t\tmodel,\n\t\t\t\tcontext,\n\t\t\t\t{\n\t\t\t\t\t...options,\n\t\t\t\t\tauthToken,\n\t\t\t\t},\n\t\t\t\tthis.options.proxyUrl,\n\t\t\t);\n\t\t};\n\t}\n\n\tprivate buildContext(messages: Message[], cfg: AgentRunConfig): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: cfg.systemPrompt,\n\t\t\tmessages,\n\t\t\ttools: cfg.tools,\n\t\t};\n\t}\n\n\tprivate buildLoopConfig(cfg: AgentRunConfig): AgentLoopConfig {\n\t\treturn {\n\t\t\tmodel: cfg.model,\n\t\t\treasoning: cfg.reasoning,\n\t\t\tgetQueuedMessages: cfg.getQueuedMessages,\n\t\t};\n\t}\n\n\tasync *run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst authToken = await this.options.getAuthToken();\n\t\tif (!authToken) {\n\t\t\tthrow new Error(\"Auth token is required for AppTransport\");\n\t\t}\n\n\t\tconst streamFn = await this.getStreamFn(authToken);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(cfg);\n\n\t\tfor await (const ev of agentLoop(userMessage as unknown as UserMessage, context, pc, signal, streamFn as any)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n\n\tasync *continue(messages: Message[], cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst authToken = await this.options.getAuthToken();\n\t\tif (!authToken) {\n\t\t\tthrow new Error(\"Auth token is required for AppTransport\");\n\t\t}\n\n\t\tconst streamFn = await this.getStreamFn(authToken);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(cfg);\n\n\t\tfor await (const ev of agentLoopContinue(context, pc, signal, streamFn as any)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { agentLoop } from "@mariozechner/pi-ai";
|
|
1
|
+
import { agentLoop, agentLoopContinue } from "@mariozechner/pi-ai";
|
|
2
2
|
import { AssistantMessageEventStream } from "@mariozechner/pi-ai/dist/utils/event-stream.js";
|
|
3
3
|
import { parseStreamingJson } from "@mariozechner/pi-ai/dist/utils/json-parse.js";
|
|
4
4
|
/**
|
|
@@ -280,34 +280,51 @@ export class AppTransport {
|
|
|
280
280
|
constructor(options) {
|
|
281
281
|
this.options = options;
|
|
282
282
|
}
|
|
283
|
-
async
|
|
284
|
-
|
|
285
|
-
if (!authToken) {
|
|
286
|
-
throw new Error("Auth token is required for AppTransport");
|
|
287
|
-
}
|
|
288
|
-
// Use proxy - no local API key needed
|
|
289
|
-
const streamFn = (model, context, options) => {
|
|
283
|
+
async getStreamFn(authToken) {
|
|
284
|
+
return (model, context, options) => {
|
|
290
285
|
return streamSimpleProxy(model, context, {
|
|
291
286
|
...options,
|
|
292
287
|
authToken,
|
|
293
288
|
}, this.options.proxyUrl);
|
|
294
289
|
};
|
|
295
|
-
|
|
296
|
-
|
|
290
|
+
}
|
|
291
|
+
buildContext(messages, cfg) {
|
|
292
|
+
return {
|
|
297
293
|
systemPrompt: cfg.systemPrompt,
|
|
298
294
|
messages,
|
|
299
295
|
tools: cfg.tools,
|
|
300
296
|
};
|
|
301
|
-
|
|
297
|
+
}
|
|
298
|
+
buildLoopConfig(cfg) {
|
|
299
|
+
return {
|
|
302
300
|
model: cfg.model,
|
|
303
301
|
reasoning: cfg.reasoning,
|
|
304
302
|
getQueuedMessages: cfg.getQueuedMessages,
|
|
305
303
|
};
|
|
306
|
-
|
|
307
|
-
|
|
304
|
+
}
|
|
305
|
+
async *run(messages, userMessage, cfg, signal) {
|
|
306
|
+
const authToken = await this.options.getAuthToken();
|
|
307
|
+
if (!authToken) {
|
|
308
|
+
throw new Error("Auth token is required for AppTransport");
|
|
309
|
+
}
|
|
310
|
+
const streamFn = await this.getStreamFn(authToken);
|
|
311
|
+
const context = this.buildContext(messages, cfg);
|
|
312
|
+
const pc = this.buildLoopConfig(cfg);
|
|
308
313
|
for await (const ev of agentLoop(userMessage, context, pc, signal, streamFn)) {
|
|
309
314
|
yield ev;
|
|
310
315
|
}
|
|
311
316
|
}
|
|
317
|
+
async *continue(messages, cfg, signal) {
|
|
318
|
+
const authToken = await this.options.getAuthToken();
|
|
319
|
+
if (!authToken) {
|
|
320
|
+
throw new Error("Auth token is required for AppTransport");
|
|
321
|
+
}
|
|
322
|
+
const streamFn = await this.getStreamFn(authToken);
|
|
323
|
+
const context = this.buildContext(messages, cfg);
|
|
324
|
+
const pc = this.buildLoopConfig(cfg);
|
|
325
|
+
for await (const ev of agentLoopContinue(context, pc, signal, streamFn)) {
|
|
326
|
+
yield ev;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
312
329
|
}
|
|
313
330
|
//# sourceMappingURL=AppTransport.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppTransport.js","sourceRoot":"","sources":["../../src/transports/AppTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tToolCall,\n\tUserMessage,\n} from \"@mariozechner/pi-ai\";\nimport { agentLoop } from \"@mariozechner/pi-ai\";\nimport { AssistantMessageEventStream } from \"@mariozechner/pi-ai/dist/utils/event-stream.js\";\nimport { parseStreamingJson } from \"@mariozechner/pi-ai/dist/utils/json-parse.js\";\nimport type { ProxyAssistantMessageEvent } from \"./proxy-types.js\";\nimport type { AgentRunConfig, AgentTransport } from \"./types.js\";\n\n/**\n * Stream function that proxies through a server instead of calling providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n */\nfunction streamSimpleProxy(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions: SimpleStreamOptions & { authToken: string },\n\tproxyUrl: string,\n): AssistantMessageEventStream {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\t// Set up abort handler to cancel the reader\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: {\n\t\t\t\t\t\ttemperature: options.temperature,\n\t\t\t\t\t\tmaxTokens: options.maxTokens,\n\t\t\t\t\t\treasoning: options.reasoning,\n\t\t\t\t\t\t// Don't send apiKey or signal - those are added server-side\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response, use default message\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\t// Parse SSE stream\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\t// Check if aborted after reading\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tlet event: AssistantMessageEvent | undefined;\n\n\t\t\t\t\t\t\t// Handle different event types\n\t\t\t\t\t\t\t// Server sends events with partial for non-delta events,\n\t\t\t\t\t\t\t// and without partial for delta events\n\t\t\t\t\t\t\tswitch (proxyEvent.type) {\n\t\t\t\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\t\t\t\tevent = { type: \"start\", partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcase \"text_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\t\t\t\tthinking: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"thinking_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\t\t\tid: proxyEvent.id,\n\t\t\t\t\t\t\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\t\t\t\t\t\t\targuments: {},\n\t\t\t\t\t\t\t\t\t\tpartialJson: \"\",\n\t\t\t\t\t\t\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\t\t\t\t\t\t\tevent = { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"toolcall_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\ttoolCall: content,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"done\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t// Exhaustive check\n\t\t\t\t\t\t\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\t\t\t\t\t\t\tconsole.warn(`Unhandled event type: ${(proxyEvent as any).type}`);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Push the event to stream\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new Error(\"Failed to create event from proxy event\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check if aborted after reading\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tpartial.stopReason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: partial.stopReason,\n\t\t\t\terror: partial,\n\t\t\t} satisfies AssistantMessageEvent);\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\t// Clean up abort handler\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nexport interface AppTransportOptions {\n\t/**\n\t * Proxy server URL. The server manages user accounts and proxies requests to LLM providers.\n\t * Example: \"https://genai.mariozechner.at\"\n\t */\n\tproxyUrl: string;\n\n\t/**\n\t * Function to retrieve auth token for the proxy server.\n\t * The token is used for user authentication and authorization.\n\t */\n\tgetAuthToken: () => Promise<string> | string;\n}\n\n/**\n * Transport that uses an app server with user authentication tokens.\n * The server manages user accounts and proxies requests to LLM providers.\n */\nexport class AppTransport implements AgentTransport {\n\tprivate options: AppTransportOptions;\n\n\tconstructor(options: AppTransportOptions) {\n\t\tthis.options = options;\n\t}\n\n\tasync *run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst authToken = await this.options.getAuthToken();\n\t\tif (!authToken) {\n\t\t\tthrow new Error(\"Auth token is required for AppTransport\");\n\t\t}\n\n\t\t// Use proxy - no local API key needed\n\t\tconst streamFn = <TApi extends Api>(model: Model<TApi>, context: Context, options?: SimpleStreamOptions) => {\n\t\t\treturn streamSimpleProxy(\n\t\t\t\tmodel,\n\t\t\t\tcontext,\n\t\t\t\t{\n\t\t\t\t\t...options,\n\t\t\t\t\tauthToken,\n\t\t\t\t},\n\t\t\t\tthis.options.proxyUrl,\n\t\t\t);\n\t\t};\n\n\t\t// Messages are already LLM-compatible (filtered by Agent)\n\t\tconst context: AgentContext = {\n\t\t\tsystemPrompt: cfg.systemPrompt,\n\t\t\tmessages,\n\t\t\ttools: cfg.tools,\n\t\t};\n\n\t\tconst pc: AgentLoopConfig = {\n\t\t\tmodel: cfg.model,\n\t\t\treasoning: cfg.reasoning,\n\t\t\tgetQueuedMessages: cfg.getQueuedMessages,\n\t\t};\n\n\t\t// Yield events from the upstream agentLoop iterator\n\t\t// Pass streamFn as the 5th parameter to use proxy\n\t\tfor await (const ev of agentLoop(userMessage as unknown as UserMessage, context, pc, signal, streamFn as any)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AppTransport.js","sourceRoot":"","sources":["../../src/transports/AppTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAgentContext,\n\tAgentLoopConfig,\n\tApi,\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tContext,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tToolCall,\n\tUserMessage,\n} from \"@mariozechner/pi-ai\";\nimport { agentLoop, agentLoopContinue } from \"@mariozechner/pi-ai\";\nimport { AssistantMessageEventStream } from \"@mariozechner/pi-ai/dist/utils/event-stream.js\";\nimport { parseStreamingJson } from \"@mariozechner/pi-ai/dist/utils/json-parse.js\";\nimport type { ProxyAssistantMessageEvent } from \"./proxy-types.js\";\nimport type { AgentRunConfig, AgentTransport } from \"./types.js\";\n\n/**\n * Stream function that proxies through a server instead of calling providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n */\nfunction streamSimpleProxy(\n\tmodel: Model<any>,\n\tcontext: Context,\n\toptions: SimpleStreamOptions & { authToken: string },\n\tproxyUrl: string,\n): AssistantMessageEventStream {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\t// Initialize the partial message that we'll build up from events\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\t// Set up abort handler to cancel the reader\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: {\n\t\t\t\t\t\ttemperature: options.temperature,\n\t\t\t\t\t\tmaxTokens: options.maxTokens,\n\t\t\t\t\t\treasoning: options.reasoning,\n\t\t\t\t\t\t// Don't send apiKey or signal - those are added server-side\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Couldn't parse error response, use default message\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\t// Parse SSE stream\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\t// Check if aborted after reading\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tlet event: AssistantMessageEvent | undefined;\n\n\t\t\t\t\t\t\t// Handle different event types\n\t\t\t\t\t\t\t// Server sends events with partial for non-delta events,\n\t\t\t\t\t\t\t// and without partial for delta events\n\t\t\t\t\t\t\tswitch (proxyEvent.type) {\n\t\t\t\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\t\t\t\tevent = { type: \"start\", partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"text_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcase \"text_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.text,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"thinking\",\n\t\t\t\t\t\t\t\t\t\tthinking: \"\",\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tevent = { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"thinking_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"thinking_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\t\t\t\t\t\t\ttype: \"toolCall\",\n\t\t\t\t\t\t\t\t\t\tid: proxyEvent.id,\n\t\t\t\t\t\t\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\t\t\t\t\t\t\targuments: {},\n\t\t\t\t\t\t\t\t\t\tpartialJson: \"\",\n\t\t\t\t\t\t\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\t\t\t\t\t\t\tevent = { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"toolcall_delta\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\t\t\t\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"toolcall_end\": {\n\t\t\t\t\t\t\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\t\t\t\t\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t\t\t\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\t\t\t\t\t\t\tevent = {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\t\t\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\t\t\t\t\t\t\ttoolCall: content,\n\t\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tcase \"done\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"done\", reason: proxyEvent.reason, message: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\t\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\t\t\t\t\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\t\t\t\t\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\t\t\t\t\t\t\tevent = { type: \"error\", reason: proxyEvent.reason, error: partial };\n\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t\t\t// Exhaustive check\n\t\t\t\t\t\t\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\t\t\t\t\t\t\tconsole.warn(`Unhandled event type: ${(proxyEvent as any).type}`);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Push the event to stream\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new Error(\"Failed to create event from proxy event\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check if aborted after reading\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tpartial.stopReason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason: partial.stopReason,\n\t\t\t\terror: partial,\n\t\t\t} satisfies AssistantMessageEvent);\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\t// Clean up abort handler\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nexport interface AppTransportOptions {\n\t/**\n\t * Proxy server URL. The server manages user accounts and proxies requests to LLM providers.\n\t * Example: \"https://genai.mariozechner.at\"\n\t */\n\tproxyUrl: string;\n\n\t/**\n\t * Function to retrieve auth token for the proxy server.\n\t * The token is used for user authentication and authorization.\n\t */\n\tgetAuthToken: () => Promise<string> | string;\n}\n\n/**\n * Transport that uses an app server with user authentication tokens.\n * The server manages user accounts and proxies requests to LLM providers.\n */\nexport class AppTransport implements AgentTransport {\n\tprivate options: AppTransportOptions;\n\n\tconstructor(options: AppTransportOptions) {\n\t\tthis.options = options;\n\t}\n\n\tprivate async getStreamFn(authToken: string) {\n\t\treturn <TApi extends Api>(model: Model<TApi>, context: Context, options?: SimpleStreamOptions) => {\n\t\t\treturn streamSimpleProxy(\n\t\t\t\tmodel,\n\t\t\t\tcontext,\n\t\t\t\t{\n\t\t\t\t\t...options,\n\t\t\t\t\tauthToken,\n\t\t\t\t},\n\t\t\t\tthis.options.proxyUrl,\n\t\t\t);\n\t\t};\n\t}\n\n\tprivate buildContext(messages: Message[], cfg: AgentRunConfig): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: cfg.systemPrompt,\n\t\t\tmessages,\n\t\t\ttools: cfg.tools,\n\t\t};\n\t}\n\n\tprivate buildLoopConfig(cfg: AgentRunConfig): AgentLoopConfig {\n\t\treturn {\n\t\t\tmodel: cfg.model,\n\t\t\treasoning: cfg.reasoning,\n\t\t\tgetQueuedMessages: cfg.getQueuedMessages,\n\t\t};\n\t}\n\n\tasync *run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst authToken = await this.options.getAuthToken();\n\t\tif (!authToken) {\n\t\t\tthrow new Error(\"Auth token is required for AppTransport\");\n\t\t}\n\n\t\tconst streamFn = await this.getStreamFn(authToken);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(cfg);\n\n\t\tfor await (const ev of agentLoop(userMessage as unknown as UserMessage, context, pc, signal, streamFn as any)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n\n\tasync *continue(messages: Message[], cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst authToken = await this.options.getAuthToken();\n\t\tif (!authToken) {\n\t\t\tthrow new Error(\"Auth token is required for AppTransport\");\n\t\t}\n\n\t\tconst streamFn = await this.getStreamFn(authToken);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(cfg);\n\n\t\tfor await (const ev of agentLoopContinue(context, pc, signal, streamFn as any)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -20,6 +20,10 @@ export interface ProviderTransportOptions {
|
|
|
20
20
|
export declare class ProviderTransport implements AgentTransport {
|
|
21
21
|
private options;
|
|
22
22
|
constructor(options?: ProviderTransportOptions);
|
|
23
|
+
private getModelAndKey;
|
|
24
|
+
private buildContext;
|
|
25
|
+
private buildLoopConfig;
|
|
23
26
|
run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal): AsyncGenerator<import("@mariozechner/pi-ai").AgentEvent, void, unknown>;
|
|
27
|
+
continue(messages: Message[], cfg: AgentRunConfig, signal?: AbortSignal): AsyncGenerator<import("@mariozechner/pi-ai").AgentEvent, void, unknown>;
|
|
24
28
|
}
|
|
25
29
|
//# sourceMappingURL=ProviderTransport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderTransport.d.ts","sourceRoot":"","sources":["../../src/transports/ProviderTransport.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ProviderTransport.d.ts","sourceRoot":"","sources":["../../src/transports/ProviderTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAKN,KAAK,OAAO,EAEZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,WAAW,wBAAwB;IACxC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;IAEnF;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IACvD,OAAO,CAAC,OAAO,CAA2B;IAE1C,YAAY,OAAO,GAAE,wBAA6B,EAEjD;YAEa,cAAc;IAoB5B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,eAAe;IAShB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,WAAW,2EAQ9F;IAEM,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,WAAW,2EAQ7E;CACD","sourcesContent":["import {\n\ttype AgentContext,\n\ttype AgentLoopConfig,\n\tagentLoop,\n\tagentLoopContinue,\n\ttype Message,\n\ttype UserMessage,\n} from \"@mariozechner/pi-ai\";\nimport type { AgentRunConfig, AgentTransport } from \"./types.js\";\n\nexport interface ProviderTransportOptions {\n\t/**\n\t * Function to retrieve API key for a given provider.\n\t * If not provided, transport will try to use environment variables.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Optional CORS proxy URL for browser environments.\n\t * If provided, all requests will be routed through this proxy.\n\t * Format: \"https://proxy.example.com\"\n\t */\n\tcorsProxyUrl?: string;\n}\n\n/**\n * Transport that calls LLM providers directly.\n * Optionally routes calls through a CORS proxy if configured.\n */\nexport class ProviderTransport implements AgentTransport {\n\tprivate options: ProviderTransportOptions;\n\n\tconstructor(options: ProviderTransportOptions = {}) {\n\t\tthis.options = options;\n\t}\n\n\tprivate async getModelAndKey(cfg: AgentRunConfig) {\n\t\tlet apiKey: string | undefined;\n\t\tif (this.options.getApiKey) {\n\t\t\tapiKey = await this.options.getApiKey(cfg.model.provider);\n\t\t}\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(`No API key found for provider: ${cfg.model.provider}`);\n\t\t}\n\n\t\tlet model = cfg.model;\n\t\tif (this.options.corsProxyUrl && cfg.model.baseUrl) {\n\t\t\tmodel = {\n\t\t\t\t...cfg.model,\n\t\t\t\tbaseUrl: `${this.options.corsProxyUrl}/?url=${encodeURIComponent(cfg.model.baseUrl)}`,\n\t\t\t};\n\t\t}\n\n\t\treturn { model, apiKey };\n\t}\n\n\tprivate buildContext(messages: Message[], cfg: AgentRunConfig): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: cfg.systemPrompt,\n\t\t\tmessages,\n\t\t\ttools: cfg.tools,\n\t\t};\n\t}\n\n\tprivate buildLoopConfig(model: typeof cfg.model, apiKey: string, cfg: AgentRunConfig): AgentLoopConfig {\n\t\treturn {\n\t\t\tmodel,\n\t\t\treasoning: cfg.reasoning,\n\t\t\tapiKey,\n\t\t\tgetQueuedMessages: cfg.getQueuedMessages,\n\t\t};\n\t}\n\n\tasync *run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst { model, apiKey } = await this.getModelAndKey(cfg);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(model, apiKey, cfg);\n\n\t\tfor await (const ev of agentLoop(userMessage as unknown as UserMessage, context, pc, signal)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n\n\tasync *continue(messages: Message[], cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst { model, apiKey } = await this.getModelAndKey(cfg);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(model, apiKey, cfg);\n\n\t\tfor await (const ev of agentLoopContinue(context, pc, signal)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { agentLoop, } from "@mariozechner/pi-ai";
|
|
1
|
+
import { agentLoop, agentLoopContinue, } from "@mariozechner/pi-ai";
|
|
2
2
|
/**
|
|
3
3
|
* Transport that calls LLM providers directly.
|
|
4
4
|
* Optionally routes calls through a CORS proxy if configured.
|
|
@@ -8,8 +8,7 @@ export class ProviderTransport {
|
|
|
8
8
|
constructor(options = {}) {
|
|
9
9
|
this.options = options;
|
|
10
10
|
}
|
|
11
|
-
async
|
|
12
|
-
// Get API key
|
|
11
|
+
async getModelAndKey(cfg) {
|
|
13
12
|
let apiKey;
|
|
14
13
|
if (this.options.getApiKey) {
|
|
15
14
|
apiKey = await this.options.getApiKey(cfg.model.provider);
|
|
@@ -17,7 +16,6 @@ export class ProviderTransport {
|
|
|
17
16
|
if (!apiKey) {
|
|
18
17
|
throw new Error(`No API key found for provider: ${cfg.model.provider}`);
|
|
19
18
|
}
|
|
20
|
-
// Clone model and modify baseUrl if CORS proxy is enabled
|
|
21
19
|
let model = cfg.model;
|
|
22
20
|
if (this.options.corsProxyUrl && cfg.model.baseUrl) {
|
|
23
21
|
model = {
|
|
@@ -25,22 +23,38 @@ export class ProviderTransport {
|
|
|
25
23
|
baseUrl: `${this.options.corsProxyUrl}/?url=${encodeURIComponent(cfg.model.baseUrl)}`,
|
|
26
24
|
};
|
|
27
25
|
}
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
return { model, apiKey };
|
|
27
|
+
}
|
|
28
|
+
buildContext(messages, cfg) {
|
|
29
|
+
return {
|
|
30
30
|
systemPrompt: cfg.systemPrompt,
|
|
31
31
|
messages,
|
|
32
32
|
tools: cfg.tools,
|
|
33
33
|
};
|
|
34
|
-
|
|
34
|
+
}
|
|
35
|
+
buildLoopConfig(model, apiKey, cfg) {
|
|
36
|
+
return {
|
|
35
37
|
model,
|
|
36
38
|
reasoning: cfg.reasoning,
|
|
37
39
|
apiKey,
|
|
38
40
|
getQueuedMessages: cfg.getQueuedMessages,
|
|
39
41
|
};
|
|
40
|
-
|
|
42
|
+
}
|
|
43
|
+
async *run(messages, userMessage, cfg, signal) {
|
|
44
|
+
const { model, apiKey } = await this.getModelAndKey(cfg);
|
|
45
|
+
const context = this.buildContext(messages, cfg);
|
|
46
|
+
const pc = this.buildLoopConfig(model, apiKey, cfg);
|
|
41
47
|
for await (const ev of agentLoop(userMessage, context, pc, signal)) {
|
|
42
48
|
yield ev;
|
|
43
49
|
}
|
|
44
50
|
}
|
|
51
|
+
async *continue(messages, cfg, signal) {
|
|
52
|
+
const { model, apiKey } = await this.getModelAndKey(cfg);
|
|
53
|
+
const context = this.buildContext(messages, cfg);
|
|
54
|
+
const pc = this.buildLoopConfig(model, apiKey, cfg);
|
|
55
|
+
for await (const ev of agentLoopContinue(context, pc, signal)) {
|
|
56
|
+
yield ev;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
//# sourceMappingURL=ProviderTransport.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderTransport.js","sourceRoot":"","sources":["../../src/transports/ProviderTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,SAAS,
|
|
1
|
+
{"version":3,"file":"ProviderTransport.js","sourceRoot":"","sources":["../../src/transports/ProviderTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,SAAS,EACT,iBAAiB,GAGjB,MAAM,qBAAqB,CAAC;AAkB7B;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IACrB,OAAO,CAA2B;IAE1C,YAAY,OAAO,GAA6B,EAAE,EAAE;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAAA,CACvB;IAEO,KAAK,CAAC,cAAc,CAAC,GAAmB,EAAE;QACjD,IAAI,MAA0B,CAAC;QAC/B,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC5B,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,IAAI,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;QACtB,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACpD,KAAK,GAAG;gBACP,GAAG,GAAG,CAAC,KAAK;gBACZ,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,SAAS,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;aACrF,CAAC;QACH,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAA,CACzB;IAEO,YAAY,CAAC,QAAmB,EAAE,GAAmB,EAAgB;QAC5E,OAAO;YACN,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,QAAQ;YACR,KAAK,EAAE,GAAG,CAAC,KAAK;SAChB,CAAC;IAAA,CACF;IAEO,eAAe,CAAC,KAAuB,EAAE,MAAc,EAAE,GAAmB,EAAmB;QACtG,OAAO;YACN,KAAK;YACL,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,MAAM;YACN,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;SACxC,CAAC;IAAA,CACF;IAED,KAAK,CAAC,CAAC,GAAG,CAAC,QAAmB,EAAE,WAAoB,EAAE,GAAmB,EAAE,MAAoB,EAAE;QAChG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAEpD,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,SAAS,CAAC,WAAqC,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;YAC9F,MAAM,EAAE,CAAC;QACV,CAAC;IAAA,CACD;IAED,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAmB,EAAE,GAAmB,EAAE,MAAoB,EAAE;QAC/E,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACjD,MAAM,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAEpD,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,iBAAiB,CAAC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;YAC/D,MAAM,EAAE,CAAC;QACV,CAAC;IAAA,CACD;CACD","sourcesContent":["import {\n\ttype AgentContext,\n\ttype AgentLoopConfig,\n\tagentLoop,\n\tagentLoopContinue,\n\ttype Message,\n\ttype UserMessage,\n} from \"@mariozechner/pi-ai\";\nimport type { AgentRunConfig, AgentTransport } from \"./types.js\";\n\nexport interface ProviderTransportOptions {\n\t/**\n\t * Function to retrieve API key for a given provider.\n\t * If not provided, transport will try to use environment variables.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Optional CORS proxy URL for browser environments.\n\t * If provided, all requests will be routed through this proxy.\n\t * Format: \"https://proxy.example.com\"\n\t */\n\tcorsProxyUrl?: string;\n}\n\n/**\n * Transport that calls LLM providers directly.\n * Optionally routes calls through a CORS proxy if configured.\n */\nexport class ProviderTransport implements AgentTransport {\n\tprivate options: ProviderTransportOptions;\n\n\tconstructor(options: ProviderTransportOptions = {}) {\n\t\tthis.options = options;\n\t}\n\n\tprivate async getModelAndKey(cfg: AgentRunConfig) {\n\t\tlet apiKey: string | undefined;\n\t\tif (this.options.getApiKey) {\n\t\t\tapiKey = await this.options.getApiKey(cfg.model.provider);\n\t\t}\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(`No API key found for provider: ${cfg.model.provider}`);\n\t\t}\n\n\t\tlet model = cfg.model;\n\t\tif (this.options.corsProxyUrl && cfg.model.baseUrl) {\n\t\t\tmodel = {\n\t\t\t\t...cfg.model,\n\t\t\t\tbaseUrl: `${this.options.corsProxyUrl}/?url=${encodeURIComponent(cfg.model.baseUrl)}`,\n\t\t\t};\n\t\t}\n\n\t\treturn { model, apiKey };\n\t}\n\n\tprivate buildContext(messages: Message[], cfg: AgentRunConfig): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: cfg.systemPrompt,\n\t\t\tmessages,\n\t\t\ttools: cfg.tools,\n\t\t};\n\t}\n\n\tprivate buildLoopConfig(model: typeof cfg.model, apiKey: string, cfg: AgentRunConfig): AgentLoopConfig {\n\t\treturn {\n\t\t\tmodel,\n\t\t\treasoning: cfg.reasoning,\n\t\t\tapiKey,\n\t\t\tgetQueuedMessages: cfg.getQueuedMessages,\n\t\t};\n\t}\n\n\tasync *run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst { model, apiKey } = await this.getModelAndKey(cfg);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(model, apiKey, cfg);\n\n\t\tfor await (const ev of agentLoop(userMessage as unknown as UserMessage, context, pc, signal)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n\n\tasync *continue(messages: Message[], cfg: AgentRunConfig, signal?: AbortSignal) {\n\t\tconst { model, apiKey } = await this.getModelAndKey(cfg);\n\t\tconst context = this.buildContext(messages, cfg);\n\t\tconst pc = this.buildLoopConfig(model, apiKey, cfg);\n\n\t\tfor await (const ev of agentLoopContinue(context, pc, signal)) {\n\t\t\tyield ev;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -17,6 +17,9 @@ export interface AgentRunConfig {
|
|
|
17
17
|
* Events yielded must match the @mariozechner/pi-ai AgentEvent types.
|
|
18
18
|
*/
|
|
19
19
|
export interface AgentTransport {
|
|
20
|
+
/** Run with a new user message */
|
|
20
21
|
run(messages: Message[], userMessage: Message, config: AgentRunConfig, signal?: AbortSignal): AsyncIterable<AgentEvent>;
|
|
22
|
+
/** Continue from current context (no new user message) */
|
|
23
|
+
continue(messages: Message[], config: AgentRunConfig, signal?: AbortSignal): AsyncIterable<AgentEvent>;
|
|
21
24
|
}
|
|
22
25
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transports/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEjH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,iBAAiB,CAAC,EAAE,CAAC,CAAC,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;CACzD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B,GAAG,CACF,QAAQ,EAAE,OAAO,EAAE,EACnB,WAAW,EAAE,OAAO,EACpB,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GAClB,aAAa,CAAC,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transports/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEjH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,iBAAiB,CAAC,EAAE,CAAC,CAAC,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;CACzD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B,kCAAkC;IAClC,GAAG,CACF,QAAQ,EAAE,OAAO,EAAE,EACnB,WAAW,EAAE,OAAO,EACpB,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GAClB,aAAa,CAAC,UAAU,CAAC,CAAC;IAE7B,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;CACvG","sourcesContent":["import type { AgentEvent, AgentTool, Message, Model, QueuedMessage, ReasoningEffort } from \"@mariozechner/pi-ai\";\n\n/**\n * The minimal configuration needed to run an agent turn.\n */\nexport interface AgentRunConfig {\n\tsystemPrompt: string;\n\ttools: AgentTool<any>[];\n\tmodel: Model<any>;\n\treasoning?: ReasoningEffort;\n\tgetQueuedMessages?: <T>() => Promise<QueuedMessage<T>[]>;\n}\n\n/**\n * Transport interface for executing agent turns.\n * Transports handle the communication with LLM providers,\n * abstracting away the details of API calls, proxies, etc.\n *\n * Events yielded must match the @mariozechner/pi-ai AgentEvent types.\n */\nexport interface AgentTransport {\n\t/** Run with a new user message */\n\trun(\n\t\tmessages: Message[],\n\t\tuserMessage: Message,\n\t\tconfig: AgentRunConfig,\n\t\tsignal?: AbortSignal,\n\t): AsyncIterable<AgentEvent>;\n\n\t/** Continue from current context (no new user message) */\n\tcontinue(messages: Message[], config: AgentRunConfig, signal?: AbortSignal): AsyncIterable<AgentEvent>;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transports/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentEvent, AgentTool, Message, Model, QueuedMessage, ReasoningEffort } from \"@mariozechner/pi-ai\";\n\n/**\n * The minimal configuration needed to run an agent turn.\n */\nexport interface AgentRunConfig {\n\tsystemPrompt: string;\n\ttools: AgentTool<any>[];\n\tmodel: Model<any>;\n\treasoning?: ReasoningEffort;\n\tgetQueuedMessages?: <T>() => Promise<QueuedMessage<T>[]>;\n}\n\n/**\n * Transport interface for executing agent turns.\n * Transports handle the communication with LLM providers,\n * abstracting away the details of API calls, proxies, etc.\n *\n * Events yielded must match the @mariozechner/pi-ai AgentEvent types.\n */\nexport interface AgentTransport {\n\trun(\n\t\tmessages: Message[],\n\t\tuserMessage: Message,\n\t\tconfig: AgentRunConfig,\n\t\tsignal?: AbortSignal,\n\t): AsyncIterable<AgentEvent>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transports/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentEvent, AgentTool, Message, Model, QueuedMessage, ReasoningEffort } from \"@mariozechner/pi-ai\";\n\n/**\n * The minimal configuration needed to run an agent turn.\n */\nexport interface AgentRunConfig {\n\tsystemPrompt: string;\n\ttools: AgentTool<any>[];\n\tmodel: Model<any>;\n\treasoning?: ReasoningEffort;\n\tgetQueuedMessages?: <T>() => Promise<QueuedMessage<T>[]>;\n}\n\n/**\n * Transport interface for executing agent turns.\n * Transports handle the communication with LLM providers,\n * abstracting away the details of API calls, proxies, etc.\n *\n * Events yielded must match the @mariozechner/pi-ai AgentEvent types.\n */\nexport interface AgentTransport {\n\t/** Run with a new user message */\n\trun(\n\t\tmessages: Message[],\n\t\tuserMessage: Message,\n\t\tconfig: AgentRunConfig,\n\t\tsignal?: AbortSignal,\n\t): AsyncIterable<AgentEvent>;\n\n\t/** Continue from current context (no new user message) */\n\tcontinue(messages: Message[], config: AgentRunConfig, signal?: AbortSignal): AsyncIterable<AgentEvent>;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mariozechner/pi-agent-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"prepublishOnly": "npm run clean && npm run build"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@mariozechner/pi-ai": "^0.
|
|
22
|
-
"@mariozechner/pi-tui": "^0.
|
|
21
|
+
"@mariozechner/pi-ai": "^0.17.0",
|
|
22
|
+
"@mariozechner/pi-tui": "^0.17.0"
|
|
23
23
|
},
|
|
24
24
|
"keywords": [
|
|
25
25
|
"ai",
|