@pontia/pi-client-plugin 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/events.ts CHANGED
@@ -1,9 +1,20 @@
1
1
  import type { TurnContext } from "./context.js";
2
2
  import type { SessionContext } from "./session.js";
3
3
 
4
+ const MAX_TURN_INPUT_CHARS = 200;
4
5
  const MAX_TURN_OUTPUT_CHARS = 200;
5
6
 
6
- export type InternalEventType = "session.ready" | "session.exited" | "session.message_updated" | "session.context_usage_updated" | "turn.started" | "turn.output" | "turn.completed" | "turn.failed" | "turn.interrupted";
7
+ export type InternalEventType =
8
+ | "session.ready"
9
+ | "session.exited"
10
+ | "session.model_updated"
11
+ | "session.message_updated"
12
+ | "session.context_usage_updated"
13
+ | "turn.started"
14
+ | "turn.output"
15
+ | "turn.completed"
16
+ | "turn.failed"
17
+ | "turn.interrupted";
7
18
 
8
19
  export interface ContextUsagePayload {
9
20
  used_tokens: number | null;
@@ -57,11 +68,18 @@ export interface InternalEvent {
57
68
 
58
69
  type ActiveTurnContext = TurnContext & { turnId: string };
59
70
 
60
- function turnFact(context: ActiveTurnContext, type: InternalEventType, data: Record<string, unknown>): InternalEvent {
71
+ function turnFact(
72
+ context: ActiveTurnContext,
73
+ type: InternalEventType,
74
+ data: Record<string, unknown>,
75
+ ): InternalEvent {
61
76
  return { session_id: context.sessionId, turn_id: context.turnId, type, data };
62
77
  }
63
78
 
64
- export function buildSessionMessageUpdatedEvent(context: TurnContext, reason: SessionMessageUpdatedReason): InternalEvent {
79
+ export function buildSessionMessageUpdatedEvent(
80
+ context: TurnContext,
81
+ reason: SessionMessageUpdatedReason,
82
+ ): InternalEvent {
65
83
  return {
66
84
  session_id: context.sessionId,
67
85
  type: "session.message_updated",
@@ -69,7 +87,11 @@ export function buildSessionMessageUpdatedEvent(context: TurnContext, reason: Se
69
87
  };
70
88
  }
71
89
 
72
- export function buildSessionContextUsageUpdatedEvent(context: TurnContext, usage: ContextUsagePayload, model?: string | null): InternalEvent {
90
+ export function buildSessionContextUsageUpdatedEvent(
91
+ context: TurnContext,
92
+ usage: ContextUsagePayload,
93
+ model?: string | null,
94
+ ): InternalEvent {
73
95
  const contextUsage: Record<string, unknown> = {};
74
96
  for (const [key, value] of Object.entries(usage)) {
75
97
  if (value !== null) contextUsage[key] = value;
@@ -110,11 +132,23 @@ function modelNameFromRecord(record: Record<string, unknown>): string | null | u
110
132
  const model = record.model;
111
133
  if (!model || typeof model !== "object" || Array.isArray(model)) return null;
112
134
  const modelRecord = model as Record<string, unknown>;
113
- return optionalNullableString(modelRecord.id) ?? optionalNullableString(modelRecord.modelId) ?? optionalNullableString(modelRecord.name);
135
+ return (
136
+ optionalNullableString(modelRecord.id) ??
137
+ optionalNullableString(modelRecord.modelId) ??
138
+ optionalNullableString(modelRecord.name)
139
+ );
114
140
  }
115
141
 
116
142
  function hasObservedUsageValue(usage: ContextUsagePayload): boolean {
117
- return usage.used_tokens !== null || usage.max_tokens !== null || usage.remaining_tokens !== null || usage.usage_ratio !== null || usage.input_tokens !== null || usage.output_tokens !== null || usage.cache_tokens !== null;
143
+ return (
144
+ usage.used_tokens !== null ||
145
+ usage.max_tokens !== null ||
146
+ usage.remaining_tokens !== null ||
147
+ usage.usage_ratio !== null ||
148
+ usage.input_tokens !== null ||
149
+ usage.output_tokens !== null ||
150
+ usage.cache_tokens !== null
151
+ );
118
152
  }
119
153
 
120
154
  function contextUsageFromPontiaShape(raw: unknown): ContextUsagePayload | undefined {
@@ -187,7 +221,10 @@ function contextUsageFromPiMessage(message: unknown): ContextUsageObservation |
187
221
  usage_ratio: null,
188
222
  input_tokens,
189
223
  output_tokens,
190
- cache_tokens: cache_read_tokens === null && cache_write_tokens === null ? null : (cache_read_tokens ?? 0) + (cache_write_tokens ?? 0),
224
+ cache_tokens:
225
+ cache_read_tokens === null && cache_write_tokens === null
226
+ ? null
227
+ : (cache_read_tokens ?? 0) + (cache_write_tokens ?? 0),
191
228
  confidence: "estimated",
192
229
  };
193
230
  return hasObservedUsageValue(context_usage) ? { context_usage, model } : undefined;
@@ -209,17 +246,28 @@ export function contextUsageFromPiContext(ctx: unknown): ContextUsageObservation
209
246
  const usage = rawUsage as Record<string, unknown>;
210
247
  const used_tokens = optionalNonNegativeInteger(usage.tokens);
211
248
  const max_tokens = optionalNonNegativeInteger(usage.contextWindow);
212
- const percent = usage.percent == null ? null : typeof usage.percent === "number" && usage.percent >= 0 && usage.percent <= 100 ? usage.percent : undefined;
249
+ const percent =
250
+ usage.percent == null
251
+ ? null
252
+ : typeof usage.percent === "number" && usage.percent >= 0 && usage.percent <= 100
253
+ ? usage.percent
254
+ : undefined;
213
255
  const model = modelNameFromRecord(record);
214
256
 
215
- if (used_tokens === undefined || max_tokens === undefined || percent === undefined || model === undefined) {
257
+ if (
258
+ used_tokens === undefined ||
259
+ max_tokens === undefined ||
260
+ percent === undefined ||
261
+ model === undefined
262
+ ) {
216
263
  return undefined;
217
264
  }
218
265
 
219
266
  const context_usage: ContextUsagePayload = {
220
267
  used_tokens,
221
268
  max_tokens,
222
- remaining_tokens: used_tokens !== null && max_tokens !== null ? Math.max(0, max_tokens - used_tokens) : null,
269
+ remaining_tokens:
270
+ used_tokens !== null && max_tokens !== null ? Math.max(0, max_tokens - used_tokens) : null,
223
271
  usage_ratio: percent === null ? null : percent / 100,
224
272
  input_tokens: null,
225
273
  output_tokens: null,
@@ -237,24 +285,42 @@ export function contextUsageFromPiEvent(event: unknown): ContextUsageObservation
237
285
  return contextUsageFromPiMessage(record.message);
238
286
  }
239
287
 
240
- function mergeContextUsageObservations(primary: ContextUsageObservation | undefined, secondary: ContextUsageObservation | undefined): ContextUsageObservation | undefined {
288
+ function mergeContextUsageObservations(
289
+ primary: ContextUsageObservation | undefined,
290
+ secondary: ContextUsageObservation | undefined,
291
+ ): ContextUsageObservation | undefined {
241
292
  if (!primary) return secondary;
242
293
  if (!secondary) return primary;
243
294
  const context_usage: ContextUsagePayload = {
244
295
  used_tokens: secondary.context_usage.used_tokens ?? primary.context_usage.used_tokens,
245
296
  max_tokens: secondary.context_usage.max_tokens ?? primary.context_usage.max_tokens,
246
- remaining_tokens: secondary.context_usage.remaining_tokens ?? primary.context_usage.remaining_tokens,
297
+ remaining_tokens:
298
+ secondary.context_usage.remaining_tokens ?? primary.context_usage.remaining_tokens,
247
299
  usage_ratio: secondary.context_usage.usage_ratio ?? primary.context_usage.usage_ratio,
248
300
  input_tokens: primary.context_usage.input_tokens ?? secondary.context_usage.input_tokens,
249
301
  output_tokens: primary.context_usage.output_tokens ?? secondary.context_usage.output_tokens,
250
302
  cache_tokens: primary.context_usage.cache_tokens ?? secondary.context_usage.cache_tokens,
251
- confidence: primary.context_usage.confidence === "exact" || secondary.context_usage.confidence === "exact" ? "exact" : primary.context_usage.confidence === "estimated" || secondary.context_usage.confidence === "estimated" ? "estimated" : "unknown",
303
+ confidence:
304
+ primary.context_usage.confidence === "exact" || secondary.context_usage.confidence === "exact"
305
+ ? "exact"
306
+ : primary.context_usage.confidence === "estimated" ||
307
+ secondary.context_usage.confidence === "estimated"
308
+ ? "estimated"
309
+ : "unknown",
252
310
  };
253
- return hasObservedUsageValue(context_usage) ? { context_usage, model: primary.model ?? secondary.model } : undefined;
311
+ return hasObservedUsageValue(context_usage)
312
+ ? { context_usage, model: primary.model ?? secondary.model }
313
+ : undefined;
254
314
  }
255
315
 
256
- export function contextUsageFromPiHook(event: unknown, ctx?: unknown): ContextUsageObservation | undefined {
257
- return mergeContextUsageObservations(contextUsageFromPiEvent(event), contextUsageFromPiContext(ctx));
316
+ export function contextUsageFromPiHook(
317
+ event: unknown,
318
+ ctx?: unknown,
319
+ ): ContextUsageObservation | undefined {
320
+ return mergeContextUsageObservations(
321
+ contextUsageFromPiEvent(event),
322
+ contextUsageFromPiContext(ctx),
323
+ );
258
324
  }
259
325
 
260
326
  export function buildTurnStartedEvent(
@@ -264,7 +330,10 @@ export function buildTurnStartedEvent(
264
330
  ): InternalEvent {
265
331
  const data: Record<string, unknown> = {
266
332
  runtime_instance_id: context.runtimeInstanceId,
267
- input_summary: context.input,
333
+ input_summary:
334
+ context.input === undefined
335
+ ? undefined
336
+ : Array.from(context.input).slice(0, MAX_TURN_INPUT_CHARS).join(""),
268
337
  previous_leaf_id: previousLeafId,
269
338
  };
270
339
  if (context.inboxMessageId) data.inbox_message_id = context.inboxMessageId;
@@ -278,20 +347,30 @@ export function buildTurnOutputEvent(context: ActiveTurnContext, output: string)
278
347
  });
279
348
  }
280
349
 
281
- export function buildTurnCompletedEvent(context: ActiveTurnContext, terminalLeafId: string | null = null): InternalEvent {
350
+ export function buildTurnCompletedEvent(
351
+ context: ActiveTurnContext,
352
+ terminalLeafId: string | null = null,
353
+ ): InternalEvent {
282
354
  return turnFact(context, "turn.completed", {
283
355
  terminal_leaf_id: terminalLeafId,
284
356
  });
285
357
  }
286
358
 
287
- export function buildTurnFailedEvent(context: ActiveTurnContext, message: string, terminalLeafId: string | null = null): InternalEvent {
359
+ export function buildTurnFailedEvent(
360
+ context: ActiveTurnContext,
361
+ message: string,
362
+ terminalLeafId: string | null = null,
363
+ ): InternalEvent {
288
364
  return turnFact(context, "turn.failed", {
289
365
  failure_message: message,
290
366
  terminal_leaf_id: terminalLeafId,
291
367
  });
292
368
  }
293
369
 
294
- export function buildTurnInterruptedEvent(context: ActiveTurnContext, terminalLeafId: string | null = null): InternalEvent {
370
+ export function buildTurnInterruptedEvent(
371
+ context: ActiveTurnContext,
372
+ terminalLeafId: string | null = null,
373
+ ): InternalEvent {
295
374
  return turnFact(context, "turn.interrupted", {
296
375
  terminal_leaf_id: terminalLeafId,
297
376
  });