neatlogs 1.0.5 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/azure-openai.cjs +344 -0
  2. package/dist/azure-openai.cjs.map +1 -0
  3. package/dist/azure-openai.d.ts +30 -0
  4. package/dist/azure-openai.mjs +318 -0
  5. package/dist/azure-openai.mjs.map +1 -0
  6. package/dist/bedrock.cjs +540 -0
  7. package/dist/bedrock.cjs.map +1 -0
  8. package/dist/bedrock.d.ts +29 -0
  9. package/dist/bedrock.mjs +514 -0
  10. package/dist/bedrock.mjs.map +1 -0
  11. package/dist/browser.cjs +121 -0
  12. package/dist/browser.cjs.map +1 -0
  13. package/dist/browser.d.ts +143 -0
  14. package/dist/browser.mjs +96 -0
  15. package/dist/browser.mjs.map +1 -0
  16. package/dist/claude-agent-sdk.cjs +406 -0
  17. package/dist/claude-agent-sdk.cjs.map +1 -0
  18. package/dist/claude-agent-sdk.d.ts +49 -0
  19. package/dist/claude-agent-sdk.mjs +381 -0
  20. package/dist/claude-agent-sdk.mjs.map +1 -0
  21. package/dist/index.cjs +3263 -839
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +10 -2
  24. package/dist/index.mjs +3254 -838
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/langchain.cjs +9 -9
  27. package/dist/langchain.cjs.map +1 -1
  28. package/dist/langchain.mjs +9 -9
  29. package/dist/langchain.mjs.map +1 -1
  30. package/dist/mastra-wrap.cjs +25 -25
  31. package/dist/mastra-wrap.cjs.map +1 -1
  32. package/dist/mastra-wrap.mjs +25 -25
  33. package/dist/mastra-wrap.mjs.map +1 -1
  34. package/dist/openai-agents.cjs +6 -6
  35. package/dist/openai-agents.cjs.map +1 -1
  36. package/dist/openai-agents.mjs +6 -6
  37. package/dist/openai-agents.mjs.map +1 -1
  38. package/dist/opencode-plugin.cjs +5879 -0
  39. package/dist/opencode-plugin.cjs.map +1 -0
  40. package/dist/opencode-plugin.d.ts +38 -0
  41. package/dist/opencode-plugin.mjs +5854 -0
  42. package/dist/opencode-plugin.mjs.map +1 -0
  43. package/dist/openrouter-agent.cjs +273 -0
  44. package/dist/openrouter-agent.cjs.map +1 -0
  45. package/dist/openrouter-agent.d.ts +34 -0
  46. package/dist/openrouter-agent.mjs +247 -0
  47. package/dist/openrouter-agent.mjs.map +1 -0
  48. package/dist/pi-agent.cjs +237 -0
  49. package/dist/pi-agent.cjs.map +1 -0
  50. package/dist/pi-agent.d.ts +31 -0
  51. package/dist/pi-agent.mjs +216 -0
  52. package/dist/pi-agent.mjs.map +1 -0
  53. package/dist/strands.cjs +125 -2
  54. package/dist/strands.cjs.map +1 -1
  55. package/dist/strands.d.ts +13 -12
  56. package/dist/strands.mjs +125 -2
  57. package/dist/strands.mjs.map +1 -1
  58. package/dist/vertex-ai.cjs +424 -0
  59. package/dist/vertex-ai.cjs.map +1 -0
  60. package/dist/vertex-ai.d.ts +39 -0
  61. package/dist/vertex-ai.mjs +397 -0
  62. package/dist/vertex-ai.mjs.map +1 -0
  63. package/package.json +88 -1
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/pi-agent.ts
21
+ var pi_agent_exports = {};
22
+ __export(pi_agent_exports, {
23
+ piAgentHooks: () => piAgentHooks
24
+ });
25
+ module.exports = __toCommonJS(pi_agent_exports);
26
+ var import_api = require("@opentelemetry/api");
27
+ var TRACER_NAME = "neatlogs.pi-agent";
28
+ var PATCH_FLAG = "_neatlogs_patched";
29
+ function piAgentHooks(agent) {
30
+ if (!agent || agent[PATCH_FLAG]) return agent;
31
+ const a = agent;
32
+ if (typeof a.subscribe !== "function") return agent;
33
+ const state = { toolSpans: /* @__PURE__ */ new Map(), inputMessages: [] };
34
+ const tracer = import_api.trace.getTracer(TRACER_NAME);
35
+ a.subscribe((event) => {
36
+ try {
37
+ handleEvent(tracer, state, event);
38
+ } catch {
39
+ }
40
+ });
41
+ markPatched(a);
42
+ return agent;
43
+ }
44
+ function handleEvent(tracer, state, event) {
45
+ switch (event.type) {
46
+ case "agent_start": {
47
+ const span = tracer.startSpan(
48
+ "pi_agent.run",
49
+ { attributes: { "neatlogs.span.kind": "AGENT" } },
50
+ import_api.context.active()
51
+ );
52
+ state.agentSpan = span;
53
+ state.agentCtx = import_api.trace.setSpan(import_api.context.active(), span);
54
+ state.inputMessages = [];
55
+ break;
56
+ }
57
+ case "message_end": {
58
+ const msg = event.message;
59
+ if (!msg) return;
60
+ if (msg.role === "assistant") {
61
+ emitLlmSpan(tracer, state, msg);
62
+ const { text } = splitAssistantContent(msg.content);
63
+ if (text) state.inputMessages.push({ role: "assistant", content: text });
64
+ } else {
65
+ const role = msg.role === "toolResult" ? "tool" : String(msg.role || "user");
66
+ const content = messageText(msg);
67
+ if (content) state.inputMessages.push({ role, content });
68
+ }
69
+ break;
70
+ }
71
+ case "tool_execution_start": {
72
+ const parent = state.agentCtx ?? import_api.context.active();
73
+ const span = tracer.startSpan(
74
+ `pi_agent.tool.${event.toolName ?? "tool"}`,
75
+ {
76
+ attributes: {
77
+ "neatlogs.span.kind": "TOOL",
78
+ ...event.toolName ? { "neatlogs.tool.name": String(event.toolName) } : {},
79
+ ...event.args !== void 0 ? { "input.value": safeStringify(event.args) } : {}
80
+ }
81
+ },
82
+ parent
83
+ );
84
+ if (event.toolCallId) state.toolSpans.set(event.toolCallId, span);
85
+ break;
86
+ }
87
+ case "tool_execution_end": {
88
+ const span = event.toolCallId ? state.toolSpans.get(event.toolCallId) : void 0;
89
+ if (!span) return;
90
+ if (event.result !== void 0) {
91
+ span.setAttribute("output.value", safeStringify(event.result));
92
+ }
93
+ if (event.isError) {
94
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR });
95
+ span.setAttribute("neatlogs.tool.is_error", true);
96
+ } else {
97
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
98
+ }
99
+ span.end();
100
+ if (event.toolCallId) state.toolSpans.delete(event.toolCallId);
101
+ break;
102
+ }
103
+ case "agent_end": {
104
+ for (const ts of state.toolSpans.values()) {
105
+ try {
106
+ ts.end();
107
+ } catch {
108
+ }
109
+ }
110
+ state.toolSpans.clear();
111
+ if (state.agentSpan) {
112
+ const firstUser = state.inputMessages.find((m) => m.role === "user");
113
+ if (firstUser) state.agentSpan.setAttribute("input.value", firstUser.content);
114
+ const finalText = lastAssistantText(event.messages);
115
+ if (finalText) state.agentSpan.setAttribute("output.value", finalText);
116
+ state.agentSpan.setStatus({ code: import_api.SpanStatusCode.OK });
117
+ state.agentSpan.end();
118
+ state.agentSpan = void 0;
119
+ state.agentCtx = void 0;
120
+ }
121
+ break;
122
+ }
123
+ default:
124
+ break;
125
+ }
126
+ }
127
+ function emitLlmSpan(tracer, state, msg) {
128
+ const attrs = { "neatlogs.span.kind": "LLM" };
129
+ if (msg.model) attrs["neatlogs.llm.model_name"] = String(msg.model);
130
+ if (msg.provider) attrs["neatlogs.llm.provider"] = String(msg.provider);
131
+ if (msg.stopReason) attrs["neatlogs.llm.stop_reason"] = String(msg.stopReason);
132
+ const inMsgs = state.inputMessages;
133
+ if (inMsgs.length) {
134
+ inMsgs.forEach((m, i) => {
135
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;
136
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;
137
+ });
138
+ attrs["neatlogs.llm.input"] = safeStringify({ messages: inMsgs });
139
+ attrs["input.value"] = safeStringify({ messages: inMsgs });
140
+ }
141
+ const { text, toolCalls } = splitAssistantContent(msg.content);
142
+ const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.arguments)})`).join("\n");
143
+ if (outText || toolCalls.length) {
144
+ attrs["neatlogs.llm.output_messages.0.role"] = "assistant";
145
+ attrs["neatlogs.llm.output_messages.0.content"] = outText || "";
146
+ const outBlob = { role: "assistant", content: outText || "" };
147
+ if (toolCalls.length) {
148
+ outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));
149
+ toolCalls.forEach((tc, j) => {
150
+ if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;
151
+ if (tc.arguments !== void 0)
152
+ attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.arguments);
153
+ if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);
154
+ });
155
+ }
156
+ attrs["neatlogs.llm.output"] = safeStringify(outBlob);
157
+ attrs["output.value"] = outText || "";
158
+ }
159
+ const usage = msg.usage;
160
+ if (usage) {
161
+ if (usage.input != null) attrs["neatlogs.llm.token_count.prompt"] = usage.input;
162
+ if (usage.output != null) attrs["neatlogs.llm.token_count.completion"] = usage.output;
163
+ const total = usage.totalTokens ?? (usage.input ?? 0) + (usage.output ?? 0);
164
+ if (total) attrs["neatlogs.llm.token_count.total"] = total;
165
+ if (usage.cacheRead) attrs["neatlogs.llm.token_count.cache_read"] = usage.cacheRead;
166
+ if (usage.cacheWrite) attrs["neatlogs.llm.token_count.cache_write"] = usage.cacheWrite;
167
+ }
168
+ const parent = state.agentCtx ?? import_api.context.active();
169
+ const span = tracer.startSpan(
170
+ `pi_agent.llm.${msg.model || "model"}`,
171
+ { attributes: attrs },
172
+ parent
173
+ );
174
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
175
+ span.end();
176
+ }
177
+ function splitAssistantContent(content) {
178
+ const texts = [];
179
+ const toolCalls = [];
180
+ if (Array.isArray(content)) {
181
+ for (const block of content) {
182
+ if (!block || typeof block !== "object") continue;
183
+ if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
184
+ else if (block.type === "toolCall")
185
+ toolCalls.push(block);
186
+ }
187
+ } else if (typeof content === "string") {
188
+ texts.push(content);
189
+ }
190
+ return { text: texts.join(""), toolCalls };
191
+ }
192
+ function messageText(msg) {
193
+ if (!msg) return "";
194
+ const c = msg.content;
195
+ if (typeof c === "string") return c;
196
+ if (!Array.isArray(c)) return "";
197
+ const parts = [];
198
+ for (const block of c) {
199
+ if (typeof block === "string") parts.push(block);
200
+ else if (block && typeof block === "object") {
201
+ if (typeof block.text === "string") parts.push(block.text);
202
+ else if (block.type === "toolCall") parts.push(`${block.name ?? "tool"}(${safeStringify(block.arguments)})`);
203
+ }
204
+ }
205
+ return parts.join("");
206
+ }
207
+ function lastAssistantText(messages) {
208
+ if (!Array.isArray(messages)) return "";
209
+ for (let i = messages.length - 1; i >= 0; i--) {
210
+ const m = messages[i];
211
+ if (m && m.role === "assistant") {
212
+ const { text } = splitAssistantContent(m.content);
213
+ if (text) return text;
214
+ }
215
+ }
216
+ return "";
217
+ }
218
+ function markPatched(e) {
219
+ try {
220
+ Object.defineProperty(e, PATCH_FLAG, { value: true, enumerable: false, configurable: true });
221
+ } catch {
222
+ e[PATCH_FLAG] = true;
223
+ }
224
+ }
225
+ function safeStringify(value) {
226
+ if (typeof value === "string") return value;
227
+ try {
228
+ return JSON.stringify(value) ?? "";
229
+ } catch {
230
+ return "";
231
+ }
232
+ }
233
+ // Annotate the CommonJS export names for ESM import in node:
234
+ 0 && (module.exports = {
235
+ piAgentHooks
236
+ });
237
+ //# sourceMappingURL=pi-agent.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pi-agent.ts"],"sourcesContent":["/**\n * Neatlogs Pi Agent integration.\n *\n * Usage:\n * import { init } from 'neatlogs';\n * import { piAgentHooks } from 'neatlogs/pi-agent';\n * import { Agent } from '@mariozechner/pi-agent-core';\n *\n * await init({ apiKey, workflowName });\n * const agent = piAgentHooks(new Agent({ initialState: { systemPrompt, model } }));\n * await agent.prompt('Hello');\n *\n * Pi Agent's `Agent` exposes a first-class `subscribe(listener)` API and emits\n * AgentEvents for its run lifecycle (message_*, tool_execution_*, turn_*,\n * agent_*). It does NOT emit its own OpenTelemetry spans — so we LISTEN to those\n * events (no monkey-patching) and translate them into neatlogs OTel spans:\n *\n * AGENT agent run (agent_start → agent_end)\n * ↳ LLM assistant message (each assistant message_end)\n * ↳ TOOL tool call (tool_execution_start → tool_execution_end)\n *\n * The AGENT span is opened as the active span so the LLM/TOOL children nest under\n * it (and under any user @span / trace() block active when prompt() is called).\n */\n\nimport {\n trace,\n context as otelContext,\n SpanStatusCode,\n type Span,\n type Context,\n} from '@opentelemetry/api';\n\nconst TRACER_NAME = 'neatlogs.pi-agent';\nconst PATCH_FLAG = '_neatlogs_patched';\n\n// Minimal structural types for the Pi Agent event surface (we duck-type — no\n// hard dependency on the pi-agent-core package).\ninterface PiUsage {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n totalTokens?: number;\n}\ninterface PiToolCall {\n type: 'toolCall';\n id?: string;\n name?: string;\n arguments?: Record<string, unknown>;\n}\ninterface PiAssistantMessage {\n role: 'assistant';\n content?: Array<{ type: string; text?: string; name?: string; arguments?: unknown }>;\n model?: string;\n provider?: string;\n usage?: PiUsage;\n stopReason?: string;\n}\ninterface PiAgentEvent {\n type: string;\n message?: any;\n messages?: any[];\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n result?: unknown;\n isError?: boolean;\n}\n\ninterface PerAgentState {\n agentSpan?: Span;\n agentCtx?: Context;\n toolSpans: Map<string, Span>;\n /** Running conversation (system/user/tool turns) to use as LLM-span input.\n * Pi Agent's assistant message_end carries only the response, not the prompt. */\n inputMessages: Array<{ role: string; content: string }>;\n}\n\n/**\n * Subscribe neatlogs tracing to a Pi Agent instance. Returns the same agent\n * (marked so re-subscribing is a no-op). Idempotent per agent.\n */\nexport function piAgentHooks<T extends object>(agent: T): T {\n if (!agent || (agent as any)[PATCH_FLAG]) return agent;\n const a = agent as any;\n if (typeof a.subscribe !== 'function') return agent; // not a Pi Agent — leave alone\n\n const state: PerAgentState = { toolSpans: new Map(), inputMessages: [] };\n const tracer = trace.getTracer(TRACER_NAME);\n\n a.subscribe((event: PiAgentEvent) => {\n try {\n handleEvent(tracer, state, event);\n } catch {\n // never let tracing break the agent run\n }\n });\n\n markPatched(a);\n return agent;\n}\n\nfunction handleEvent(\n tracer: ReturnType<typeof trace.getTracer>,\n state: PerAgentState,\n event: PiAgentEvent,\n): void {\n switch (event.type) {\n case 'agent_start': {\n // Open the AGENT (run) span as the active span so children nest under it.\n const span = tracer.startSpan(\n 'pi_agent.run',\n { attributes: { 'neatlogs.span.kind': 'AGENT' } },\n otelContext.active(),\n );\n state.agentSpan = span;\n state.agentCtx = trace.setSpan(otelContext.active(), span);\n state.inputMessages = [];\n break;\n }\n\n case 'message_end': {\n const msg = event.message as any;\n if (!msg) return;\n if (msg.role === 'assistant') {\n // Assistant message = the LLM response. Emit an LLM span using the\n // accumulated conversation as input, then record the assistant turn too.\n emitLlmSpan(tracer, state, msg as PiAssistantMessage);\n const { text } = splitAssistantContent(msg.content);\n if (text) state.inputMessages.push({ role: 'assistant', content: text });\n } else {\n // user / toolResult turns — accumulate as input context for later LLM spans.\n const role = msg.role === 'toolResult' ? 'tool' : String(msg.role || 'user');\n const content = messageText(msg);\n if (content) state.inputMessages.push({ role, content });\n }\n break;\n }\n\n case 'tool_execution_start': {\n const parent = state.agentCtx ?? otelContext.active();\n const span = tracer.startSpan(\n `pi_agent.tool.${event.toolName ?? 'tool'}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n ...(event.toolName ? { 'neatlogs.tool.name': String(event.toolName) } : {}),\n ...(event.args !== undefined\n ? { 'input.value': safeStringify(event.args) }\n : {}),\n },\n },\n parent,\n );\n if (event.toolCallId) state.toolSpans.set(event.toolCallId, span);\n break;\n }\n\n case 'tool_execution_end': {\n const span = event.toolCallId ? state.toolSpans.get(event.toolCallId) : undefined;\n if (!span) return;\n if (event.result !== undefined) {\n span.setAttribute('output.value', safeStringify(event.result));\n }\n if (event.isError) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.setAttribute('neatlogs.tool.is_error', true);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n if (event.toolCallId) state.toolSpans.delete(event.toolCallId);\n break;\n }\n\n case 'agent_end': {\n // Close any tool spans that never received an end event, then the agent span.\n for (const ts of state.toolSpans.values()) {\n try {\n ts.end();\n } catch {\n /* ignore */\n }\n }\n state.toolSpans.clear();\n if (state.agentSpan) {\n // Agent input = the first user message of the run; output = final answer.\n const firstUser = state.inputMessages.find((m) => m.role === 'user');\n if (firstUser) state.agentSpan.setAttribute('input.value', firstUser.content);\n const finalText = lastAssistantText(event.messages);\n if (finalText) state.agentSpan.setAttribute('output.value', finalText);\n state.agentSpan.setStatus({ code: SpanStatusCode.OK });\n state.agentSpan.end();\n state.agentSpan = undefined;\n state.agentCtx = undefined;\n }\n break;\n }\n\n default:\n break;\n }\n}\n\nfunction emitLlmSpan(\n tracer: ReturnType<typeof trace.getTracer>,\n state: PerAgentState,\n msg: PiAssistantMessage,\n): void {\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'LLM' };\n if (msg.model) attrs['neatlogs.llm.model_name'] = String(msg.model);\n if (msg.provider) attrs['neatlogs.llm.provider'] = String(msg.provider);\n if (msg.stopReason) attrs['neatlogs.llm.stop_reason'] = String(msg.stopReason);\n\n // Input = the conversation accumulated up to this assistant turn (system +\n // user + prior assistant/tool messages). Pi Agent's message_end doesn't carry\n // the prompt, so we reconstruct it from the running inputMessages list.\n const inMsgs = state.inputMessages;\n if (inMsgs.length) {\n inMsgs.forEach((m, i) => {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;\n attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;\n });\n attrs['neatlogs.llm.input'] = safeStringify({ messages: inMsgs });\n attrs['input.value'] = safeStringify({ messages: inMsgs });\n }\n\n const { text, toolCalls } = splitAssistantContent(msg.content);\n // Output: text if present, else a readable tool-call summary so the span isn't blank.\n const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.arguments)})`).join('\\n');\n if (outText || toolCalls.length) {\n attrs['neatlogs.llm.output_messages.0.role'] = 'assistant';\n attrs['neatlogs.llm.output_messages.0.content'] = (outText || '');\n const outBlob: Record<string, unknown> = { role: 'assistant', content: outText || '' };\n if (toolCalls.length) {\n outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));\n toolCalls.forEach((tc, j) => {\n if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;\n if (tc.arguments !== undefined)\n attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.arguments);\n if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);\n });\n }\n attrs['neatlogs.llm.output'] = safeStringify(outBlob);\n attrs['output.value'] = (outText || '');\n }\n\n const usage = msg.usage;\n if (usage) {\n if (usage.input != null) attrs['neatlogs.llm.token_count.prompt'] = usage.input;\n if (usage.output != null) attrs['neatlogs.llm.token_count.completion'] = usage.output;\n const total = usage.totalTokens ?? ((usage.input ?? 0) + (usage.output ?? 0));\n if (total) attrs['neatlogs.llm.token_count.total'] = total;\n if (usage.cacheRead) attrs['neatlogs.llm.token_count.cache_read'] = usage.cacheRead;\n if (usage.cacheWrite) attrs['neatlogs.llm.token_count.cache_write'] = usage.cacheWrite;\n }\n\n const parent = state.agentCtx ?? otelContext.active();\n const span = tracer.startSpan(\n `pi_agent.llm.${msg.model || 'model'}`,\n { attributes: attrs },\n parent,\n );\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Content helpers\n// ---------------------------------------------------------------------------\n\nfunction splitAssistantContent(\n content: PiAssistantMessage['content'],\n): { text: string; toolCalls: PiToolCall[] } {\n const texts: string[] = [];\n const toolCalls: PiToolCall[] = [];\n if (Array.isArray(content)) {\n for (const block of content) {\n if (!block || typeof block !== 'object') continue;\n if (block.type === 'text' && typeof block.text === 'string') texts.push(block.text);\n else if (block.type === 'toolCall')\n toolCalls.push(block as unknown as PiToolCall);\n // thinking blocks intentionally omitted from the main output text\n }\n } else if (typeof content === 'string') {\n texts.push(content);\n }\n return { text: texts.join(''), toolCalls };\n}\n\n/** Flatten any message's content (string or block array) to readable text. */\nfunction messageText(msg: any): string {\n if (!msg) return '';\n const c = msg.content;\n if (typeof c === 'string') return c;\n if (!Array.isArray(c)) return '';\n const parts: string[] = [];\n for (const block of c) {\n if (typeof block === 'string') parts.push(block);\n else if (block && typeof block === 'object') {\n if (typeof block.text === 'string') parts.push(block.text);\n else if (block.type === 'toolCall') parts.push(`${block.name ?? 'tool'}(${safeStringify(block.arguments)})`);\n }\n }\n return parts.join('');\n}\n\nfunction lastAssistantText(messages: any[] | undefined): string {\n if (!Array.isArray(messages)) return '';\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m && m.role === 'assistant') {\n const { text } = splitAssistantContent(m.content);\n if (text) return text;\n }\n }\n return '';\n}\n\nfunction markPatched(e: any): void {\n try {\n Object.defineProperty(e, PATCH_FLAG, { value: true, enumerable: false, configurable: true });\n } catch {\n e[PATCH_FLAG] = true;\n }\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value) ?? '';\n } catch {\n return '';\n }\n}\n"],"mappings":"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","names":["otelContext"]}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Neatlogs Pi Agent integration.
3
+ *
4
+ * Usage:
5
+ * import { init } from 'neatlogs';
6
+ * import { piAgentHooks } from 'neatlogs/pi-agent';
7
+ * import { Agent } from '@mariozechner/pi-agent-core';
8
+ *
9
+ * await init({ apiKey, workflowName });
10
+ * const agent = piAgentHooks(new Agent({ initialState: { systemPrompt, model } }));
11
+ * await agent.prompt('Hello');
12
+ *
13
+ * Pi Agent's `Agent` exposes a first-class `subscribe(listener)` API and emits
14
+ * AgentEvents for its run lifecycle (message_*, tool_execution_*, turn_*,
15
+ * agent_*). It does NOT emit its own OpenTelemetry spans — so we LISTEN to those
16
+ * events (no monkey-patching) and translate them into neatlogs OTel spans:
17
+ *
18
+ * AGENT agent run (agent_start → agent_end)
19
+ * ↳ LLM assistant message (each assistant message_end)
20
+ * ↳ TOOL tool call (tool_execution_start → tool_execution_end)
21
+ *
22
+ * The AGENT span is opened as the active span so the LLM/TOOL children nest under
23
+ * it (and under any user @span / trace() block active when prompt() is called).
24
+ */
25
+ /**
26
+ * Subscribe neatlogs tracing to a Pi Agent instance. Returns the same agent
27
+ * (marked so re-subscribing is a no-op). Idempotent per agent.
28
+ */
29
+ declare function piAgentHooks<T extends object>(agent: T): T;
30
+
31
+ export { piAgentHooks };
@@ -0,0 +1,216 @@
1
+ // src/pi-agent.ts
2
+ import {
3
+ trace,
4
+ context as otelContext,
5
+ SpanStatusCode
6
+ } from "@opentelemetry/api";
7
+ var TRACER_NAME = "neatlogs.pi-agent";
8
+ var PATCH_FLAG = "_neatlogs_patched";
9
+ function piAgentHooks(agent) {
10
+ if (!agent || agent[PATCH_FLAG]) return agent;
11
+ const a = agent;
12
+ if (typeof a.subscribe !== "function") return agent;
13
+ const state = { toolSpans: /* @__PURE__ */ new Map(), inputMessages: [] };
14
+ const tracer = trace.getTracer(TRACER_NAME);
15
+ a.subscribe((event) => {
16
+ try {
17
+ handleEvent(tracer, state, event);
18
+ } catch {
19
+ }
20
+ });
21
+ markPatched(a);
22
+ return agent;
23
+ }
24
+ function handleEvent(tracer, state, event) {
25
+ switch (event.type) {
26
+ case "agent_start": {
27
+ const span = tracer.startSpan(
28
+ "pi_agent.run",
29
+ { attributes: { "neatlogs.span.kind": "AGENT" } },
30
+ otelContext.active()
31
+ );
32
+ state.agentSpan = span;
33
+ state.agentCtx = trace.setSpan(otelContext.active(), span);
34
+ state.inputMessages = [];
35
+ break;
36
+ }
37
+ case "message_end": {
38
+ const msg = event.message;
39
+ if (!msg) return;
40
+ if (msg.role === "assistant") {
41
+ emitLlmSpan(tracer, state, msg);
42
+ const { text } = splitAssistantContent(msg.content);
43
+ if (text) state.inputMessages.push({ role: "assistant", content: text });
44
+ } else {
45
+ const role = msg.role === "toolResult" ? "tool" : String(msg.role || "user");
46
+ const content = messageText(msg);
47
+ if (content) state.inputMessages.push({ role, content });
48
+ }
49
+ break;
50
+ }
51
+ case "tool_execution_start": {
52
+ const parent = state.agentCtx ?? otelContext.active();
53
+ const span = tracer.startSpan(
54
+ `pi_agent.tool.${event.toolName ?? "tool"}`,
55
+ {
56
+ attributes: {
57
+ "neatlogs.span.kind": "TOOL",
58
+ ...event.toolName ? { "neatlogs.tool.name": String(event.toolName) } : {},
59
+ ...event.args !== void 0 ? { "input.value": safeStringify(event.args) } : {}
60
+ }
61
+ },
62
+ parent
63
+ );
64
+ if (event.toolCallId) state.toolSpans.set(event.toolCallId, span);
65
+ break;
66
+ }
67
+ case "tool_execution_end": {
68
+ const span = event.toolCallId ? state.toolSpans.get(event.toolCallId) : void 0;
69
+ if (!span) return;
70
+ if (event.result !== void 0) {
71
+ span.setAttribute("output.value", safeStringify(event.result));
72
+ }
73
+ if (event.isError) {
74
+ span.setStatus({ code: SpanStatusCode.ERROR });
75
+ span.setAttribute("neatlogs.tool.is_error", true);
76
+ } else {
77
+ span.setStatus({ code: SpanStatusCode.OK });
78
+ }
79
+ span.end();
80
+ if (event.toolCallId) state.toolSpans.delete(event.toolCallId);
81
+ break;
82
+ }
83
+ case "agent_end": {
84
+ for (const ts of state.toolSpans.values()) {
85
+ try {
86
+ ts.end();
87
+ } catch {
88
+ }
89
+ }
90
+ state.toolSpans.clear();
91
+ if (state.agentSpan) {
92
+ const firstUser = state.inputMessages.find((m) => m.role === "user");
93
+ if (firstUser) state.agentSpan.setAttribute("input.value", firstUser.content);
94
+ const finalText = lastAssistantText(event.messages);
95
+ if (finalText) state.agentSpan.setAttribute("output.value", finalText);
96
+ state.agentSpan.setStatus({ code: SpanStatusCode.OK });
97
+ state.agentSpan.end();
98
+ state.agentSpan = void 0;
99
+ state.agentCtx = void 0;
100
+ }
101
+ break;
102
+ }
103
+ default:
104
+ break;
105
+ }
106
+ }
107
+ function emitLlmSpan(tracer, state, msg) {
108
+ const attrs = { "neatlogs.span.kind": "LLM" };
109
+ if (msg.model) attrs["neatlogs.llm.model_name"] = String(msg.model);
110
+ if (msg.provider) attrs["neatlogs.llm.provider"] = String(msg.provider);
111
+ if (msg.stopReason) attrs["neatlogs.llm.stop_reason"] = String(msg.stopReason);
112
+ const inMsgs = state.inputMessages;
113
+ if (inMsgs.length) {
114
+ inMsgs.forEach((m, i) => {
115
+ attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;
116
+ attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;
117
+ });
118
+ attrs["neatlogs.llm.input"] = safeStringify({ messages: inMsgs });
119
+ attrs["input.value"] = safeStringify({ messages: inMsgs });
120
+ }
121
+ const { text, toolCalls } = splitAssistantContent(msg.content);
122
+ const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.arguments)})`).join("\n");
123
+ if (outText || toolCalls.length) {
124
+ attrs["neatlogs.llm.output_messages.0.role"] = "assistant";
125
+ attrs["neatlogs.llm.output_messages.0.content"] = outText || "";
126
+ const outBlob = { role: "assistant", content: outText || "" };
127
+ if (toolCalls.length) {
128
+ outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));
129
+ toolCalls.forEach((tc, j) => {
130
+ if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;
131
+ if (tc.arguments !== void 0)
132
+ attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.arguments);
133
+ if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);
134
+ });
135
+ }
136
+ attrs["neatlogs.llm.output"] = safeStringify(outBlob);
137
+ attrs["output.value"] = outText || "";
138
+ }
139
+ const usage = msg.usage;
140
+ if (usage) {
141
+ if (usage.input != null) attrs["neatlogs.llm.token_count.prompt"] = usage.input;
142
+ if (usage.output != null) attrs["neatlogs.llm.token_count.completion"] = usage.output;
143
+ const total = usage.totalTokens ?? (usage.input ?? 0) + (usage.output ?? 0);
144
+ if (total) attrs["neatlogs.llm.token_count.total"] = total;
145
+ if (usage.cacheRead) attrs["neatlogs.llm.token_count.cache_read"] = usage.cacheRead;
146
+ if (usage.cacheWrite) attrs["neatlogs.llm.token_count.cache_write"] = usage.cacheWrite;
147
+ }
148
+ const parent = state.agentCtx ?? otelContext.active();
149
+ const span = tracer.startSpan(
150
+ `pi_agent.llm.${msg.model || "model"}`,
151
+ { attributes: attrs },
152
+ parent
153
+ );
154
+ span.setStatus({ code: SpanStatusCode.OK });
155
+ span.end();
156
+ }
157
+ function splitAssistantContent(content) {
158
+ const texts = [];
159
+ const toolCalls = [];
160
+ if (Array.isArray(content)) {
161
+ for (const block of content) {
162
+ if (!block || typeof block !== "object") continue;
163
+ if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
164
+ else if (block.type === "toolCall")
165
+ toolCalls.push(block);
166
+ }
167
+ } else if (typeof content === "string") {
168
+ texts.push(content);
169
+ }
170
+ return { text: texts.join(""), toolCalls };
171
+ }
172
+ function messageText(msg) {
173
+ if (!msg) return "";
174
+ const c = msg.content;
175
+ if (typeof c === "string") return c;
176
+ if (!Array.isArray(c)) return "";
177
+ const parts = [];
178
+ for (const block of c) {
179
+ if (typeof block === "string") parts.push(block);
180
+ else if (block && typeof block === "object") {
181
+ if (typeof block.text === "string") parts.push(block.text);
182
+ else if (block.type === "toolCall") parts.push(`${block.name ?? "tool"}(${safeStringify(block.arguments)})`);
183
+ }
184
+ }
185
+ return parts.join("");
186
+ }
187
+ function lastAssistantText(messages) {
188
+ if (!Array.isArray(messages)) return "";
189
+ for (let i = messages.length - 1; i >= 0; i--) {
190
+ const m = messages[i];
191
+ if (m && m.role === "assistant") {
192
+ const { text } = splitAssistantContent(m.content);
193
+ if (text) return text;
194
+ }
195
+ }
196
+ return "";
197
+ }
198
+ function markPatched(e) {
199
+ try {
200
+ Object.defineProperty(e, PATCH_FLAG, { value: true, enumerable: false, configurable: true });
201
+ } catch {
202
+ e[PATCH_FLAG] = true;
203
+ }
204
+ }
205
+ function safeStringify(value) {
206
+ if (typeof value === "string") return value;
207
+ try {
208
+ return JSON.stringify(value) ?? "";
209
+ } catch {
210
+ return "";
211
+ }
212
+ }
213
+ export {
214
+ piAgentHooks
215
+ };
216
+ //# sourceMappingURL=pi-agent.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pi-agent.ts"],"sourcesContent":["/**\n * Neatlogs Pi Agent integration.\n *\n * Usage:\n * import { init } from 'neatlogs';\n * import { piAgentHooks } from 'neatlogs/pi-agent';\n * import { Agent } from '@mariozechner/pi-agent-core';\n *\n * await init({ apiKey, workflowName });\n * const agent = piAgentHooks(new Agent({ initialState: { systemPrompt, model } }));\n * await agent.prompt('Hello');\n *\n * Pi Agent's `Agent` exposes a first-class `subscribe(listener)` API and emits\n * AgentEvents for its run lifecycle (message_*, tool_execution_*, turn_*,\n * agent_*). It does NOT emit its own OpenTelemetry spans — so we LISTEN to those\n * events (no monkey-patching) and translate them into neatlogs OTel spans:\n *\n * AGENT agent run (agent_start → agent_end)\n * ↳ LLM assistant message (each assistant message_end)\n * ↳ TOOL tool call (tool_execution_start → tool_execution_end)\n *\n * The AGENT span is opened as the active span so the LLM/TOOL children nest under\n * it (and under any user @span / trace() block active when prompt() is called).\n */\n\nimport {\n trace,\n context as otelContext,\n SpanStatusCode,\n type Span,\n type Context,\n} from '@opentelemetry/api';\n\nconst TRACER_NAME = 'neatlogs.pi-agent';\nconst PATCH_FLAG = '_neatlogs_patched';\n\n// Minimal structural types for the Pi Agent event surface (we duck-type — no\n// hard dependency on the pi-agent-core package).\ninterface PiUsage {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n totalTokens?: number;\n}\ninterface PiToolCall {\n type: 'toolCall';\n id?: string;\n name?: string;\n arguments?: Record<string, unknown>;\n}\ninterface PiAssistantMessage {\n role: 'assistant';\n content?: Array<{ type: string; text?: string; name?: string; arguments?: unknown }>;\n model?: string;\n provider?: string;\n usage?: PiUsage;\n stopReason?: string;\n}\ninterface PiAgentEvent {\n type: string;\n message?: any;\n messages?: any[];\n toolCallId?: string;\n toolName?: string;\n args?: unknown;\n result?: unknown;\n isError?: boolean;\n}\n\ninterface PerAgentState {\n agentSpan?: Span;\n agentCtx?: Context;\n toolSpans: Map<string, Span>;\n /** Running conversation (system/user/tool turns) to use as LLM-span input.\n * Pi Agent's assistant message_end carries only the response, not the prompt. */\n inputMessages: Array<{ role: string; content: string }>;\n}\n\n/**\n * Subscribe neatlogs tracing to a Pi Agent instance. Returns the same agent\n * (marked so re-subscribing is a no-op). Idempotent per agent.\n */\nexport function piAgentHooks<T extends object>(agent: T): T {\n if (!agent || (agent as any)[PATCH_FLAG]) return agent;\n const a = agent as any;\n if (typeof a.subscribe !== 'function') return agent; // not a Pi Agent — leave alone\n\n const state: PerAgentState = { toolSpans: new Map(), inputMessages: [] };\n const tracer = trace.getTracer(TRACER_NAME);\n\n a.subscribe((event: PiAgentEvent) => {\n try {\n handleEvent(tracer, state, event);\n } catch {\n // never let tracing break the agent run\n }\n });\n\n markPatched(a);\n return agent;\n}\n\nfunction handleEvent(\n tracer: ReturnType<typeof trace.getTracer>,\n state: PerAgentState,\n event: PiAgentEvent,\n): void {\n switch (event.type) {\n case 'agent_start': {\n // Open the AGENT (run) span as the active span so children nest under it.\n const span = tracer.startSpan(\n 'pi_agent.run',\n { attributes: { 'neatlogs.span.kind': 'AGENT' } },\n otelContext.active(),\n );\n state.agentSpan = span;\n state.agentCtx = trace.setSpan(otelContext.active(), span);\n state.inputMessages = [];\n break;\n }\n\n case 'message_end': {\n const msg = event.message as any;\n if (!msg) return;\n if (msg.role === 'assistant') {\n // Assistant message = the LLM response. Emit an LLM span using the\n // accumulated conversation as input, then record the assistant turn too.\n emitLlmSpan(tracer, state, msg as PiAssistantMessage);\n const { text } = splitAssistantContent(msg.content);\n if (text) state.inputMessages.push({ role: 'assistant', content: text });\n } else {\n // user / toolResult turns — accumulate as input context for later LLM spans.\n const role = msg.role === 'toolResult' ? 'tool' : String(msg.role || 'user');\n const content = messageText(msg);\n if (content) state.inputMessages.push({ role, content });\n }\n break;\n }\n\n case 'tool_execution_start': {\n const parent = state.agentCtx ?? otelContext.active();\n const span = tracer.startSpan(\n `pi_agent.tool.${event.toolName ?? 'tool'}`,\n {\n attributes: {\n 'neatlogs.span.kind': 'TOOL',\n ...(event.toolName ? { 'neatlogs.tool.name': String(event.toolName) } : {}),\n ...(event.args !== undefined\n ? { 'input.value': safeStringify(event.args) }\n : {}),\n },\n },\n parent,\n );\n if (event.toolCallId) state.toolSpans.set(event.toolCallId, span);\n break;\n }\n\n case 'tool_execution_end': {\n const span = event.toolCallId ? state.toolSpans.get(event.toolCallId) : undefined;\n if (!span) return;\n if (event.result !== undefined) {\n span.setAttribute('output.value', safeStringify(event.result));\n }\n if (event.isError) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n span.setAttribute('neatlogs.tool.is_error', true);\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n span.end();\n if (event.toolCallId) state.toolSpans.delete(event.toolCallId);\n break;\n }\n\n case 'agent_end': {\n // Close any tool spans that never received an end event, then the agent span.\n for (const ts of state.toolSpans.values()) {\n try {\n ts.end();\n } catch {\n /* ignore */\n }\n }\n state.toolSpans.clear();\n if (state.agentSpan) {\n // Agent input = the first user message of the run; output = final answer.\n const firstUser = state.inputMessages.find((m) => m.role === 'user');\n if (firstUser) state.agentSpan.setAttribute('input.value', firstUser.content);\n const finalText = lastAssistantText(event.messages);\n if (finalText) state.agentSpan.setAttribute('output.value', finalText);\n state.agentSpan.setStatus({ code: SpanStatusCode.OK });\n state.agentSpan.end();\n state.agentSpan = undefined;\n state.agentCtx = undefined;\n }\n break;\n }\n\n default:\n break;\n }\n}\n\nfunction emitLlmSpan(\n tracer: ReturnType<typeof trace.getTracer>,\n state: PerAgentState,\n msg: PiAssistantMessage,\n): void {\n const attrs: Record<string, any> = { 'neatlogs.span.kind': 'LLM' };\n if (msg.model) attrs['neatlogs.llm.model_name'] = String(msg.model);\n if (msg.provider) attrs['neatlogs.llm.provider'] = String(msg.provider);\n if (msg.stopReason) attrs['neatlogs.llm.stop_reason'] = String(msg.stopReason);\n\n // Input = the conversation accumulated up to this assistant turn (system +\n // user + prior assistant/tool messages). Pi Agent's message_end doesn't carry\n // the prompt, so we reconstruct it from the running inputMessages list.\n const inMsgs = state.inputMessages;\n if (inMsgs.length) {\n inMsgs.forEach((m, i) => {\n attrs[`neatlogs.llm.input_messages.${i}.role`] = m.role;\n attrs[`neatlogs.llm.input_messages.${i}.content`] = m.content;\n });\n attrs['neatlogs.llm.input'] = safeStringify({ messages: inMsgs });\n attrs['input.value'] = safeStringify({ messages: inMsgs });\n }\n\n const { text, toolCalls } = splitAssistantContent(msg.content);\n // Output: text if present, else a readable tool-call summary so the span isn't blank.\n const outText = text || toolCalls.map((tc) => `${tc.name}(${safeStringify(tc.arguments)})`).join('\\n');\n if (outText || toolCalls.length) {\n attrs['neatlogs.llm.output_messages.0.role'] = 'assistant';\n attrs['neatlogs.llm.output_messages.0.content'] = (outText || '');\n const outBlob: Record<string, unknown> = { role: 'assistant', content: outText || '' };\n if (toolCalls.length) {\n outBlob.tool_calls = toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments }));\n toolCalls.forEach((tc, j) => {\n if (tc.name) attrs[`neatlogs.llm.tool_calls.${j}.name`] = tc.name;\n if (tc.arguments !== undefined)\n attrs[`neatlogs.llm.tool_calls.${j}.arguments`] = safeStringify(tc.arguments);\n if (tc.id) attrs[`neatlogs.llm.tool_calls.${j}.id`] = String(tc.id);\n });\n }\n attrs['neatlogs.llm.output'] = safeStringify(outBlob);\n attrs['output.value'] = (outText || '');\n }\n\n const usage = msg.usage;\n if (usage) {\n if (usage.input != null) attrs['neatlogs.llm.token_count.prompt'] = usage.input;\n if (usage.output != null) attrs['neatlogs.llm.token_count.completion'] = usage.output;\n const total = usage.totalTokens ?? ((usage.input ?? 0) + (usage.output ?? 0));\n if (total) attrs['neatlogs.llm.token_count.total'] = total;\n if (usage.cacheRead) attrs['neatlogs.llm.token_count.cache_read'] = usage.cacheRead;\n if (usage.cacheWrite) attrs['neatlogs.llm.token_count.cache_write'] = usage.cacheWrite;\n }\n\n const parent = state.agentCtx ?? otelContext.active();\n const span = tracer.startSpan(\n `pi_agent.llm.${msg.model || 'model'}`,\n { attributes: attrs },\n parent,\n );\n span.setStatus({ code: SpanStatusCode.OK });\n span.end();\n}\n\n// ---------------------------------------------------------------------------\n// Content helpers\n// ---------------------------------------------------------------------------\n\nfunction splitAssistantContent(\n content: PiAssistantMessage['content'],\n): { text: string; toolCalls: PiToolCall[] } {\n const texts: string[] = [];\n const toolCalls: PiToolCall[] = [];\n if (Array.isArray(content)) {\n for (const block of content) {\n if (!block || typeof block !== 'object') continue;\n if (block.type === 'text' && typeof block.text === 'string') texts.push(block.text);\n else if (block.type === 'toolCall')\n toolCalls.push(block as unknown as PiToolCall);\n // thinking blocks intentionally omitted from the main output text\n }\n } else if (typeof content === 'string') {\n texts.push(content);\n }\n return { text: texts.join(''), toolCalls };\n}\n\n/** Flatten any message's content (string or block array) to readable text. */\nfunction messageText(msg: any): string {\n if (!msg) return '';\n const c = msg.content;\n if (typeof c === 'string') return c;\n if (!Array.isArray(c)) return '';\n const parts: string[] = [];\n for (const block of c) {\n if (typeof block === 'string') parts.push(block);\n else if (block && typeof block === 'object') {\n if (typeof block.text === 'string') parts.push(block.text);\n else if (block.type === 'toolCall') parts.push(`${block.name ?? 'tool'}(${safeStringify(block.arguments)})`);\n }\n }\n return parts.join('');\n}\n\nfunction lastAssistantText(messages: any[] | undefined): string {\n if (!Array.isArray(messages)) return '';\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m && m.role === 'assistant') {\n const { text } = splitAssistantContent(m.content);\n if (text) return text;\n }\n }\n return '';\n}\n\nfunction markPatched(e: any): void {\n try {\n Object.defineProperty(e, PATCH_FLAG, { value: true, enumerable: false, configurable: true });\n } catch {\n e[PATCH_FLAG] = true;\n }\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value) ?? '';\n } catch {\n return '';\n }\n}\n"],"mappings":"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","names":[]}