@pontia/pi-client-plugin 0.2.0 → 0.2.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/README.md +17 -25
- package/package.json +26 -19
- package/src/context.ts +0 -104
- package/src/control-socket.ts +456 -0
- package/src/discovery.ts +2 -65
- package/src/events.ts +99 -20
- package/src/index.ts +504 -177
- package/src/live-output.ts +339 -0
- package/src/managed-runtime.ts +5 -1
- package/src/pi-message.ts +13 -3
- package/src/profile.ts +14 -22
- package/src/reporter.ts +62 -32
- package/src/runtime-binding.ts +22 -46
- package/src/session.ts +0 -1
- package/src/values.ts +9 -0
- package/src/workspace.ts +8 -13
- package/src/internal-api.ts +0 -29
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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:
|
|
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 =
|
|
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 (
|
|
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:
|
|
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(
|
|
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:
|
|
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:
|
|
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)
|
|
311
|
+
return hasObservedUsageValue(context_usage)
|
|
312
|
+
? { context_usage, model: primary.model ?? secondary.model }
|
|
313
|
+
: undefined;
|
|
254
314
|
}
|
|
255
315
|
|
|
256
|
-
export function contextUsageFromPiHook(
|
|
257
|
-
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
});
|