@raindrop-ai/pi-agent 0.0.2

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/index.js ADDED
@@ -0,0 +1,498 @@
1
+ import {
2
+ EventShipper,
3
+ TraceShipper,
4
+ attrInt,
5
+ attrString,
6
+ extractAssistantText,
7
+ extractModelName,
8
+ extractTokenUsage,
9
+ extractToolCallIds,
10
+ extractUserText,
11
+ formatToolSpanName,
12
+ getHostname,
13
+ getUsername,
14
+ libraryVersion,
15
+ randomUUID,
16
+ safeStringify,
17
+ truncate
18
+ } from "./chunk-LZIGXU2D.js";
19
+
20
+ // src/internal/subscriber.ts
21
+ function createSubscriber(agent, eventShipper, traceShipper, defaultOptions, options, debug) {
22
+ var _a, _b, _c, _d, _e;
23
+ const userId = (_a = options.userId) != null ? _a : defaultOptions.userId;
24
+ const convoId = (_b = options.convoId) != null ? _b : defaultOptions.convoId;
25
+ const eventName = (_c = options.eventName) != null ? _c : defaultOptions.eventName;
26
+ const properties = {
27
+ ...(_d = defaultOptions.properties) != null ? _d : {},
28
+ ...(_e = options.properties) != null ? _e : {}
29
+ };
30
+ let currentRun;
31
+ function log(msg) {
32
+ if (debug) console.log(`[raindrop-ai/pi-agent] ${msg}`);
33
+ }
34
+ function handleAgentStart() {
35
+ try {
36
+ if (currentRun) {
37
+ if (traceShipper) {
38
+ for (const [, span] of currentRun.toolSpans) {
39
+ traceShipper.endSpan(span);
40
+ }
41
+ }
42
+ if (currentRun.currentTurnSpan && traceShipper) {
43
+ traceShipper.endSpan(currentRun.currentTurnSpan);
44
+ }
45
+ if (currentRun.rootSpan && traceShipper) {
46
+ traceShipper.endSpan(currentRun.rootSpan, {
47
+ error: "Previous run was not finalized"
48
+ });
49
+ }
50
+ if (eventShipper) {
51
+ eventShipper.finish(currentRun.eventId, {
52
+ userId: userId != null ? userId : "anonymous",
53
+ properties: { sdk_version: libraryVersion, incomplete: true }
54
+ }).catch(() => {
55
+ });
56
+ }
57
+ currentRun = void 0;
58
+ }
59
+ const eventId = randomUUID();
60
+ const rootSpan = traceShipper ? traceShipper.startSpan({
61
+ name: "ai.event",
62
+ eventId,
63
+ attributes: [
64
+ attrString("hostname", getHostname()),
65
+ attrString("os", process.platform),
66
+ attrString("username", getUsername())
67
+ ]
68
+ }) : void 0;
69
+ currentRun = {
70
+ eventId,
71
+ rootSpan,
72
+ currentInput: "",
73
+ outputParts: [],
74
+ toolSpans: /* @__PURE__ */ new Map(),
75
+ toolArgs: /* @__PURE__ */ new Map(),
76
+ toolCallToLlmSpan: /* @__PURE__ */ new Map(),
77
+ lastModel: "",
78
+ turnNumber: 0,
79
+ totalInputTokens: 0,
80
+ totalOutputTokens: 0,
81
+ totalCacheReadTokens: 0
82
+ };
83
+ if (eventShipper) {
84
+ eventShipper.patch(eventId, {
85
+ isPending: true,
86
+ userId: userId != null ? userId : "anonymous",
87
+ convoId,
88
+ eventName,
89
+ properties: {
90
+ ...properties,
91
+ sdk_version: libraryVersion,
92
+ hostname: getHostname(),
93
+ os: process.platform
94
+ }
95
+ }).catch(() => {
96
+ });
97
+ }
98
+ } catch (err) {
99
+ log(`Error in agent_start handler: ${err instanceof Error ? err.message : String(err)}`);
100
+ }
101
+ }
102
+ function handleMessageEnd(message) {
103
+ try {
104
+ if (!currentRun) return;
105
+ const userText = extractUserText(message);
106
+ if (userText !== void 0) {
107
+ currentRun.currentInput = userText;
108
+ if (eventShipper) {
109
+ eventShipper.patch(currentRun.eventId, { input: userText }).catch(() => {
110
+ });
111
+ }
112
+ return;
113
+ }
114
+ if (!("role" in message) || message.role !== "assistant") return;
115
+ const model = extractModelName(message);
116
+ const usage = extractTokenUsage(message);
117
+ const assistantText = extractAssistantText(message);
118
+ if (model) currentRun.lastModel = model;
119
+ if (usage) {
120
+ currentRun.totalInputTokens += usage.input;
121
+ currentRun.totalOutputTokens += usage.output;
122
+ if (usage.cacheRead) currentRun.totalCacheReadTokens += usage.cacheRead;
123
+ }
124
+ if (!traceShipper || !currentRun.currentTurnSpan) return;
125
+ const rawProvider = "provider" in message && typeof message.provider === "string" ? message.provider : void 0;
126
+ const rawModelId = typeof message.model === "string" ? message.model : void 0;
127
+ const stopReason = "stopReason" in message && typeof message.stopReason === "string" ? message.stopReason : void 0;
128
+ const llmAttrs = [
129
+ attrString("ai.operationId", "generateText")
130
+ ];
131
+ if (rawProvider) {
132
+ llmAttrs.push(attrString("gen_ai.system", rawProvider));
133
+ }
134
+ if (rawModelId) {
135
+ llmAttrs.push(
136
+ attrString("gen_ai.response.model", rawModelId),
137
+ attrString("gen_ai.request.model", rawModelId)
138
+ );
139
+ }
140
+ if (usage) {
141
+ llmAttrs.push(
142
+ attrInt("gen_ai.usage.input_tokens", usage.input),
143
+ attrInt("gen_ai.usage.output_tokens", usage.output)
144
+ );
145
+ if (usage.cacheRead) {
146
+ llmAttrs.push(attrInt("gen_ai.usage.cache_read_tokens", usage.cacheRead));
147
+ }
148
+ }
149
+ if (assistantText) {
150
+ llmAttrs.push(attrString("ai.response.text", truncate(assistantText)));
151
+ }
152
+ if (currentRun.currentInput) {
153
+ llmAttrs.push(attrString("ai.prompt", truncate(currentRun.currentInput)));
154
+ }
155
+ if (stopReason) {
156
+ llmAttrs.push(attrString("ai.stop_reason", stopReason));
157
+ }
158
+ const llmSpan = traceShipper.startSpan({
159
+ name: model != null ? model : "llm",
160
+ parent: currentRun.currentTurnSpan.ids,
161
+ eventId: currentRun.eventId,
162
+ attributes: llmAttrs
163
+ });
164
+ const errorForSpan = stopReason === "error" || stopReason === "aborted" ? "errorMessage" in message && typeof message.errorMessage === "string" ? message.errorMessage : `Assistant ${stopReason}` : void 0;
165
+ traceShipper.endSpan(llmSpan, errorForSpan ? { error: errorForSpan } : void 0);
166
+ const toolCallIds = extractToolCallIds(message);
167
+ for (const tcId of toolCallIds) {
168
+ currentRun.toolCallToLlmSpan.set(tcId, llmSpan);
169
+ }
170
+ } catch (err) {
171
+ log(`Error in message_end handler: ${err instanceof Error ? err.message : String(err)}`);
172
+ }
173
+ }
174
+ function handleMessageUpdate(event) {
175
+ try {
176
+ if (!currentRun) return;
177
+ const ame = event.assistantMessageEvent;
178
+ if (ame.type === "text_delta" && "delta" in ame && typeof ame.delta === "string") {
179
+ currentRun.outputParts.push(ame.delta);
180
+ }
181
+ } catch (err) {
182
+ log(`Error in message_update handler: ${err instanceof Error ? err.message : String(err)}`);
183
+ }
184
+ }
185
+ function handleTurnStart() {
186
+ try {
187
+ if (!currentRun) return;
188
+ currentRun.outputParts = [];
189
+ if (!traceShipper || !currentRun.rootSpan) return;
190
+ if (currentRun.currentTurnSpan) {
191
+ traceShipper.endSpan(currentRun.currentTurnSpan);
192
+ }
193
+ currentRun.turnNumber += 1;
194
+ currentRun.currentTurnSpan = traceShipper.startSpan({
195
+ name: `Turn ${currentRun.turnNumber}`,
196
+ parent: currentRun.rootSpan.ids,
197
+ eventId: currentRun.eventId,
198
+ attributes: [
199
+ attrString("ai.operationId", "ai.turn"),
200
+ attrInt("ai.turn_number", currentRun.turnNumber)
201
+ ]
202
+ });
203
+ } catch (err) {
204
+ log(`Error in turn_start handler: ${err instanceof Error ? err.message : String(err)}`);
205
+ }
206
+ }
207
+ function handleTurnEnd() {
208
+ try {
209
+ if (!currentRun || !traceShipper || !currentRun.currentTurnSpan) return;
210
+ traceShipper.endSpan(currentRun.currentTurnSpan);
211
+ currentRun.currentTurnSpan = void 0;
212
+ } catch (err) {
213
+ log(`Error in turn_end handler: ${err instanceof Error ? err.message : String(err)}`);
214
+ }
215
+ }
216
+ function handleToolExecutionStart(toolCallId, toolName, args) {
217
+ var _a2;
218
+ try {
219
+ if (!currentRun) return;
220
+ currentRun.toolArgs.set(toolCallId, args);
221
+ if (!traceShipper) return;
222
+ const parentLlmSpan = currentRun.toolCallToLlmSpan.get(toolCallId);
223
+ const parentSpan = (_a2 = parentLlmSpan != null ? parentLlmSpan : currentRun.currentTurnSpan) != null ? _a2 : currentRun.rootSpan;
224
+ if (!parentSpan) return;
225
+ const toolSpan = traceShipper.startSpan({
226
+ name: formatToolSpanName(toolName, args),
227
+ parent: parentSpan.ids,
228
+ eventId: currentRun.eventId,
229
+ attributes: [
230
+ attrString("ai.operationId", "ai.toolCall"),
231
+ attrString("ai.toolCall.name", toolName),
232
+ attrString("ai.toolCall.id", toolCallId)
233
+ ]
234
+ });
235
+ currentRun.toolSpans.set(toolCallId, toolSpan);
236
+ } catch (err) {
237
+ log(`Error in tool_execution_start handler: ${err instanceof Error ? err.message : String(err)}`);
238
+ }
239
+ }
240
+ function handleToolExecutionEnd(toolCallId, toolName, result, isError) {
241
+ try {
242
+ if (!currentRun) return;
243
+ const toolSpan = currentRun.toolSpans.get(toolCallId);
244
+ currentRun.toolSpans.delete(toolCallId);
245
+ currentRun.toolCallToLlmSpan.delete(toolCallId);
246
+ const args = currentRun.toolArgs.get(toolCallId);
247
+ currentRun.toolArgs.delete(toolCallId);
248
+ if (!traceShipper || !toolSpan) return;
249
+ const endAttrs = [];
250
+ const argsStr = truncate(safeStringify(args));
251
+ if (argsStr) endAttrs.push(attrString("ai.toolCall.args", argsStr));
252
+ const resultStr = truncate(safeStringify(result));
253
+ if (resultStr) endAttrs.push(attrString("ai.toolCall.result", resultStr));
254
+ traceShipper.endSpan(toolSpan, {
255
+ attributes: endAttrs,
256
+ ...isError ? { error: `Tool "${toolName}" failed` } : {}
257
+ });
258
+ } catch (err) {
259
+ log(`Error in tool_execution_end handler: ${err instanceof Error ? err.message : String(err)}`);
260
+ }
261
+ }
262
+ function handleAgentEnd() {
263
+ try {
264
+ if (!currentRun) return;
265
+ const run = currentRun;
266
+ currentRun = void 0;
267
+ if (traceShipper) {
268
+ for (const [, span] of run.toolSpans) {
269
+ traceShipper.endSpan(span);
270
+ }
271
+ }
272
+ run.toolSpans.clear();
273
+ run.toolCallToLlmSpan.clear();
274
+ if (run.currentTurnSpan && traceShipper) {
275
+ traceShipper.endSpan(run.currentTurnSpan);
276
+ run.currentTurnSpan = void 0;
277
+ }
278
+ const outputText = run.outputParts.join("");
279
+ if (traceShipper && run.rootSpan) {
280
+ const rootAttrs = [
281
+ attrString("ai.operationId", "generateText")
282
+ ];
283
+ if (run.lastModel) {
284
+ run.rootSpan.name = run.lastModel;
285
+ rootAttrs.push(attrString("gen_ai.response.model", run.lastModel));
286
+ }
287
+ if (run.currentInput) {
288
+ rootAttrs.push(attrString("ai.prompt", truncate(run.currentInput)));
289
+ }
290
+ if (outputText) {
291
+ rootAttrs.push(attrString("ai.response.text", truncate(outputText)));
292
+ }
293
+ if (run.totalInputTokens > 0) {
294
+ rootAttrs.push(attrInt("gen_ai.usage.input_tokens", run.totalInputTokens));
295
+ }
296
+ if (run.totalOutputTokens > 0) {
297
+ rootAttrs.push(attrInt("gen_ai.usage.output_tokens", run.totalOutputTokens));
298
+ }
299
+ if (run.totalCacheReadTokens > 0) {
300
+ rootAttrs.push(attrInt("gen_ai.usage.cache_read_tokens", run.totalCacheReadTokens));
301
+ }
302
+ rootAttrs.push(attrInt("ai.total_turns", run.turnNumber));
303
+ traceShipper.endSpan(run.rootSpan, { attributes: rootAttrs });
304
+ }
305
+ if (eventShipper) {
306
+ eventShipper.finish(run.eventId, {
307
+ userId: userId != null ? userId : "anonymous",
308
+ model: run.lastModel || void 0,
309
+ output: outputText || void 0,
310
+ properties: {
311
+ ...properties,
312
+ sdk_version: libraryVersion,
313
+ ...run.totalInputTokens > 0 ? { "ai.usage.prompt_tokens": run.totalInputTokens } : {},
314
+ ...run.totalOutputTokens > 0 ? { "ai.usage.completion_tokens": run.totalOutputTokens } : {},
315
+ ...run.totalCacheReadTokens > 0 ? { "ai.usage.cache_read_tokens": run.totalCacheReadTokens } : {}
316
+ }
317
+ }).catch(() => {
318
+ });
319
+ }
320
+ Promise.all([
321
+ eventShipper == null ? void 0 : eventShipper.flush(),
322
+ traceShipper == null ? void 0 : traceShipper.flush()
323
+ ]).catch(() => {
324
+ });
325
+ } catch (err) {
326
+ log(`Error in agent_end handler: ${err instanceof Error ? err.message : String(err)}`);
327
+ }
328
+ }
329
+ const unsubscribe = agent.subscribe((event) => {
330
+ try {
331
+ switch (event.type) {
332
+ case "agent_start":
333
+ handleAgentStart();
334
+ break;
335
+ case "message_end":
336
+ handleMessageEnd(event.message);
337
+ break;
338
+ case "message_update":
339
+ handleMessageUpdate(event);
340
+ break;
341
+ case "turn_start":
342
+ handleTurnStart();
343
+ break;
344
+ case "turn_end":
345
+ handleTurnEnd();
346
+ break;
347
+ case "tool_execution_start":
348
+ handleToolExecutionStart(event.toolCallId, event.toolName, event.args);
349
+ break;
350
+ case "tool_execution_end":
351
+ handleToolExecutionEnd(
352
+ event.toolCallId,
353
+ event.toolName,
354
+ event.result,
355
+ event.isError
356
+ );
357
+ break;
358
+ case "agent_end":
359
+ handleAgentEnd();
360
+ break;
361
+ }
362
+ } catch (err) {
363
+ log(`Unhandled error in event handler: ${err instanceof Error ? err.message : String(err)}`);
364
+ }
365
+ });
366
+ return unsubscribe;
367
+ }
368
+
369
+ // src/index.ts
370
+ function envDebugEnabled() {
371
+ var _a;
372
+ if (typeof process === "undefined") return false;
373
+ const flag = (_a = process.env) == null ? void 0 : _a.RAINDROP_AI_DEBUG;
374
+ return flag === "1" || flag === "true";
375
+ }
376
+ function createRaindropPiAgent(opts) {
377
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
378
+ const hasWriteKey = typeof opts.writeKey === "string" && opts.writeKey.trim().length > 0;
379
+ const eventsEnabled = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
380
+ const tracesEnabled = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
381
+ if (!hasWriteKey && !opts.endpoint) {
382
+ console.warn(
383
+ "[raindrop-ai/pi-agent] writeKey not provided; telemetry shipping is disabled"
384
+ );
385
+ }
386
+ const envDebug = envDebugEnabled();
387
+ const debug = ((_c = opts.events) == null ? void 0 : _c.debug) === true || ((_d = opts.traces) == null ? void 0 : _d.debug) === true || envDebug;
388
+ const eventShipper = eventsEnabled && (hasWriteKey || opts.endpoint) ? new EventShipper({
389
+ writeKey: opts.writeKey,
390
+ endpoint: opts.endpoint,
391
+ enabled: true,
392
+ debug: ((_e = opts.events) == null ? void 0 : _e.debug) === true || envDebug,
393
+ partialFlushMs: (_f = opts.events) == null ? void 0 : _f.partialFlushMs
394
+ }) : null;
395
+ const traceShipper = tracesEnabled && (hasWriteKey || opts.endpoint) ? new TraceShipper({
396
+ writeKey: opts.writeKey,
397
+ endpoint: opts.endpoint,
398
+ enabled: true,
399
+ debug: ((_g = opts.traces) == null ? void 0 : _g.debug) === true || envDebug,
400
+ debugSpans: ((_h = opts.traces) == null ? void 0 : _h.debugSpans) === true || envDebug,
401
+ flushIntervalMs: (_i = opts.traces) == null ? void 0 : _i.flushIntervalMs,
402
+ maxBatchSize: (_j = opts.traces) == null ? void 0 : _j.maxBatchSize,
403
+ maxQueueSize: (_k = opts.traces) == null ? void 0 : _k.maxQueueSize
404
+ }) : null;
405
+ const defaultOptions = {
406
+ userId: opts.userId,
407
+ convoId: opts.convoId,
408
+ eventName: opts.eventName,
409
+ properties: opts.properties
410
+ };
411
+ return {
412
+ subscribe(agent, options) {
413
+ return createSubscriber(
414
+ agent,
415
+ eventShipper,
416
+ traceShipper,
417
+ defaultOptions,
418
+ options != null ? options : {},
419
+ debug
420
+ );
421
+ },
422
+ events: {
423
+ async patch(eventId, data) {
424
+ if (!eventShipper) return;
425
+ await eventShipper.patch(eventId, data);
426
+ },
427
+ async addAttachments(eventId, attachments) {
428
+ if (!eventShipper) return;
429
+ await eventShipper.patch(eventId, { attachments });
430
+ },
431
+ async setProperties(eventId, properties) {
432
+ if (!eventShipper) return;
433
+ await eventShipper.patch(eventId, { properties });
434
+ },
435
+ async finish(eventId) {
436
+ if (!eventShipper) return;
437
+ await eventShipper.finish(eventId, {});
438
+ }
439
+ },
440
+ users: {
441
+ async identify(params) {
442
+ var _a2;
443
+ if (!eventShipper) return;
444
+ await eventShipper.identify([
445
+ { userId: params.userId, traits: (_a2 = params.traits) != null ? _a2 : {} }
446
+ ]);
447
+ }
448
+ },
449
+ signals: {
450
+ async track(params) {
451
+ if (!eventShipper) return;
452
+ if (!params.name || !params.name.trim()) {
453
+ console.warn("[raindrop-ai/pi-agent] signal name is required");
454
+ return;
455
+ }
456
+ const {
457
+ eventId,
458
+ name,
459
+ type,
460
+ sentiment,
461
+ timestamp,
462
+ attachmentId,
463
+ comment,
464
+ after,
465
+ ...otherProperties
466
+ } = params;
467
+ await eventShipper.trackSignal({
468
+ eventId: eventId != null ? eventId : "",
469
+ name,
470
+ type,
471
+ sentiment,
472
+ timestamp,
473
+ attachmentId,
474
+ comment,
475
+ after,
476
+ properties: otherProperties
477
+ });
478
+ }
479
+ },
480
+ async flush() {
481
+ var _a2, _b2;
482
+ await Promise.all([
483
+ (_a2 = eventShipper == null ? void 0 : eventShipper.flush()) != null ? _a2 : Promise.resolve(),
484
+ (_b2 = traceShipper == null ? void 0 : traceShipper.flush()) != null ? _b2 : Promise.resolve()
485
+ ]);
486
+ },
487
+ async shutdown() {
488
+ var _a2, _b2;
489
+ await Promise.all([
490
+ (_a2 = eventShipper == null ? void 0 : eventShipper.shutdown()) != null ? _a2 : Promise.resolve(),
491
+ (_b2 = traceShipper == null ? void 0 : traceShipper.shutdown()) != null ? _b2 : Promise.resolve()
492
+ ]);
493
+ }
494
+ };
495
+ }
496
+ export {
497
+ createRaindropPiAgent
498
+ };
package/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@raindrop-ai/pi-agent",
3
+ "version": "0.0.2",
4
+ "description": "Raindrop observability for Pi Agent — automatic tracing via subscriber or pi-coding-agent extension",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/raindrop-ai/raindrop-js.git",
10
+ "directory": "packages/pi-agent"
11
+ },
12
+ "homepage": "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/pi-agent#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/raindrop-ai/raindrop-js/issues"
15
+ },
16
+ "keywords": [
17
+ "pi-package",
18
+ "raindrop",
19
+ "pi-agent",
20
+ "observability",
21
+ "tracing"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "require": "./dist/index.cjs"
28
+ },
29
+ "./extension": {
30
+ "types": "./dist/extension.d.ts",
31
+ "import": "./dist/extension.js",
32
+ "require": "./dist/extension.cjs"
33
+ }
34
+ },
35
+ "pi": {
36
+ "extensions": [
37
+ "./dist/extension.js"
38
+ ]
39
+ },
40
+ "sideEffects": false,
41
+ "files": [
42
+ "dist/**",
43
+ "README.md"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "dev": "tsup --watch",
48
+ "clean": "rm -rf dist",
49
+ "test": "vitest run",
50
+ "test:watch": "vitest"
51
+ },
52
+ "peerDependencies": {
53
+ "@mariozechner/pi-agent-core": ">=0.60.0",
54
+ "@mariozechner/pi-coding-agent": ">=0.65.2"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "@mariozechner/pi-coding-agent": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "devDependencies": {
62
+ "@raindrop-ai/core": "workspace:*",
63
+ "@mariozechner/pi-agent-core": "^0.66.0",
64
+ "@mariozechner/pi-coding-agent": "^0.66.0",
65
+ "@types/node": "^20.11.17",
66
+ "msw": "^2.12.7",
67
+ "tsup": "^8.5.1",
68
+ "typescript": "^5.7.3",
69
+ "vitest": "^2.1.9"
70
+ },
71
+ "tsup": {
72
+ "entry": [
73
+ "src/index.ts",
74
+ "src/extension.ts"
75
+ ],
76
+ "format": [
77
+ "cjs",
78
+ "esm"
79
+ ],
80
+ "dts": {
81
+ "resolve": true
82
+ },
83
+ "clean": true,
84
+ "noExternal": [
85
+ "@raindrop-ai/core"
86
+ ]
87
+ },
88
+ "publishConfig": {
89
+ "access": "public"
90
+ }
91
+ }