@omercnet/paseo-omp 0.4.0-next.129.1 → 0.4.0-next.131.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-omp",
3
- "version": "0.4.0-next.129.1",
3
+ "version": "0.4.0-next.131.1",
4
4
  "type": "module",
5
5
  "description": "Paseo integration for OMP, including its direct provider and workspace tooling.",
6
6
  "license": "MIT",
@@ -1,2 +1,2 @@
1
1
  /** Release Please keeps this bundle-safe constant aligned with package.json. */
2
- export const PASEO_OMP_PACKAGE_VERSION = "0.4.0-next.129.1"; // x-release-please-version
2
+ export const PASEO_OMP_PACKAGE_VERSION = "0.4.0-next.131.1"; // x-release-please-version
@@ -159,6 +159,7 @@ export class OmpRpcProcess {
159
159
  private readonly terminateProcessTree: (pid: number) => Promise<ProcessTreeCleanup>;
160
160
  private readonly streamedBlocks = new Map<number, string>();
161
161
  private readonly activeToolCallIds = new Set<string>();
162
+ private readonly announcedToolCallIds = new Set<string>();
162
163
  private pendingWriteBytes = 0;
163
164
  private commandTextLength = 0;
164
165
  private lineParts: Buffer[] = [];
@@ -1112,6 +1113,7 @@ export class OmpRpcProcess {
1112
1113
  if (event.data.type === "turn_end") {
1113
1114
  this.commandTextLength = 0;
1114
1115
  this.activeToolCallIds.clear();
1116
+ this.announcedToolCallIds.clear();
1115
1117
  }
1116
1118
  }
1117
1119
 
@@ -1133,6 +1135,7 @@ export class OmpRpcProcess {
1133
1135
  this.streamedBlocks.clear();
1134
1136
  this.commandTextLength = 0;
1135
1137
  this.activeToolCallIds.clear();
1138
+ this.announcedToolCallIds.clear();
1136
1139
  return true;
1137
1140
  }
1138
1141
  if (event.type === "command_output") {
@@ -1151,12 +1154,19 @@ export class OmpRpcProcess {
1151
1154
  this.activeToolCallIds.add(event.toolCallId);
1152
1155
  return true;
1153
1156
  }
1154
- if (event.type === "tool_execution_update" || event.type === "tool_stream_update") {
1157
+ if (event.type === "tool_stream_update") {
1158
+ return (
1159
+ this.activeToolCallIds.has(event.toolCallId) ||
1160
+ this.announcedToolCallIds.has(event.toolCallId)
1161
+ );
1162
+ }
1163
+ if (event.type === "tool_execution_update") {
1155
1164
  return this.activeToolCallIds.has(event.toolCallId);
1156
1165
  }
1157
1166
  if (event.type === "tool_execution_end") {
1158
1167
  if (!this.activeToolCallIds.has(event.toolCallId)) return false;
1159
1168
  this.activeToolCallIds.delete(event.toolCallId);
1169
+ this.announcedToolCallIds.delete(event.toolCallId);
1160
1170
  return true;
1161
1171
  }
1162
1172
  if (
@@ -1174,6 +1184,14 @@ export class OmpRpcProcess {
1174
1184
  nextBlocks.set(0, content);
1175
1185
  } else if (Array.isArray(content)) {
1176
1186
  for (const [index, part] of content.entries()) {
1187
+ if (
1188
+ part.type === "toolCall" &&
1189
+ typeof part.id === "string" &&
1190
+ (this.announcedToolCallIds.has(part.id) ||
1191
+ this.announcedToolCallIds.size < MAX_ACTIVE_TOOLS)
1192
+ ) {
1193
+ this.announcedToolCallIds.add(part.id);
1194
+ }
1177
1195
  const text =
1178
1196
  part.type === "text" ? part.text : part.type === "thinking" ? part.thinking : undefined;
1179
1197
  if (text !== undefined) nextBlocks.set(index, text);
@@ -63,6 +63,7 @@ import {
63
63
  OmpThinkingLevelSchema,
64
64
  validateBoundedText,
65
65
  } from "./omp-rpc-values";
66
+ import { buildOmpPromptRequest } from "./prompt-payload";
66
67
  import { OmpCleanupFailure, OmpPublicError } from "./security";
67
68
  import {
68
69
  listOmpSessionDescriptors,
@@ -497,7 +498,7 @@ class OmpRpcSession implements OmpRuntimeSession {
497
498
  const safeMessage = validateBoundedText(message, "prompt", MAX_TEXT_LENGTH);
498
499
  let acknowledgement: z.infer<typeof OmpPromptAckSchema> | undefined;
499
500
  const request = this.process.startRequest(
500
- { type: "prompt", message: safeMessage, ...(images.length > 0 ? { images } : {}) },
501
+ buildOmpPromptRequest(safeMessage, images),
501
502
  undefined,
502
503
  (value) => {
503
504
  acknowledgement = OmpPromptAckSchema.parse(value) ?? {};
@@ -511,7 +512,7 @@ class OmpRpcSession implements OmpRuntimeSession {
511
512
 
512
513
  async steer(message: string, images: readonly OmpImage[] = []): Promise<void> {
513
514
  const safeMessage = validateBoundedText(message, "steer", MAX_TEXT_LENGTH);
514
- await this.process.sendFrame({
515
+ await this.process.request({
515
516
  type: "steer",
516
517
  message: safeMessage,
517
518
  ...(images.length > 0 ? { images } : {}),
@@ -519,7 +520,7 @@ class OmpRpcSession implements OmpRuntimeSession {
519
520
  }
520
521
  async followUp(message: string, images: readonly OmpImage[] = []): Promise<void> {
521
522
  const safeMessage = validateBoundedText(message, "follow-up", MAX_TEXT_LENGTH);
522
- await this.process.sendFrame({
523
+ await this.process.request({
523
524
  type: "follow_up",
524
525
  message: safeMessage,
525
526
  ...(images.length > 0 ? { images } : {}),
@@ -14,6 +14,14 @@ export function isSafeCommandName(name: string): boolean {
14
14
  }
15
15
 
16
16
  export type OmpPromptPayload = { text: string; images: OmpImage[]; commandName?: string };
17
+ export function buildOmpPromptRequest(message: string, images: readonly OmpImage[] = []) {
18
+ return {
19
+ type: "prompt" as const,
20
+ message,
21
+ streamingBehavior: "followUp" as const,
22
+ ...(images.length > 0 ? { images } : {}),
23
+ };
24
+ }
17
25
 
18
26
  const REVIEW_LINE_MARKERS = { add: "+", remove: "-", context: " " } as const;
19
27
 
@@ -222,12 +230,10 @@ export function inlinePromptFrameBytes(
222
230
  delivery: "prompt" | "steer",
223
231
  ): number {
224
232
  const images = payload.images.map(({ type, mimeType }) => ({ type, data: "", mimeType }));
225
- const frame = {
226
- type: delivery,
227
- message: payload.text,
228
- ...(images.length > 0 ? { images } : {}),
229
- ...(delivery === "prompt" ? { id: "" } : {}),
230
- };
233
+ const frame =
234
+ delivery === "prompt"
235
+ ? { ...buildOmpPromptRequest(payload.text, images), id: "" }
236
+ : { type: "steer", message: payload.text, ...(images.length > 0 ? { images } : {}) };
231
237
  const requestIdBytes = delivery === "prompt" ? RPC_REQUEST_ID_BYTES : 0;
232
238
  return (
233
239
  utf8Bytes(JSON.stringify(frame)) +
@@ -80,6 +80,10 @@ type ToolSnapshot = {
80
80
  specializedRendered: boolean;
81
81
  silent: boolean;
82
82
  };
83
+ type PendingToolStream = {
84
+ output: JsonValue;
85
+ retainedBytes: number;
86
+ };
83
87
 
84
88
  export interface OmpTimelineScheduler {
85
89
  set(callback: () => void | Promise<void>, delayMs: number): unknown;
@@ -354,6 +358,8 @@ type CompactionSlot = {
354
358
 
355
359
  export class OmpTimelineProjector {
356
360
  private readonly tools = new Map<string, ToolSnapshot>();
361
+ private readonly pendingToolStreams = new Map<string, PendingToolStream>();
362
+ private pendingToolStreamBytes = 0;
357
363
  private stream: StreamSnapshot | null = null;
358
364
  private flushTimer: unknown;
359
365
  private currentTurnId: string | null = null;
@@ -493,6 +499,11 @@ export class OmpTimelineProjector {
493
499
  return;
494
500
  case "tool_execution_start": {
495
501
  this.flush(true);
502
+ const pendingStream = this.pendingToolStreams.get(event.toolCallId);
503
+ if (pendingStream) {
504
+ this.pendingToolStreams.delete(event.toolCallId);
505
+ this.pendingToolStreamBytes -= pendingStream.retainedBytes;
506
+ }
496
507
  if (this.toolIdentitySaturated || this.retiredToolCallIds.has(event.toolCallId)) return;
497
508
  const previous = this.tools.get(event.toolCallId);
498
509
  if (!previous && this.tools.size >= MAX_ACTIVE_TOOLS) return;
@@ -501,7 +512,17 @@ export class OmpTimelineProjector {
501
512
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
502
513
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
503
514
  );
504
- const retainedBytes = boundedJsonBytes(input, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
515
+ const inputBytes = boundedJsonBytes(input, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
516
+ let output = pendingStream?.output ?? null;
517
+ let retainedBytes = inputBytes + (pendingStream?.retainedBytes ?? 0);
518
+ if (
519
+ pendingStream &&
520
+ this.activeToolBytes - (previous?.retainedBytes ?? 0) + retainedBytes >
521
+ MAX_ACTIVE_TOOL_BYTES
522
+ ) {
523
+ output = null;
524
+ retainedBytes = inputBytes;
525
+ }
505
526
  if (
506
527
  this.activeToolBytes - (previous?.retainedBytes ?? 0) + retainedBytes >
507
528
  MAX_ACTIVE_TOOL_BYTES
@@ -520,7 +541,7 @@ export class OmpTimelineProjector {
520
541
  nativeName: event.toolName,
521
542
  name: this.dataFilter.text(displayName, 256),
522
543
  input,
523
- output: null,
544
+ output,
524
545
  retainedBytes,
525
546
  turnId,
526
547
  generation: this.runtimeGeneration,
@@ -534,15 +555,28 @@ export class OmpTimelineProjector {
534
555
  }
535
556
  case "tool_execution_update":
536
557
  case "tool_stream_update": {
537
- const previous = this.tools.get(event.toolCallId);
538
- if (!previous) return;
539
- if (previous.turnId !== turnId || previous.generation !== this.runtimeGeneration) return;
540
558
  const output = this.dataFilter.json(
541
559
  event.type === "tool_execution_update" ? event.partialResult : event.update,
542
560
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
543
561
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
544
562
  );
545
563
  const outputBytes = boundedJsonBytes(output, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
564
+ const previous = this.tools.get(event.toolCallId);
565
+ if (!previous) {
566
+ if (event.type !== "tool_stream_update") return;
567
+ const pending = this.pendingToolStreams.get(event.toolCallId);
568
+ if (!pending && this.pendingToolStreams.size >= MAX_ACTIVE_TOOLS) return;
569
+ if (
570
+ this.pendingToolStreamBytes - (pending?.retainedBytes ?? 0) + outputBytes >
571
+ MAX_ACTIVE_TOOL_BYTES
572
+ ) {
573
+ return;
574
+ }
575
+ this.pendingToolStreamBytes += outputBytes - (pending?.retainedBytes ?? 0);
576
+ this.pendingToolStreams.set(event.toolCallId, { output, retainedBytes: outputBytes });
577
+ return;
578
+ }
579
+ if (previous.turnId !== turnId || previous.generation !== this.runtimeGeneration) return;
546
580
  const inputBytes = boundedJsonBytes(previous.input, MAX_PUBLIC_TOOL_PAYLOAD_BYTES);
547
581
  const retainedBytes = inputBytes + outputBytes;
548
582
  if (this.activeToolBytes - previous.retainedBytes + retainedBytes > MAX_ACTIVE_TOOL_BYTES)
@@ -869,6 +903,7 @@ export class OmpTimelineProjector {
869
903
  this.activeToolBytes = 0;
870
904
  this.replayToolCalls.clear();
871
905
  this.tools.clear();
906
+ this.clearPendingToolStreams();
872
907
  this.commandText = "";
873
908
  this.commandPublishedText = "";
874
909
  this.revertEntryByToken.clear();
@@ -1136,6 +1171,7 @@ export class OmpTimelineProjector {
1136
1171
  this.clearFlushTimer();
1137
1172
  this.stream = null;
1138
1173
  this.tools.clear();
1174
+ this.clearPendingToolStreams();
1139
1175
  this.replayToolCalls.clear();
1140
1176
  this.activeToolBytes = 0;
1141
1177
  this.replayCandidates.clear();
@@ -1176,6 +1212,11 @@ export class OmpTimelineProjector {
1176
1212
  }
1177
1213
  this.activeToolBytes = 0;
1178
1214
  this.tools.clear();
1215
+ this.clearPendingToolStreams();
1216
+ }
1217
+ private clearPendingToolStreams(): void {
1218
+ this.pendingToolStreams.clear();
1219
+ this.pendingToolStreamBytes = 0;
1179
1220
  }
1180
1221
  private ensureTurn(turnId: string): void {
1181
1222
  if (this.currentTurnId === turnId) return;