@omercnet/paseo-omp 0.4.0-next.119.1 → 0.4.0

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.
@@ -86,105 +86,7 @@ const MAX_RPC_ERROR_BYTES = 4_096;
86
86
  const MAX_RPC_ERROR_CODE_BYTES = 256;
87
87
  const PROMPT_SCHEDULING_FAILURE = "OMP prompt scheduling failed";
88
88
  const PROTOCOL_VIOLATION_COALESCE_MS = 10_000;
89
- const MAX_REPLAY_MESSAGES = 100_000;
90
- const MAX_REPLAY_BYTES = 64 * 1024 * 1024;
91
- const MAX_REPLAY_NODES = 400_000;
92
- const OMP_MESSAGE_PAGE_LIMIT = 256;
93
- const MAX_MESSAGE_PAGES = 512;
94
- const MAX_MESSAGE_PAGE_BUSY_RETRIES = 4;
95
- const MAX_MESSAGE_PAGE_STALE_RESTARTS = 2;
96
- const MESSAGE_PAGE_RETRY_BASE_MS = 50;
97
-
98
- export const OMP_MESSAGE_REPLAY_POLICIES = {
99
- assistant: "render",
100
- user: "render",
101
- developer: "ignore",
102
- toolResult: "render",
103
- bashExecution: "normalize",
104
- pythonExecution: "normalize",
105
- custom: "normalize",
106
- hookMessage: "normalize",
107
- branchSummary: "ignore",
108
- compactionSummary: "ignore",
109
- fileMention: "ignore",
110
- } as const;
111
-
112
- export const OMP_SESSION_EVENT_POLICIES = {
113
- agent_start: "lifecycle",
114
- agent_end: "lifecycle",
115
- turn_start: "lifecycle",
116
- turn_end: "lifecycle",
117
- message_start: "normalize",
118
- message_update: "normalize",
119
- message_end: "normalize",
120
- tool_execution_start: "normalize",
121
- tool_execution_update: "normalize",
122
- tool_stream_update: "normalize",
123
- tool_execution_end: "normalize",
124
- auto_compaction_start: "normalize",
125
- auto_compaction_end: "normalize",
126
- auto_retry_start: "normalize",
127
- auto_retry_end: "normalize",
128
- retry_fallback_applied: "normalize",
129
- retry_fallback_succeeded: "normalize",
130
- model_changed: "normalize",
131
- config_warnings_changed: "ignore",
132
- advisor_cost_changed: "ignore",
133
- advisor_yielded: "normalize",
134
- ttsr_triggered: "ignore",
135
- todo_reminder: "normalize",
136
- todo_auto_clear: "normalize",
137
- irc_message: "normalize",
138
- notice: "normalize",
139
- thinking_level_changed: "normalize",
140
- goal_updated: "normalize",
141
- } as const;
142
-
143
- export const OMP_RPC_COMMAND_POLICIES = {
144
- negotiate_protocol: "implemented",
145
- prompt: "implemented",
146
- steer: "implemented",
147
- follow_up: "implemented",
148
- abort: "implemented",
149
- abort_and_prompt: "unsupported",
150
- new_session: "unsupported",
151
- get_state: "implemented",
152
- set_fast_mode: "unsupported",
153
- get_available_commands: "implemented",
154
- set_todos: "unsupported",
155
- set_host_tools: "implemented",
156
- set_host_uri_schemes: "unsupported",
157
- set_subagent_subscription: "implemented",
158
- get_subagents: "implemented",
159
- get_subagent_messages: "implemented",
160
- set_model: "implemented",
161
- cycle_model: "unsupported",
162
- get_available_models: "implemented",
163
- set_thinking_level: "implemented",
164
- cycle_thinking_level: "unsupported",
165
- set_steering_mode: "unsupported",
166
- set_follow_up_mode: "unsupported",
167
- set_interrupt_mode: "unsupported",
168
- compact: "implemented",
169
- set_auto_compaction: "implemented",
170
- set_auto_retry: "unsupported",
171
- abort_retry: "unsupported",
172
- bash: "unsupported",
173
- abort_bash: "unsupported",
174
- get_session_stats: "implemented",
175
- export_html: "unsupported",
176
- switch_session: "unsupported",
177
- branch: "implemented",
178
- get_branch_messages: "implemented",
179
- get_last_assistant_text: "unsupported",
180
- set_session_name: "unsupported",
181
- handoff: "implemented",
182
- get_messages: "implemented",
183
- get_messages_page: "implemented",
184
- get_login_providers: "unsupported",
185
- login: "unsupported",
186
- } as const;
187
- export const OMP_RPC_RESPONSE_POLICIES = OMP_RPC_COMMAND_POLICIES;
89
+
188
90
  export const OMP_PROTOCOL_VIOLATION_CATEGORIES = [
189
91
  "duplicate-ready",
190
92
  "frame-limit",
@@ -254,7 +156,6 @@ export const OMP_PROTOCOL_EVENT_TYPES = [
254
156
  "tool_execution_start",
255
157
  "tool_execution_update",
256
158
  "tool_execution_end",
257
- "tool_stream_update",
258
159
  "compaction_start",
259
160
  "compaction_end",
260
161
  "subagent_lifecycle",
@@ -262,10 +163,6 @@ export const OMP_PROTOCOL_EVENT_TYPES = [
262
163
  "subagent_event",
263
164
  "todo_reminder",
264
165
  "model_changed",
265
- "config_warnings_changed",
266
- "advisor_cost_changed",
267
- "ttsr_triggered",
268
- "irc_message",
269
166
  "thinking_level_changed",
270
167
  "goal_updated",
271
168
  "auto_retry_start",
@@ -395,11 +292,8 @@ function eventSchemaClassification(
395
292
  case "message_update":
396
293
  case "message_end":
397
294
  return { reason: "message-event-schema", expected: "valid-message-event" };
398
- case "irc_message":
399
- return { reason: "message-event-schema", expected: "valid-message-event" };
400
295
  case "tool_execution_start":
401
296
  case "tool_execution_update":
402
- case "tool_stream_update":
403
297
  case "tool_execution_end":
404
298
  case "host_tool_call":
405
299
  case "host_tool_cancel":
@@ -418,9 +312,6 @@ function eventSchemaClassification(
418
312
  case "available_commands_update":
419
313
  case "retry_fallback_applied":
420
314
  case "retry_fallback_succeeded":
421
- case "config_warnings_changed":
422
- case "advisor_cost_changed":
423
- case "ttsr_triggered":
424
315
  return { reason: "configuration-event-schema", expected: "valid-configuration-event" };
425
316
  case "extension_ui_request":
426
317
  return { reason: "extension-ui-schema", expected: "valid-extension-ui-event" };
@@ -517,13 +408,6 @@ function boundedString(maxBytes: number, minBytes = 0) {
517
408
  return bytes >= minBytes && bytes <= maxBytes;
518
409
  });
519
410
  }
520
- function isSafeOpaqueCursor(value: string): boolean {
521
- for (let index = 0; index < value.length; index += 1) {
522
- const code = value.charCodeAt(index);
523
- if (code <= 0x1f || code === 0x7f) return false;
524
- }
525
- return true;
526
- }
527
411
  function boundedOptionalStringArray(maxItems: number, maxBytes: number) {
528
412
  return z.unknown().transform((value): string[] | undefined => {
529
413
  if (!Array.isArray(value)) return undefined;
@@ -581,7 +465,7 @@ function sanitizeLiveMessageDisplay(value: unknown): unknown {
581
465
  return content === message.content ? value : { ...message, content };
582
466
  }
583
467
  if (
584
- (message.role === "bashExecution" || message.role === "pythonExecution") &&
468
+ message.role === "bashExecution" &&
585
469
  typeof message.output === "string" &&
586
470
  utf8Bytes(message.output) > MAX_IMAGE_DATA_LENGTH
587
471
  ) {
@@ -623,178 +507,6 @@ function isBoundedJson(
623
507
  boundedJsonBytes(value, maxBytes, maxItems, maxBytes, maxNodes) !== Number.POSITIVE_INFINITY
624
508
  );
625
509
  }
626
- const MAX_RESPONSE_DEPTH = 16;
627
- export const OMP_RPC_BOUND_DIMENSIONS = [
628
- "bytes",
629
- "items",
630
- "nodes",
631
- "depth",
632
- "string-bytes",
633
- ] as const;
634
- export type OmpRpcBoundDimension = (typeof OMP_RPC_BOUND_DIMENSIONS)[number];
635
- export const OMP_RPC_DIAGNOSTIC_COMMANDS = [
636
- "negotiate_protocol",
637
- "get_state",
638
- "get_session_stats",
639
- "get_available_models",
640
- "get_available_commands",
641
- "set_subagent_subscription",
642
- "get_subagents",
643
- "get_subagent_messages",
644
- "prompt",
645
- "compact",
646
- "set_auto_compaction",
647
- "set_model",
648
- "set_thinking_level",
649
- "handoff",
650
- "get_branch_messages",
651
- "branch",
652
- "get_messages_page",
653
- "get_messages",
654
- "abort",
655
- "set_host_tools",
656
- ] as const;
657
- export type OmpRpcDiagnosticCommand = (typeof OMP_RPC_DIAGNOSTIC_COMMANDS)[number];
658
- type JsonBoundViolation = {
659
- dimension: OmpRpcBoundDimension;
660
- actual: number;
661
- limit: number;
662
- };
663
- type JsonBoundInspection = { bytes: number; nodes: number; violation?: JsonBoundViolation };
664
-
665
- function rpcDiagnosticCommand(value: string): OmpRpcDiagnosticCommand | undefined {
666
- return (OMP_RPC_DIAGNOSTIC_COMMANDS as readonly string[]).includes(value)
667
- ? (value as OmpRpcDiagnosticCommand)
668
- : undefined;
669
- }
670
-
671
- function encodedJsonStringBytes(value: string): number {
672
- let bytes = 2;
673
- for (let index = 0; index < value.length; index += 1) {
674
- const code = value.charCodeAt(index);
675
- if (code === 0x22 || code === 0x5c) {
676
- bytes += 2;
677
- } else if (code <= 0x1f) {
678
- bytes +=
679
- code === 0x08 || code === 0x09 || code === 0x0a || code === 0x0c || code === 0x0d ? 2 : 6;
680
- } else if (code >= 0xd800 && code <= 0xdbff) {
681
- const low = value.charCodeAt(index + 1);
682
- if (low >= 0xdc00 && low <= 0xdfff) {
683
- bytes += 4;
684
- index += 1;
685
- } else {
686
- bytes += 6;
687
- }
688
- } else if (code >= 0xdc00 && code <= 0xdfff) {
689
- bytes += 6;
690
- } else {
691
- bytes += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : 3;
692
- }
693
- }
694
- return bytes;
695
- }
696
-
697
- function inspectJsonBounds(
698
- value: unknown,
699
- maxBytes: number,
700
- maxItems: number,
701
- maxStringBytes: number,
702
- maxNodes: number,
703
- ): JsonBoundInspection {
704
- const stack: Array<{ value: unknown; depth: number }> = [{ value, depth: 0 }];
705
- let nodes = 0;
706
- let bytes = 0;
707
- const addBytes = (count: number): JsonBoundViolation | undefined => {
708
- bytes += count;
709
- return bytes > maxBytes ? { dimension: "bytes", actual: bytes, limit: maxBytes } : undefined;
710
- };
711
- while (stack.length > 0) {
712
- const current = stack.pop();
713
- if (!current) break;
714
- nodes += 1;
715
- if (nodes > maxNodes)
716
- return { bytes, nodes, violation: { dimension: "nodes", actual: nodes, limit: maxNodes } };
717
- if (current.depth > MAX_RESPONSE_DEPTH) {
718
- return {
719
- bytes,
720
- nodes,
721
- violation: { dimension: "depth", actual: current.depth, limit: MAX_RESPONSE_DEPTH },
722
- };
723
- }
724
- const item = current.value;
725
- if (item === null) {
726
- const violation = addBytes(4);
727
- if (violation) return { bytes, nodes, violation };
728
- continue;
729
- }
730
- if (typeof item === "boolean") {
731
- const violation = addBytes(item ? 4 : 5);
732
- if (violation) return { bytes, nodes, violation };
733
- continue;
734
- }
735
- if (typeof item === "number") {
736
- const violation = addBytes((JSON.stringify(item) ?? "null").length);
737
- if (violation) return { bytes, nodes, violation };
738
- continue;
739
- }
740
- if (typeof item === "string") {
741
- const itemBytes = utf8Bytes(item);
742
- if (itemBytes > maxStringBytes) {
743
- return {
744
- bytes,
745
- nodes,
746
- violation: { dimension: "string-bytes", actual: itemBytes, limit: maxStringBytes },
747
- };
748
- }
749
- const violation = addBytes(encodedJsonStringBytes(item));
750
- if (violation) return { bytes, nodes, violation };
751
- continue;
752
- }
753
- if (typeof item !== "object") continue;
754
- if (Array.isArray(item)) {
755
- if (item.length > maxItems) {
756
- return {
757
- bytes,
758
- nodes,
759
- violation: { dimension: "items", actual: item.length, limit: maxItems },
760
- };
761
- }
762
- const violation = addBytes(2 + Math.max(0, item.length - 1));
763
- if (violation) return { bytes, nodes, violation };
764
- for (let index = item.length - 1; index >= 0; index -= 1) {
765
- stack.push({ value: item[index], depth: current.depth + 1 });
766
- }
767
- continue;
768
- }
769
- const entries = Object.entries(item).filter(([, child]) => child !== undefined);
770
- if (entries.length > maxItems) {
771
- return {
772
- bytes,
773
- nodes,
774
- violation: { dimension: "items", actual: entries.length, limit: maxItems },
775
- };
776
- }
777
- let violation = addBytes(2 + Math.max(0, entries.length - 1) + entries.length);
778
- if (violation) return { bytes, nodes, violation };
779
- for (let index = entries.length - 1; index >= 0; index -= 1) {
780
- const [key, child] = entries[index] as [string, unknown];
781
- violation = addBytes(encodedJsonStringBytes(key));
782
- if (violation) return { bytes, nodes, violation };
783
- stack.push({ value: child, depth: current.depth + 1 });
784
- }
785
- }
786
- return { bytes, nodes };
787
- }
788
-
789
- function jsonBoundViolation(
790
- value: unknown,
791
- maxBytes: number,
792
- maxItems: number,
793
- maxStringBytes: number,
794
- maxNodes: number,
795
- ): JsonBoundViolation | undefined {
796
- return inspectJsonBounds(value, maxBytes, maxItems, maxStringBytes, maxNodes).violation;
797
- }
798
510
  function optionalMetadataMetrics(value: unknown) {
799
511
  return boundedJsonMetrics(
800
512
  value,
@@ -994,46 +706,52 @@ const OmpMessageIdentityShape = {
994
706
  timestamp: z.number().finite().optional(),
995
707
  details: OmpOptionalMetadataSchema,
996
708
  };
997
- const OmpOpaqueMessagePayloadSchema = z
998
- .unknown()
999
- .refine((value) => isBoundedJson(value, MAX_SEMANTIC_FRAME_BYTES, 1_024, 8_192));
1000
- const OmpExecutionOutputSchema = z.preprocess(
1001
- (value) =>
1002
- typeof value === "string" && utf8Bytes(value) > MAX_IMAGE_DATA_LENGTH
1003
- ? DISPLAY_TRUNCATION_MARKER
1004
- : value,
1005
- RAW_DISPLAY_TEXT.optional(),
1006
- );
1007
- const OmpExecutionMessageShape = {
1008
- output: OmpExecutionOutputSchema,
1009
- exitCode: z.number().int().nullable().optional(),
1010
- cancelled: z.boolean().optional(),
1011
- truncated: z.boolean().optional(),
1012
- excludeFromContext: z.boolean().optional(),
1013
- meta: OmpOptionalMetadataSchema,
1014
- };
1015
- const OmpCustomMessageShape = {
1016
- customType: NAME.optional(),
1017
- content: OmpDisplayContentSchema.optional(),
1018
- display: z.boolean().optional(),
1019
- attribution: OmpOpaqueMessagePayloadSchema.optional(),
709
+ type OmpContentPart = z.infer<typeof OmpContentPartSchema>;
710
+ type OmpMessageIdentity = {
711
+ id?: string;
712
+ entryId?: string;
713
+ responseId?: string;
714
+ images?: OmpContentPart[];
715
+ timestamp?: number;
716
+ details?: unknown;
717
+ display?: boolean;
718
+ customType?: string;
719
+ content?: unknown;
720
+ command?: string;
721
+ output?: string;
722
+ exitCode?: number | null;
723
+ cancelled?: boolean;
724
+ truncated?: boolean;
1020
725
  };
1021
- const OmpFileMentionSchema = z
1022
- .object({
1023
- path: boundedString(MAX_PATH_LENGTH),
1024
- content: TEXT,
1025
- lineCount: z.number().int().nonnegative().safe().optional(),
1026
- byteSize: z.number().int().nonnegative().safe().optional(),
1027
- skippedReason: z.enum(["tooLarge", "binary"]).optional(),
1028
- image: OmpContentPartSchema.optional(),
1029
- })
1030
- .superRefine((file, context) => {
1031
- if (file.image && file.image.type !== "image") {
1032
- context.addIssue({ code: "custom", message: "invalid file mention image" });
1033
- }
1034
- });
726
+ export type OmpMessage = OmpMessageIdentity &
727
+ (
728
+ | {
729
+ role: "assistant";
730
+ content?: string | OmpContentPart[];
731
+ errorMessage?: string | null;
732
+ stopReason?: string;
733
+ }
734
+ | { role: "user"; content: string | OmpContentPart[] }
735
+ | {
736
+ role: "toolResult";
737
+ toolCallId: string;
738
+ toolName: string;
739
+ content: unknown;
740
+ details?: unknown;
741
+ isError?: boolean;
742
+ }
743
+ | {
744
+ role: "bashExecution";
745
+ command: string;
746
+ output?: string;
747
+ exitCode?: number | null;
748
+ cancelled?: boolean;
749
+ truncated?: boolean;
750
+ }
751
+ | { role: "custom"; customType?: string; content?: unknown; display?: boolean }
752
+ );
1035
753
 
1036
- const OmpMessageSchema = z.discriminatedUnion("role", [
754
+ const OmpMessageSchema: z.ZodType<OmpMessage> = z.union([
1037
755
  z.object({
1038
756
  role: z.literal("assistant"),
1039
757
  content: OmpAssistantDisplayContentSchema.optional(),
@@ -1046,70 +764,42 @@ const OmpMessageSchema = z.discriminatedUnion("role", [
1046
764
  content: OmpDisplayContentSchema,
1047
765
  ...OmpMessageIdentityShape,
1048
766
  }),
1049
- z.object({
1050
- role: z.literal("developer"),
1051
- content: OmpDisplayContentSchema,
1052
- attribution: OmpOpaqueMessagePayloadSchema.optional(),
1053
- synthetic: z.boolean().optional(),
1054
- userInitiated: z.boolean().optional(),
1055
- providerPayload: OmpOpaqueMessagePayloadSchema.optional(),
1056
- ...OmpMessageIdentityShape,
1057
- }),
1058
767
  z.object({
1059
768
  role: z.literal("toolResult"),
1060
769
  toolCallId: IDENTIFIER,
1061
770
  toolName: NAME,
1062
- content: OmpOpaqueMessagePayloadSchema,
771
+ content: z
772
+ .unknown()
773
+ .refine((value) => isBoundedJson(value, MAX_SEMANTIC_FRAME_BYTES, 1_024, 8_192)),
1063
774
  isError: z.boolean().optional(),
1064
- attribution: OmpOpaqueMessagePayloadSchema.optional(),
1065
- prunedAt: z.number().finite().nonnegative().optional(),
1066
- providerMetadata: OmpOpaqueMessagePayloadSchema.optional(),
1067
- useless: z.boolean().optional(),
1068
775
  ...OmpMessageIdentityShape,
1069
776
  }),
1070
777
  z.object({
1071
778
  role: z.literal("bashExecution"),
1072
779
  command: TEXT,
1073
- ...OmpExecutionMessageShape,
1074
- ...OmpMessageIdentityShape,
1075
- }),
1076
- z.object({
1077
- role: z.literal("pythonExecution"),
1078
- code: TEXT,
1079
- ...OmpExecutionMessageShape,
1080
- ...OmpMessageIdentityShape,
1081
- }),
1082
- z.object({ role: z.literal("custom"), ...OmpCustomMessageShape, ...OmpMessageIdentityShape }),
1083
- z.object({
1084
- role: z.literal("hookMessage"),
1085
- ...OmpCustomMessageShape,
1086
- ...OmpMessageIdentityShape,
1087
- }),
1088
- z.object({
1089
- role: z.literal("branchSummary"),
1090
- summary: TEXT,
1091
- fromId: IDENTIFIER,
1092
- ...OmpMessageIdentityShape,
1093
- }),
1094
- z.object({
1095
- role: z.literal("compactionSummary"),
1096
- summary: TEXT,
1097
- shortSummary: TEXT.optional(),
1098
- tokensBefore: z.number().finite().nonnegative(),
1099
- tokensAfter: z.number().finite().nonnegative().optional(),
1100
- method: NAME.optional(),
1101
- providerPayload: OmpOpaqueMessagePayloadSchema.optional(),
1102
- blocks: z.array(OmpContentPartSchema).max(OMP_MAX_CONTENT_PARTS).optional(),
1103
- warning: boundedString(64 * 1024).optional(),
780
+ output: z.preprocess(
781
+ (value) =>
782
+ typeof value === "string" && utf8Bytes(value) > MAX_IMAGE_DATA_LENGTH
783
+ ? DISPLAY_TRUNCATION_MARKER
784
+ : value,
785
+ RAW_DISPLAY_TEXT.optional(),
786
+ ),
787
+ exitCode: z.number().int().nullable().optional(),
788
+ cancelled: z.boolean().optional(),
789
+ truncated: z.boolean().optional(),
1104
790
  ...OmpMessageIdentityShape,
1105
791
  }),
1106
792
  z.object({
1107
- role: z.literal("fileMention"),
1108
- files: z.array(OmpFileMentionSchema).max(MAX_ARRAY_ITEMS),
793
+ role: z.literal("custom"),
794
+ customType: NAME.optional(),
795
+ content: z
796
+ .unknown()
797
+ .refine((value) => isBoundedJson(value, MAX_SEMANTIC_FRAME_BYTES, 1_024, 8_192))
798
+ .optional(),
799
+ display: z.boolean().optional(),
1109
800
  ...OmpMessageIdentityShape,
1110
801
  }),
1111
802
  ]);
1112
- export type OmpMessage = z.infer<typeof OmpMessageSchema>;
1113
803
 
1114
804
  const OmpAssistantMessageEventSchema = z
1115
805
  .object({
@@ -1258,7 +948,6 @@ const OmpReadyFrameSchema = z.object({
1258
948
  const OmpResponseFrameSchema = z.object({
1259
949
  type: z.literal("response"),
1260
950
  id: IDENTIFIER,
1261
- command: boundedString(64, 1).optional(),
1262
951
  success: z.boolean(),
1263
952
  data: z.unknown().optional(),
1264
953
  error: boundedString(MAX_RPC_ERROR_BYTES).optional(),
@@ -1464,7 +1153,7 @@ const OmpCompactionEndSchema = z.object({
1464
1153
  errorMessage: boundedString(4_096).optional(),
1465
1154
  skipped: z.boolean().optional(),
1466
1155
  });
1467
- const OmpCoreAgentEventSchema = z.discriminatedUnion("type", [
1156
+ const OmpAgentSessionEventSchema = z.discriminatedUnion("type", [
1468
1157
  z.object({ type: z.literal("agent_start") }),
1469
1158
  z.object({
1470
1159
  type: z.literal("agent_end"),
@@ -1495,12 +1184,6 @@ const OmpCoreAgentEventSchema = z.discriminatedUnion("type", [
1495
1184
  args: BoundedToolPayloadSchema.optional(),
1496
1185
  partialResult: OmpToolResultPayloadSchema,
1497
1186
  }),
1498
- z.object({
1499
- type: z.literal("tool_stream_update"),
1500
- toolCallId: IDENTIFIER,
1501
- toolName: NAME,
1502
- update: BoundedToolPayloadSchema,
1503
- }),
1504
1187
  z.object({
1505
1188
  type: z.literal("tool_execution_end"),
1506
1189
  toolCallId: IDENTIFIER,
@@ -1527,99 +1210,6 @@ const OmpGoalModeStateSchema = z.object({
1527
1210
  reason: boundedString(4_096).optional(),
1528
1211
  goal: OmpGoalSchema.optional(),
1529
1212
  });
1530
- const OmpTodoReminderEventSchema = z.object({
1531
- type: z.literal("todo_reminder"),
1532
- todos: z
1533
- .array(
1534
- z.object({
1535
- id: IDENTIFIER.optional(),
1536
- content: boundedString(16_384),
1537
- status: z.enum(["pending", "in_progress", "blocked", "completed", "abandoned"]),
1538
- }),
1539
- )
1540
- .max(MAX_TODOS),
1541
- attempt: z.number().int().nonnegative().safe().optional(),
1542
- maxAttempts: z.number().int().nonnegative().safe().optional(),
1543
- });
1544
- const OmpThinkingLevelChangedEventSchema = z.object({
1545
- type: z.literal("thinking_level_changed"),
1546
- thinkingLevel: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES).optional(),
1547
- configured: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES).optional(),
1548
- resolved: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES).optional(),
1549
- });
1550
- const OmpAutoRetryStartEventSchema = z.object({
1551
- type: z.literal("auto_retry_start"),
1552
- attempt: z.number().int().nonnegative().safe(),
1553
- maxAttempts: z.number().int().positive().safe(),
1554
- delayMs: z.number().int().nonnegative().safe(),
1555
- errorMessage: boundedString(64 * 1024),
1556
- errorId: z.number().int().safe().optional(),
1557
- });
1558
- const OmpAutoRetryEndEventSchema = z.object({
1559
- type: z.literal("auto_retry_end"),
1560
- success: z.boolean(),
1561
- attempt: z.number().int().nonnegative().safe(),
1562
- finalError: boundedString(64 * 1024).optional(),
1563
- retryErrors: BoundedToolPayloadSchema.optional(),
1564
- recoveredErrors: BoundedToolPayloadSchema.optional(),
1565
- });
1566
- const OmpAgentSessionEventSchema = z.discriminatedUnion("type", [
1567
- ...OmpCoreAgentEventSchema.options,
1568
- z.object({ type: z.literal("config_warnings_changed") }),
1569
- z.object({ type: z.literal("advisor_cost_changed") }),
1570
- z.object({ type: z.literal("advisor_yielded") }),
1571
- z.object({
1572
- type: z.literal("ttsr_triggered"),
1573
- rules: z.array(BoundedToolPayloadSchema).max(MAX_ARRAY_ITEMS),
1574
- }),
1575
- OmpTodoReminderEventSchema,
1576
- z.object({ type: z.literal("todo_auto_clear") }),
1577
- z.object({
1578
- type: z.literal("irc_message"),
1579
- message: OmpMessageSchema.refine((message) => message.role === "custom"),
1580
- }),
1581
- z.object({ type: z.literal("model_changed") }),
1582
- OmpThinkingLevelChangedEventSchema,
1583
- z.object({
1584
- type: z.literal("goal_updated"),
1585
- goal: OmpGoalSchema.nullable().optional(),
1586
- state: OmpGoalModeStateSchema.optional(),
1587
- }),
1588
- OmpAutoRetryStartEventSchema,
1589
- OmpAutoRetryEndEventSchema,
1590
- z.object({
1591
- type: z.literal("retry_fallback_applied"),
1592
- from: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1593
- to: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1594
- role: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1595
- }),
1596
- z.object({
1597
- type: z.literal("retry_fallback_succeeded"),
1598
- model: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1599
- role: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1600
- }),
1601
- z.object({
1602
- type: z.literal("auto_compaction_start"),
1603
- reason: boundedString(4_096),
1604
- action: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1605
- }),
1606
- z.object({
1607
- type: z.literal("auto_compaction_end"),
1608
- action: NAME.optional(),
1609
- result: OmpCompactionResultSchema.nullable().optional(),
1610
- aborted: z.boolean().optional(),
1611
- willRetry: z.boolean().optional(),
1612
- errorMessage: boundedString(64 * 1024).optional(),
1613
- skipped: z.boolean().optional(),
1614
- }),
1615
- z.object({
1616
- type: z.literal("notice"),
1617
- id: IDENTIFIER.optional(),
1618
- level: z.enum(["info", "warning", "error"]),
1619
- message: boundedString(64 * 1024),
1620
- source: boundedString(MAX_NAME_LENGTH).optional(),
1621
- }),
1622
- ]);
1623
1213
  const OmpSubagentStatusSchema = z.enum([
1624
1214
  "pending",
1625
1215
  "running",
@@ -1762,10 +1352,80 @@ const OmpRuntimeEventSchema = z.discriminatedUnion("type", [
1762
1352
  type: z.literal("subagent_event"),
1763
1353
  payload: z.object({ id: IDENTIFIER, event: OmpAgentSessionEventSchema }),
1764
1354
  }),
1355
+ z.object({
1356
+ type: z.literal("todo_reminder"),
1357
+ todos: z
1358
+ .array(
1359
+ z.object({
1360
+ id: IDENTIFIER.optional(),
1361
+ content: boundedString(16_384),
1362
+ status: z.enum(["pending", "in_progress", "blocked", "completed", "abandoned"]),
1363
+ }),
1364
+ )
1365
+ .max(MAX_TODOS),
1366
+ }),
1367
+ z.object({ type: z.literal("model_changed") }),
1368
+ z.object({
1369
+ type: z.literal("thinking_level_changed"),
1370
+ thinkingLevel: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES).optional(),
1371
+ }),
1372
+ z.object({
1373
+ type: z.literal("goal_updated"),
1374
+ goal: OmpGoalSchema.nullable().optional(),
1375
+ state: OmpGoalModeStateSchema.optional(),
1376
+ }),
1377
+ z.object({
1378
+ type: z.literal("auto_retry_start"),
1379
+ attempt: z.number().int().nonnegative().safe(),
1380
+ maxAttempts: z.number().int().positive().safe(),
1381
+ delayMs: z.number().int().nonnegative().safe(),
1382
+ errorMessage: boundedString(64 * 1024),
1383
+ errorId: z.number().int().safe().optional(),
1384
+ }),
1385
+ z.object({
1386
+ type: z.literal("auto_retry_end"),
1387
+ success: z.boolean(),
1388
+ attempt: z.number().int().nonnegative().safe(),
1389
+ finalError: boundedString(64 * 1024).optional(),
1390
+ recoveredErrors: BoundedToolPayloadSchema.optional(),
1391
+ }),
1392
+ z.object({
1393
+ type: z.literal("retry_fallback_applied"),
1394
+ from: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1395
+ to: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1396
+ role: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1397
+ }),
1398
+ z.object({
1399
+ type: z.literal("retry_fallback_succeeded"),
1400
+ model: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1401
+ role: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1402
+ }),
1403
+ z.object({ type: z.literal("todo_auto_clear") }),
1404
+ z.object({
1405
+ type: z.literal("auto_compaction_start"),
1406
+ reason: boundedString(4_096),
1407
+ action: boundedString(MAX_CONFIG_EVENT_TEXT_BYTES),
1408
+ }),
1409
+ z.object({
1410
+ type: z.literal("auto_compaction_end"),
1411
+ action: NAME.optional(),
1412
+ result: OmpCompactionResultSchema.nullable().optional(),
1413
+ aborted: z.boolean().optional(),
1414
+ willRetry: z.boolean().optional(),
1415
+ errorMessage: boundedString(64 * 1024).optional(),
1416
+ skipped: z.boolean().optional(),
1417
+ }),
1765
1418
  z.object({
1766
1419
  type: z.literal("available_commands_update"),
1767
1420
  commands: z.array(OmpAvailableCommandSchema).max(MAX_ARRAY_ITEMS),
1768
1421
  }),
1422
+ z.object({
1423
+ type: z.literal("notice"),
1424
+ id: IDENTIFIER.optional(),
1425
+ level: z.enum(["info", "warning", "error"]),
1426
+ message: boundedString(64 * 1024),
1427
+ source: boundedString(MAX_NAME_LENGTH).optional(),
1428
+ }),
1769
1429
  z.object({ type: z.literal("command_output"), text: TEXT.optional() }),
1770
1430
  OmpExtensionUiRequestSchema,
1771
1431
  z.object({
@@ -1777,6 +1437,7 @@ const OmpRuntimeEventSchema = z.discriminatedUnion("type", [
1777
1437
  OmpHostToolCancelSchema,
1778
1438
  OmpToolApprovalRequestSchema,
1779
1439
  OmpToolApprovalCancelSchema,
1440
+ z.object({ type: z.literal("advisor_yielded") }),
1780
1441
  ]);
1781
1442
  type OptionalDetailsMapper = (value: unknown, taskResult?: boolean) => unknown;
1782
1443
 
@@ -1831,8 +1492,6 @@ function mapAgentEventDetails(
1831
1492
  return mapRecordField(frame, "result", map, frame.toolName === "task");
1832
1493
  case "agent_end":
1833
1494
  return mapRecordField(frame, "messages", (messages) => mapMessageList(messages, map));
1834
- case "irc_message":
1835
- return mapRecordField(frame, "message", map);
1836
1495
  default:
1837
1496
  return frame;
1838
1497
  }
@@ -1863,25 +1522,6 @@ function sanitizeHistoryResponseData(value: unknown): unknown {
1863
1522
  return mapRecordField(value as Record<string, unknown>, "messages", sanitizeMessageListMetadata);
1864
1523
  }
1865
1524
 
1866
- function projectSessionStateResponseData(value: unknown): unknown {
1867
- if (value === null || typeof value !== "object" || Array.isArray(value)) return value;
1868
- const source = value as Record<string, unknown>;
1869
- const projected: Record<string, unknown> = {};
1870
- for (const key of [
1871
- "model",
1872
- "thinkingLevel",
1873
- "isStreaming",
1874
- "isCompacting",
1875
- "sessionId",
1876
- "autoCompactionEnabled",
1877
- "contextUsage",
1878
- "sessionFile",
1879
- ] as const) {
1880
- if (Object.hasOwn(source, key)) projected[key] = source[key];
1881
- }
1882
- return projected;
1883
- }
1884
-
1885
1525
  function runtimeFrameCollectionLimit(frame: Record<string, unknown>): number {
1886
1526
  const type = frame.type;
1887
1527
  if (
@@ -1907,12 +1547,7 @@ const OmpBranchMessagesResultSchema = z.object({
1907
1547
  });
1908
1548
  const OmpBranchResultSchema = z.object({ text: TEXT, cancelled: z.boolean() });
1909
1549
  const OmpMessagesResultSchema = z.object({
1910
- messages: z.array(OmpMessageSchema).max(MAX_REPLAY_MESSAGES),
1911
- });
1912
- const OmpMessagesPageResultSchema = z.object({
1913
- messages: z.array(OmpMessageSchema).max(OMP_MESSAGE_PAGE_LIMIT),
1914
- nextCursor: boundedString(2_048, 1).refine(isSafeOpaqueCursor).optional(),
1915
- totalMessages: z.number().int().nonnegative().max(MAX_REPLAY_MESSAGES),
1550
+ messages: z.array(OmpMessageSchema).max(100_000),
1916
1551
  });
1917
1552
  const OmpSubagentsResultSchema = z.object({
1918
1553
  subagents: z
@@ -1938,7 +1573,7 @@ const OmpSubagentMessagesResultSchema = z.object({
1938
1573
  fromByte: z.number().int().nonnegative(),
1939
1574
  nextByte: z.number().int().nonnegative(),
1940
1575
  reset: z.boolean(),
1941
- messages: z.array(OmpMessageSchema).max(MAX_REPLAY_MESSAGES),
1576
+ messages: z.array(OmpMessageSchema).max(100_000),
1942
1577
  });
1943
1578
  const ProtocolNegotiationResultSchema = z.object({
1944
1579
  protocolVersion: z.literal(2),
@@ -2056,7 +1691,7 @@ export interface OmpRuntimeSession {
2056
1691
  getBranchMessages(): Promise<Array<{ entryId: string; text: string }>>;
2057
1692
  branch(entryId: string): Promise<OmpBranchResult>;
2058
1693
  readonly canReplayHistory: boolean;
2059
- getMessages(signal?: AbortSignal): Promise<OmpMessage[]>;
1694
+ getMessages(): Promise<OmpMessage[]>;
2060
1695
  abort(): Promise<void>;
2061
1696
  setHostTools(tools: readonly OmpHostToolDefinition[]): Promise<string[]>;
2062
1697
  sendHostToolResult(result: OmpHostToolResult): void;
@@ -2077,7 +1712,6 @@ export interface OmpRuntime {
2077
1712
  readPersistedSubagentTranscript(options: {
2078
1713
  parentSessionFile: string;
2079
1714
  childTranscriptId: string;
2080
- sessionFile?: string;
2081
1715
  cwd: string;
2082
1716
  signal?: AbortSignal;
2083
1717
  }): Promise<OmpPersistedSubagentMessages>;
@@ -2103,30 +1737,13 @@ export interface OmpRpcRuntimeOptions {
2103
1737
  ) => OmpSessionDescriptor[] | Promise<OmpSessionDescriptor[]>;
2104
1738
  }
2105
1739
 
2106
- export type OmpRpcRequestRejectionCode = "session_busy" | "stale_cursor" | "unsupported_command";
2107
-
2108
1740
  export class OmpRpcRequestRejectedError extends Error {
2109
- constructor(
2110
- readonly command?: OmpRpcDiagnosticCommand,
2111
- readonly code?: OmpRpcRequestRejectionCode,
2112
- ) {
1741
+ constructor() {
2113
1742
  super("OMP RPC request failed");
2114
1743
  this.name = "OmpRpcRequestRejectedError";
2115
1744
  }
2116
1745
  }
2117
1746
 
2118
- export class OmpRpcResponseLimitError extends Error {
2119
- constructor(
2120
- readonly command: OmpRpcDiagnosticCommand | undefined,
2121
- readonly bound: OmpRpcBoundDimension,
2122
- readonly actual: number,
2123
- readonly limit: number,
2124
- ) {
2125
- super("OMP RPC response exceeded command limits");
2126
- this.name = "OmpRpcResponseLimitError";
2127
- }
2128
- }
2129
-
2130
1747
  type PendingRequest = {
2131
1748
  resolve(value: unknown): void;
2132
1749
  reject(error: Error): void;
@@ -3001,13 +2618,8 @@ class OmpRpcProcess {
3001
2618
  } else {
3002
2619
  this.appendLinePart(part);
3003
2620
  if (this.fatalError) return;
3004
- if (this.discardingLine) {
3005
- if (this.discardedLineBytes > MAX_SEMANTIC_FRAME_BYTES) {
3006
- this.fail(new Error("OMP RPC frame exceeds the semantic byte limit"));
3007
- return;
3008
- }
3009
- this.resetDiscardedLine();
3010
- } else this.completeLine();
2621
+ if (this.discardingLine) this.resetDiscardedLine();
2622
+ else this.completeLine();
3011
2623
  }
3012
2624
  start = index + 1;
3013
2625
  }
@@ -3062,6 +2674,10 @@ class OmpRpcProcess {
3062
2674
  this.lineParts = [];
3063
2675
  this.lineBytes = 0;
3064
2676
  const payload = line.at(-1) === 13 ? line.subarray(0, -1) : line;
2677
+ if (payload.byteLength > MAX_SEMANTIC_FRAME_BYTES) {
2678
+ this.fail(new Error("OMP RPC frame exceeds the semantic byte limit"));
2679
+ return;
2680
+ }
3065
2681
  let decoded: unknown;
3066
2682
  try {
3067
2683
  decoded = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payload));
@@ -3083,10 +2699,7 @@ class OmpRpcProcess {
3083
2699
  frame.byteLength > MAX_SEMANTIC_FRAME_BYTES &&
3084
2700
  ![...this.pending.values()].some(
3085
2701
  (pending) =>
3086
- pending.command === "get_messages" ||
3087
- pending.command === "get_messages_page" ||
3088
- pending.command === "get_subagent_messages" ||
3089
- pending.command === "get_state",
2702
+ pending.command === "get_messages" || pending.command === "get_subagent_messages",
3090
2703
  )
3091
2704
  ) {
3092
2705
  this.fail(new Error("OMP RPC frame exceeds the semantic byte limit"));
@@ -3192,16 +2805,12 @@ class OmpRpcProcess {
3192
2805
  const pending = typeof frame.id === "string" ? this.pending.get(frame.id) : undefined;
3193
2806
  if (
3194
2807
  frame.type === "response" &&
3195
- (pending?.command === "get_messages" ||
3196
- pending?.command === "get_messages_page" ||
3197
- pending?.command === "get_subagent_messages" ||
3198
- pending?.command === "get_state")
2808
+ (pending?.command === "get_messages" || pending?.command === "get_subagent_messages")
3199
2809
  ) {
3200
2810
  this.receiveResponse(frame);
3201
2811
  return;
3202
2812
  }
3203
2813
  }
3204
- if (this.receiveUnsupportedCommandResponse(value)) return;
3205
2814
  if (rawByteLength > MAX_SEMANTIC_FRAME_BYTES) {
3206
2815
  this.fail(new Error("OMP RPC frame exceeds the semantic byte limit"));
3207
2816
  return;
@@ -3236,30 +2845,6 @@ class OmpRpcProcess {
3236
2845
  return true;
3237
2846
  }
3238
2847
 
3239
- private receiveUnsupportedCommandResponse(value: unknown): boolean {
3240
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
3241
- const frame = value as Record<string, unknown>;
3242
- if (
3243
- frame.type !== "response" ||
3244
- frame.id !== undefined ||
3245
- frame.success !== false ||
3246
- typeof frame.command !== "string" ||
3247
- frame.error !== `Unknown command: ${frame.command}`
3248
- ) {
3249
- return false;
3250
- }
3251
- const matches = [...this.pending.entries()].filter(
3252
- ([, pending]) => pending.command === frame.command,
3253
- );
3254
- if (matches.length === 0) return false;
3255
- const [id, pending] = matches[0] ?? [];
3256
- if (!id || !pending) return false;
3257
- this.takePending(id)?.reject(
3258
- new OmpRpcRequestRejectedError(rpcDiagnosticCommand(pending.command), "unsupported_command"),
3259
- );
3260
- return true;
3261
- }
3262
-
3263
2848
  private receiveResponse(frame: Record<string, unknown>): void {
3264
2849
  const rawId = typeof frame.id === "string" ? frame.id : undefined;
3265
2850
  const knownPending = rawId ? this.pending.get(rawId) : undefined;
@@ -3292,14 +2877,10 @@ class OmpRpcProcess {
3292
2877
  }
3293
2878
  const isBranchHistory = pending.command === "get_branch_messages";
3294
2879
  const isHistory =
3295
- pending.command === "get_messages" ||
3296
- pending.command === "get_messages_page" ||
3297
- pending.command === "get_subagent_messages";
3298
- const rawResponseData =
3299
- pending.command === "get_state"
3300
- ? projectSessionStateResponseData(response.data.data)
3301
- : response.data.data;
3302
- const responseData = isHistory ? sanitizeHistoryResponseData(rawResponseData) : rawResponseData;
2880
+ pending.command === "get_messages" || pending.command === "get_subagent_messages";
2881
+ const responseData = isHistory
2882
+ ? sanitizeHistoryResponseData(response.data.data)
2883
+ : response.data.data;
3303
2884
  const boundedFrame =
3304
2885
  responseData === response.data.data ? frame : { ...frame, data: responseData };
3305
2886
  const responseItemLimit = isBranchHistory
@@ -3322,21 +2903,17 @@ class OmpRpcProcess {
3322
2903
  : pending.command === "get_available_models"
3323
2904
  ? 16_384
3324
2905
  : 2_048;
3325
- const violation = jsonBoundViolation(
3326
- boundedFrame,
3327
- responseByteLimit,
3328
- responseItemLimit,
3329
- MAX_IMAGE_DATA_LENGTH,
3330
- responseNodeLimit,
3331
- );
3332
- if (violation) {
2906
+ if (
2907
+ boundedJsonBytes(
2908
+ boundedFrame,
2909
+ responseByteLimit,
2910
+ responseItemLimit,
2911
+ MAX_IMAGE_DATA_LENGTH,
2912
+ responseNodeLimit,
2913
+ ) === Number.POSITIVE_INFINITY
2914
+ ) {
3333
2915
  this.takePending(response.data.id)?.reject(
3334
- new OmpRpcResponseLimitError(
3335
- rpcDiagnosticCommand(pending.command),
3336
- violation.dimension,
3337
- violation.actual,
3338
- violation.limit,
3339
- ),
2916
+ new Error("OMP RPC response exceeded command limits"),
3340
2917
  );
3341
2918
  return;
3342
2919
  }
@@ -3357,16 +2934,7 @@ class OmpRpcProcess {
3357
2934
  settled.reject(new Error("OMP RPC response is invalid"));
3358
2935
  }
3359
2936
  } else {
3360
- const responseCode = response.data.code;
3361
- const code: OmpRpcRequestRejectionCode | undefined =
3362
- responseCode === "session_busy" ||
3363
- responseCode === "stale_cursor" ||
3364
- responseCode === "unsupported_command"
3365
- ? responseCode
3366
- : response.data.error === `Unknown command: ${pending.command}`
3367
- ? "unsupported_command"
3368
- : undefined;
3369
- settled.reject(new OmpRpcRequestRejectedError(rpcDiagnosticCommand(pending.command), code));
2937
+ settled.reject(new OmpRpcRequestRejectedError());
3370
2938
  }
3371
2939
  }
3372
2940
 
@@ -3621,7 +3189,7 @@ class OmpRpcProcess {
3621
3189
  this.activeToolCallIds.add(event.toolCallId);
3622
3190
  return true;
3623
3191
  }
3624
- if (event.type === "tool_execution_update" || event.type === "tool_stream_update") {
3192
+ if (event.type === "tool_execution_update") {
3625
3193
  return this.activeToolCallIds.has(event.toolCallId);
3626
3194
  }
3627
3195
  if (event.type === "tool_execution_end") {
@@ -3832,7 +3400,6 @@ class OmpRpcSession implements OmpRuntimeSession {
3832
3400
  readonly inheritedRedactionValues: readonly string[],
3833
3401
  readonly supportsTypedToolApprovals: boolean,
3834
3402
  ) {}
3835
- private messagePagingSupport: "unknown" | "supported" | "unsupported" = "unknown";
3836
3403
 
3837
3404
  onEvent(listener: (event: OmpRpcEvent) => void): () => void {
3838
3405
  return this.process.onEvent(listener);
@@ -3846,195 +3413,6 @@ class OmpRpcSession implements OmpRuntimeSession {
3846
3413
  return OmpSessionStatsSchema.parse(await this.process.request({ type: "get_session_stats" }));
3847
3414
  }
3848
3415
 
3849
- async getMessages(signal?: AbortSignal): Promise<OmpMessage[]> {
3850
- if (!this.canReplayHistory) {
3851
- throw new Error("OMP history replay requires negotiated RPC protocol v2");
3852
- }
3853
- if (this.messagePagingSupport === "unsupported") {
3854
- const legacy = OmpMessagesResultSchema.parse(
3855
- await this.requestWithSignal({ type: "get_messages" }, signal),
3856
- );
3857
- return legacy.messages;
3858
- }
3859
- let staleRestarts = 0;
3860
- let busyRetries = 0;
3861
- while (true) {
3862
- const messages: OmpMessage[] = [];
3863
- const seenCursors = new Set<string>();
3864
- const seenMessageIdentities = new Set<string>();
3865
- let pageCount = 0;
3866
- let cursor: string | undefined;
3867
- let totalMessages: number | undefined;
3868
- let aggregateBytes = 0;
3869
- let aggregateNodes = 0;
3870
- try {
3871
- while (true) {
3872
- signal?.throwIfAborted();
3873
- let rawPage: unknown;
3874
- try {
3875
- rawPage = await this.requestWithSignal(
3876
- {
3877
- type: "get_messages_page",
3878
- ...(cursor ? { cursor } : {}),
3879
- limit: OMP_MESSAGE_PAGE_LIMIT,
3880
- },
3881
- signal,
3882
- );
3883
- } catch (error) {
3884
- if (
3885
- error instanceof OmpRpcRequestRejectedError &&
3886
- error.code === "unsupported_command"
3887
- ) {
3888
- this.messagePagingSupport = "unsupported";
3889
- const legacy = OmpMessagesResultSchema.parse(
3890
- await this.requestWithSignal({ type: "get_messages" }, signal),
3891
- );
3892
- return legacy.messages;
3893
- }
3894
- if (
3895
- error instanceof OmpRpcRequestRejectedError &&
3896
- error.code === "session_busy" &&
3897
- busyRetries < MAX_MESSAGE_PAGE_BUSY_RETRIES
3898
- ) {
3899
- const delayMs = MESSAGE_PAGE_RETRY_BASE_MS * 2 ** busyRetries;
3900
- busyRetries += 1;
3901
- await this.waitForReplayRetry(delayMs, signal);
3902
- continue;
3903
- }
3904
- throw error;
3905
- }
3906
- pageCount += 1;
3907
- if (pageCount > MAX_MESSAGE_PAGES) {
3908
- throw new OmpRpcResponseLimitError(
3909
- "get_messages_page",
3910
- "items",
3911
- pageCount,
3912
- MAX_MESSAGE_PAGES,
3913
- );
3914
- }
3915
- const page = OmpMessagesPageResultSchema.parse(rawPage);
3916
- this.messagePagingSupport = "supported";
3917
- if (totalMessages !== undefined && page.totalMessages !== totalMessages) {
3918
- throw new Error("OMP message pagination returned an inconsistent total");
3919
- }
3920
- totalMessages ??= page.totalMessages;
3921
- const nextMessageCount = messages.length + page.messages.length;
3922
- if (nextMessageCount > totalMessages || nextMessageCount > MAX_REPLAY_MESSAGES) {
3923
- throw new OmpRpcResponseLimitError(
3924
- "get_messages_page",
3925
- "items",
3926
- nextMessageCount,
3927
- Math.min(totalMessages, MAX_REPLAY_MESSAGES),
3928
- );
3929
- }
3930
- const pageIdentities = new Set<string>();
3931
- for (const message of page.messages) {
3932
- const identity = message.entryId ?? message.id ?? message.responseId;
3933
- if (!identity) continue;
3934
- if (seenMessageIdentities.has(identity)) {
3935
- throw new Error("OMP message pagination repeated a message identity");
3936
- }
3937
- pageIdentities.add(identity);
3938
- }
3939
- const inspection = inspectJsonBounds(
3940
- page.messages,
3941
- MAX_REPLAY_BYTES,
3942
- MAX_REPLAY_MESSAGES,
3943
- MAX_IMAGE_DATA_LENGTH,
3944
- MAX_REPLAY_NODES,
3945
- );
3946
- if (inspection.violation) {
3947
- throw new OmpRpcResponseLimitError(
3948
- "get_messages_page",
3949
- inspection.violation.dimension,
3950
- inspection.violation.actual,
3951
- inspection.violation.limit,
3952
- );
3953
- }
3954
- aggregateBytes += inspection.bytes;
3955
- aggregateNodes += inspection.nodes;
3956
- if (aggregateBytes > MAX_REPLAY_BYTES) {
3957
- throw new OmpRpcResponseLimitError(
3958
- "get_messages_page",
3959
- "bytes",
3960
- aggregateBytes,
3961
- MAX_REPLAY_BYTES,
3962
- );
3963
- }
3964
- if (aggregateNodes > MAX_REPLAY_NODES) {
3965
- throw new OmpRpcResponseLimitError(
3966
- "get_messages_page",
3967
- "nodes",
3968
- aggregateNodes,
3969
- MAX_REPLAY_NODES,
3970
- );
3971
- }
3972
- for (const identity of pageIdentities) seenMessageIdentities.add(identity);
3973
- messages.push(...page.messages);
3974
- const nextCursor = page.nextCursor;
3975
- if (!nextCursor) {
3976
- if (messages.length !== totalMessages) {
3977
- throw new Error("OMP message pagination ended before the advertised total");
3978
- }
3979
- return messages;
3980
- }
3981
- if (page.messages.length === 0 || messages.length >= totalMessages) {
3982
- throw new Error("OMP message pagination did not make progress");
3983
- }
3984
- if (seenCursors.has(nextCursor)) {
3985
- throw new Error("OMP message pagination repeated a cursor");
3986
- }
3987
- seenCursors.add(nextCursor);
3988
- cursor = nextCursor;
3989
- }
3990
- } catch (error) {
3991
- if (
3992
- error instanceof OmpRpcRequestRejectedError &&
3993
- error.code === "stale_cursor" &&
3994
- staleRestarts < MAX_MESSAGE_PAGE_STALE_RESTARTS
3995
- ) {
3996
- staleRestarts += 1;
3997
- continue;
3998
- }
3999
- throw error;
4000
- }
4001
- }
4002
- }
4003
-
4004
- private async requestWithSignal(
4005
- command: Record<string, unknown>,
4006
- signal?: AbortSignal,
4007
- ): Promise<unknown> {
4008
- signal?.throwIfAborted();
4009
- const request = this.process.request(command);
4010
- if (!signal) return request;
4011
- const aborted = Promise.withResolvers<never>();
4012
- const onAbort = () => aborted.reject(signal.reason);
4013
- signal.addEventListener("abort", onAbort, { once: true });
4014
- try {
4015
- return await Promise.race([request, aborted.promise]);
4016
- } finally {
4017
- signal.removeEventListener("abort", onAbort);
4018
- void request.catch(() => undefined);
4019
- }
4020
- }
4021
-
4022
- private async waitForReplayRetry(delayMs: number, signal?: AbortSignal): Promise<void> {
4023
- signal?.throwIfAborted();
4024
- if (!signal) {
4025
- await waitMs(delayMs);
4026
- return;
4027
- }
4028
- const aborted = Promise.withResolvers<never>();
4029
- const onAbort = () => aborted.reject(signal.reason);
4030
- signal.addEventListener("abort", onAbort, { once: true });
4031
- try {
4032
- await Promise.race([waitMs(delayMs), aborted.promise]);
4033
- } finally {
4034
- signal.removeEventListener("abort", onAbort);
4035
- }
4036
- }
4037
-
4038
3416
  async compact(customInstructions?: string): Promise<OmpCompactionResult> {
4039
3417
  const instructions =
4040
3418
  customInstructions === undefined
@@ -4130,6 +3508,16 @@ class OmpRpcSession implements OmpRuntimeSession {
4130
3508
  return result.data;
4131
3509
  }
4132
3510
 
3511
+ async getMessages(): Promise<OmpMessage[]> {
3512
+ if (!this.canReplayHistory) {
3513
+ throw new Error("OMP history replay requires negotiated RPC protocol v2");
3514
+ }
3515
+ const result = OmpMessagesResultSchema.parse(
3516
+ await this.process.request({ type: "get_messages" }),
3517
+ );
3518
+ return result.messages;
3519
+ }
3520
+
4133
3521
  async setHostTools(tools: readonly OmpHostToolDefinition[]): Promise<string[]> {
4134
3522
  const safeTools = z.array(OmpHostToolDefinitionSchema).max(MAX_HOST_TOOLS).parse(tools);
4135
3523
  if (safeTools.length === 0) return [];
@@ -4239,14 +3627,13 @@ export class OmpRpcRuntime implements OmpRuntime {
4239
3627
  ...transcript,
4240
3628
  messages: z
4241
3629
  .array(OmpMessageSchema)
4242
- .max(MAX_REPLAY_MESSAGES)
3630
+ .max(100_000)
4243
3631
  .parse(sanitizeMessageListMetadata(transcript.messages)),
4244
3632
  };
4245
3633
  }
4246
3634
  async readPersistedSubagentTranscript(options: {
4247
3635
  parentSessionFile: string;
4248
3636
  childTranscriptId: string;
4249
- sessionFile?: string;
4250
3637
  cwd: string;
4251
3638
  signal?: AbortSignal;
4252
3639
  }): Promise<OmpPersistedSubagentMessages> {
@@ -4255,13 +3642,12 @@ export class OmpRpcRuntime implements OmpRuntime {
4255
3642
  options.childTranscriptId,
4256
3643
  options.cwd,
4257
3644
  options.signal,
4258
- options.sessionFile,
4259
3645
  );
4260
3646
  return {
4261
3647
  ...transcript,
4262
3648
  messages: z
4263
3649
  .array(OmpMessageSchema)
4264
- .max(MAX_REPLAY_MESSAGES)
3650
+ .max(100_000)
4265
3651
  .parse(sanitizeMessageListMetadata(transcript.messages)),
4266
3652
  };
4267
3653
  }