@omercnet/paseo-omp 0.4.0-next.114.1 → 0.4.0-next.119.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 +3 -1
- package/server/package-version.ts +1 -1
- package/server/provider/connection.ts +28 -1
- package/server/provider/omp-rpc.ts +792 -178
- package/server/provider/session-descriptors.ts +52 -18
- package/server/provider/session.ts +12 -1
- package/server/provider/subsessions.ts +28 -6
- package/server/provider/timeline-projector.ts +94 -24
|
@@ -86,7 +86,105 @@ 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
|
-
|
|
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;
|
|
90
188
|
export const OMP_PROTOCOL_VIOLATION_CATEGORIES = [
|
|
91
189
|
"duplicate-ready",
|
|
92
190
|
"frame-limit",
|
|
@@ -156,6 +254,7 @@ export const OMP_PROTOCOL_EVENT_TYPES = [
|
|
|
156
254
|
"tool_execution_start",
|
|
157
255
|
"tool_execution_update",
|
|
158
256
|
"tool_execution_end",
|
|
257
|
+
"tool_stream_update",
|
|
159
258
|
"compaction_start",
|
|
160
259
|
"compaction_end",
|
|
161
260
|
"subagent_lifecycle",
|
|
@@ -163,6 +262,10 @@ export const OMP_PROTOCOL_EVENT_TYPES = [
|
|
|
163
262
|
"subagent_event",
|
|
164
263
|
"todo_reminder",
|
|
165
264
|
"model_changed",
|
|
265
|
+
"config_warnings_changed",
|
|
266
|
+
"advisor_cost_changed",
|
|
267
|
+
"ttsr_triggered",
|
|
268
|
+
"irc_message",
|
|
166
269
|
"thinking_level_changed",
|
|
167
270
|
"goal_updated",
|
|
168
271
|
"auto_retry_start",
|
|
@@ -292,8 +395,11 @@ function eventSchemaClassification(
|
|
|
292
395
|
case "message_update":
|
|
293
396
|
case "message_end":
|
|
294
397
|
return { reason: "message-event-schema", expected: "valid-message-event" };
|
|
398
|
+
case "irc_message":
|
|
399
|
+
return { reason: "message-event-schema", expected: "valid-message-event" };
|
|
295
400
|
case "tool_execution_start":
|
|
296
401
|
case "tool_execution_update":
|
|
402
|
+
case "tool_stream_update":
|
|
297
403
|
case "tool_execution_end":
|
|
298
404
|
case "host_tool_call":
|
|
299
405
|
case "host_tool_cancel":
|
|
@@ -312,6 +418,9 @@ function eventSchemaClassification(
|
|
|
312
418
|
case "available_commands_update":
|
|
313
419
|
case "retry_fallback_applied":
|
|
314
420
|
case "retry_fallback_succeeded":
|
|
421
|
+
case "config_warnings_changed":
|
|
422
|
+
case "advisor_cost_changed":
|
|
423
|
+
case "ttsr_triggered":
|
|
315
424
|
return { reason: "configuration-event-schema", expected: "valid-configuration-event" };
|
|
316
425
|
case "extension_ui_request":
|
|
317
426
|
return { reason: "extension-ui-schema", expected: "valid-extension-ui-event" };
|
|
@@ -408,6 +517,13 @@ function boundedString(maxBytes: number, minBytes = 0) {
|
|
|
408
517
|
return bytes >= minBytes && bytes <= maxBytes;
|
|
409
518
|
});
|
|
410
519
|
}
|
|
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
|
+
}
|
|
411
527
|
function boundedOptionalStringArray(maxItems: number, maxBytes: number) {
|
|
412
528
|
return z.unknown().transform((value): string[] | undefined => {
|
|
413
529
|
if (!Array.isArray(value)) return undefined;
|
|
@@ -465,7 +581,7 @@ function sanitizeLiveMessageDisplay(value: unknown): unknown {
|
|
|
465
581
|
return content === message.content ? value : { ...message, content };
|
|
466
582
|
}
|
|
467
583
|
if (
|
|
468
|
-
message.role === "bashExecution" &&
|
|
584
|
+
(message.role === "bashExecution" || message.role === "pythonExecution") &&
|
|
469
585
|
typeof message.output === "string" &&
|
|
470
586
|
utf8Bytes(message.output) > MAX_IMAGE_DATA_LENGTH
|
|
471
587
|
) {
|
|
@@ -507,6 +623,178 @@ function isBoundedJson(
|
|
|
507
623
|
boundedJsonBytes(value, maxBytes, maxItems, maxBytes, maxNodes) !== Number.POSITIVE_INFINITY
|
|
508
624
|
);
|
|
509
625
|
}
|
|
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
|
+
}
|
|
510
798
|
function optionalMetadataMetrics(value: unknown) {
|
|
511
799
|
return boundedJsonMetrics(
|
|
512
800
|
value,
|
|
@@ -706,52 +994,46 @@ const OmpMessageIdentityShape = {
|
|
|
706
994
|
timestamp: z.number().finite().optional(),
|
|
707
995
|
details: OmpOptionalMetadataSchema,
|
|
708
996
|
};
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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,
|
|
725
1014
|
};
|
|
726
|
-
|
|
727
|
-
(
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
output?: string;
|
|
747
|
-
exitCode?: number | null;
|
|
748
|
-
cancelled?: boolean;
|
|
749
|
-
truncated?: boolean;
|
|
750
|
-
}
|
|
751
|
-
| { role: "custom"; customType?: string; content?: unknown; display?: boolean }
|
|
752
|
-
);
|
|
1015
|
+
const OmpCustomMessageShape = {
|
|
1016
|
+
customType: NAME.optional(),
|
|
1017
|
+
content: OmpDisplayContentSchema.optional(),
|
|
1018
|
+
display: z.boolean().optional(),
|
|
1019
|
+
attribution: OmpOpaqueMessagePayloadSchema.optional(),
|
|
1020
|
+
};
|
|
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
|
+
});
|
|
753
1035
|
|
|
754
|
-
const OmpMessageSchema
|
|
1036
|
+
const OmpMessageSchema = z.discriminatedUnion("role", [
|
|
755
1037
|
z.object({
|
|
756
1038
|
role: z.literal("assistant"),
|
|
757
1039
|
content: OmpAssistantDisplayContentSchema.optional(),
|
|
@@ -764,42 +1046,70 @@ const OmpMessageSchema: z.ZodType<OmpMessage> = z.union([
|
|
|
764
1046
|
content: OmpDisplayContentSchema,
|
|
765
1047
|
...OmpMessageIdentityShape,
|
|
766
1048
|
}),
|
|
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
|
+
}),
|
|
767
1058
|
z.object({
|
|
768
1059
|
role: z.literal("toolResult"),
|
|
769
1060
|
toolCallId: IDENTIFIER,
|
|
770
1061
|
toolName: NAME,
|
|
771
|
-
content:
|
|
772
|
-
.unknown()
|
|
773
|
-
.refine((value) => isBoundedJson(value, MAX_SEMANTIC_FRAME_BYTES, 1_024, 8_192)),
|
|
1062
|
+
content: OmpOpaqueMessagePayloadSchema,
|
|
774
1063
|
isError: z.boolean().optional(),
|
|
1064
|
+
attribution: OmpOpaqueMessagePayloadSchema.optional(),
|
|
1065
|
+
prunedAt: z.number().finite().nonnegative().optional(),
|
|
1066
|
+
providerMetadata: OmpOpaqueMessagePayloadSchema.optional(),
|
|
1067
|
+
useless: z.boolean().optional(),
|
|
775
1068
|
...OmpMessageIdentityShape,
|
|
776
1069
|
}),
|
|
777
1070
|
z.object({
|
|
778
1071
|
role: z.literal("bashExecution"),
|
|
779
1072
|
command: TEXT,
|
|
780
|
-
|
|
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(),
|
|
1073
|
+
...OmpExecutionMessageShape,
|
|
790
1074
|
...OmpMessageIdentityShape,
|
|
791
1075
|
}),
|
|
792
1076
|
z.object({
|
|
793
|
-
role: z.literal("
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
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(),
|
|
1104
|
+
...OmpMessageIdentityShape,
|
|
1105
|
+
}),
|
|
1106
|
+
z.object({
|
|
1107
|
+
role: z.literal("fileMention"),
|
|
1108
|
+
files: z.array(OmpFileMentionSchema).max(MAX_ARRAY_ITEMS),
|
|
800
1109
|
...OmpMessageIdentityShape,
|
|
801
1110
|
}),
|
|
802
1111
|
]);
|
|
1112
|
+
export type OmpMessage = z.infer<typeof OmpMessageSchema>;
|
|
803
1113
|
|
|
804
1114
|
const OmpAssistantMessageEventSchema = z
|
|
805
1115
|
.object({
|
|
@@ -948,6 +1258,7 @@ const OmpReadyFrameSchema = z.object({
|
|
|
948
1258
|
const OmpResponseFrameSchema = z.object({
|
|
949
1259
|
type: z.literal("response"),
|
|
950
1260
|
id: IDENTIFIER,
|
|
1261
|
+
command: boundedString(64, 1).optional(),
|
|
951
1262
|
success: z.boolean(),
|
|
952
1263
|
data: z.unknown().optional(),
|
|
953
1264
|
error: boundedString(MAX_RPC_ERROR_BYTES).optional(),
|
|
@@ -1153,7 +1464,7 @@ const OmpCompactionEndSchema = z.object({
|
|
|
1153
1464
|
errorMessage: boundedString(4_096).optional(),
|
|
1154
1465
|
skipped: z.boolean().optional(),
|
|
1155
1466
|
});
|
|
1156
|
-
const
|
|
1467
|
+
const OmpCoreAgentEventSchema = z.discriminatedUnion("type", [
|
|
1157
1468
|
z.object({ type: z.literal("agent_start") }),
|
|
1158
1469
|
z.object({
|
|
1159
1470
|
type: z.literal("agent_end"),
|
|
@@ -1184,6 +1495,12 @@ const OmpAgentSessionEventSchema = z.discriminatedUnion("type", [
|
|
|
1184
1495
|
args: BoundedToolPayloadSchema.optional(),
|
|
1185
1496
|
partialResult: OmpToolResultPayloadSchema,
|
|
1186
1497
|
}),
|
|
1498
|
+
z.object({
|
|
1499
|
+
type: z.literal("tool_stream_update"),
|
|
1500
|
+
toolCallId: IDENTIFIER,
|
|
1501
|
+
toolName: NAME,
|
|
1502
|
+
update: BoundedToolPayloadSchema,
|
|
1503
|
+
}),
|
|
1187
1504
|
z.object({
|
|
1188
1505
|
type: z.literal("tool_execution_end"),
|
|
1189
1506
|
toolCallId: IDENTIFIER,
|
|
@@ -1210,6 +1527,99 @@ const OmpGoalModeStateSchema = z.object({
|
|
|
1210
1527
|
reason: boundedString(4_096).optional(),
|
|
1211
1528
|
goal: OmpGoalSchema.optional(),
|
|
1212
1529
|
});
|
|
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
|
+
]);
|
|
1213
1623
|
const OmpSubagentStatusSchema = z.enum([
|
|
1214
1624
|
"pending",
|
|
1215
1625
|
"running",
|
|
@@ -1352,80 +1762,10 @@ const OmpRuntimeEventSchema = z.discriminatedUnion("type", [
|
|
|
1352
1762
|
type: z.literal("subagent_event"),
|
|
1353
1763
|
payload: z.object({ id: IDENTIFIER, event: OmpAgentSessionEventSchema }),
|
|
1354
1764
|
}),
|
|
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
|
-
}),
|
|
1418
1765
|
z.object({
|
|
1419
1766
|
type: z.literal("available_commands_update"),
|
|
1420
1767
|
commands: z.array(OmpAvailableCommandSchema).max(MAX_ARRAY_ITEMS),
|
|
1421
1768
|
}),
|
|
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
|
-
}),
|
|
1429
1769
|
z.object({ type: z.literal("command_output"), text: TEXT.optional() }),
|
|
1430
1770
|
OmpExtensionUiRequestSchema,
|
|
1431
1771
|
z.object({
|
|
@@ -1437,7 +1777,6 @@ const OmpRuntimeEventSchema = z.discriminatedUnion("type", [
|
|
|
1437
1777
|
OmpHostToolCancelSchema,
|
|
1438
1778
|
OmpToolApprovalRequestSchema,
|
|
1439
1779
|
OmpToolApprovalCancelSchema,
|
|
1440
|
-
z.object({ type: z.literal("advisor_yielded") }),
|
|
1441
1780
|
]);
|
|
1442
1781
|
type OptionalDetailsMapper = (value: unknown, taskResult?: boolean) => unknown;
|
|
1443
1782
|
|
|
@@ -1492,6 +1831,8 @@ function mapAgentEventDetails(
|
|
|
1492
1831
|
return mapRecordField(frame, "result", map, frame.toolName === "task");
|
|
1493
1832
|
case "agent_end":
|
|
1494
1833
|
return mapRecordField(frame, "messages", (messages) => mapMessageList(messages, map));
|
|
1834
|
+
case "irc_message":
|
|
1835
|
+
return mapRecordField(frame, "message", map);
|
|
1495
1836
|
default:
|
|
1496
1837
|
return frame;
|
|
1497
1838
|
}
|
|
@@ -1522,6 +1863,25 @@ function sanitizeHistoryResponseData(value: unknown): unknown {
|
|
|
1522
1863
|
return mapRecordField(value as Record<string, unknown>, "messages", sanitizeMessageListMetadata);
|
|
1523
1864
|
}
|
|
1524
1865
|
|
|
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
|
+
|
|
1525
1885
|
function runtimeFrameCollectionLimit(frame: Record<string, unknown>): number {
|
|
1526
1886
|
const type = frame.type;
|
|
1527
1887
|
if (
|
|
@@ -1547,7 +1907,12 @@ const OmpBranchMessagesResultSchema = z.object({
|
|
|
1547
1907
|
});
|
|
1548
1908
|
const OmpBranchResultSchema = z.object({ text: TEXT, cancelled: z.boolean() });
|
|
1549
1909
|
const OmpMessagesResultSchema = z.object({
|
|
1550
|
-
messages: z.array(OmpMessageSchema).max(
|
|
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),
|
|
1551
1916
|
});
|
|
1552
1917
|
const OmpSubagentsResultSchema = z.object({
|
|
1553
1918
|
subagents: z
|
|
@@ -1573,7 +1938,7 @@ const OmpSubagentMessagesResultSchema = z.object({
|
|
|
1573
1938
|
fromByte: z.number().int().nonnegative(),
|
|
1574
1939
|
nextByte: z.number().int().nonnegative(),
|
|
1575
1940
|
reset: z.boolean(),
|
|
1576
|
-
messages: z.array(OmpMessageSchema).max(
|
|
1941
|
+
messages: z.array(OmpMessageSchema).max(MAX_REPLAY_MESSAGES),
|
|
1577
1942
|
});
|
|
1578
1943
|
const ProtocolNegotiationResultSchema = z.object({
|
|
1579
1944
|
protocolVersion: z.literal(2),
|
|
@@ -1691,7 +2056,7 @@ export interface OmpRuntimeSession {
|
|
|
1691
2056
|
getBranchMessages(): Promise<Array<{ entryId: string; text: string }>>;
|
|
1692
2057
|
branch(entryId: string): Promise<OmpBranchResult>;
|
|
1693
2058
|
readonly canReplayHistory: boolean;
|
|
1694
|
-
getMessages(): Promise<OmpMessage[]>;
|
|
2059
|
+
getMessages(signal?: AbortSignal): Promise<OmpMessage[]>;
|
|
1695
2060
|
abort(): Promise<void>;
|
|
1696
2061
|
setHostTools(tools: readonly OmpHostToolDefinition[]): Promise<string[]>;
|
|
1697
2062
|
sendHostToolResult(result: OmpHostToolResult): void;
|
|
@@ -1712,6 +2077,7 @@ export interface OmpRuntime {
|
|
|
1712
2077
|
readPersistedSubagentTranscript(options: {
|
|
1713
2078
|
parentSessionFile: string;
|
|
1714
2079
|
childTranscriptId: string;
|
|
2080
|
+
sessionFile?: string;
|
|
1715
2081
|
cwd: string;
|
|
1716
2082
|
signal?: AbortSignal;
|
|
1717
2083
|
}): Promise<OmpPersistedSubagentMessages>;
|
|
@@ -1737,13 +2103,30 @@ export interface OmpRpcRuntimeOptions {
|
|
|
1737
2103
|
) => OmpSessionDescriptor[] | Promise<OmpSessionDescriptor[]>;
|
|
1738
2104
|
}
|
|
1739
2105
|
|
|
2106
|
+
export type OmpRpcRequestRejectionCode = "session_busy" | "stale_cursor" | "unsupported_command";
|
|
2107
|
+
|
|
1740
2108
|
export class OmpRpcRequestRejectedError extends Error {
|
|
1741
|
-
constructor(
|
|
2109
|
+
constructor(
|
|
2110
|
+
readonly command?: OmpRpcDiagnosticCommand,
|
|
2111
|
+
readonly code?: OmpRpcRequestRejectionCode,
|
|
2112
|
+
) {
|
|
1742
2113
|
super("OMP RPC request failed");
|
|
1743
2114
|
this.name = "OmpRpcRequestRejectedError";
|
|
1744
2115
|
}
|
|
1745
2116
|
}
|
|
1746
2117
|
|
|
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
|
+
|
|
1747
2130
|
type PendingRequest = {
|
|
1748
2131
|
resolve(value: unknown): void;
|
|
1749
2132
|
reject(error: Error): void;
|
|
@@ -2618,8 +3001,13 @@ class OmpRpcProcess {
|
|
|
2618
3001
|
} else {
|
|
2619
3002
|
this.appendLinePart(part);
|
|
2620
3003
|
if (this.fatalError) return;
|
|
2621
|
-
if (this.discardingLine)
|
|
2622
|
-
|
|
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();
|
|
2623
3011
|
}
|
|
2624
3012
|
start = index + 1;
|
|
2625
3013
|
}
|
|
@@ -2674,10 +3062,6 @@ class OmpRpcProcess {
|
|
|
2674
3062
|
this.lineParts = [];
|
|
2675
3063
|
this.lineBytes = 0;
|
|
2676
3064
|
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
|
-
}
|
|
2681
3065
|
let decoded: unknown;
|
|
2682
3066
|
try {
|
|
2683
3067
|
decoded = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payload));
|
|
@@ -2699,7 +3083,10 @@ class OmpRpcProcess {
|
|
|
2699
3083
|
frame.byteLength > MAX_SEMANTIC_FRAME_BYTES &&
|
|
2700
3084
|
![...this.pending.values()].some(
|
|
2701
3085
|
(pending) =>
|
|
2702
|
-
pending.command === "get_messages" ||
|
|
3086
|
+
pending.command === "get_messages" ||
|
|
3087
|
+
pending.command === "get_messages_page" ||
|
|
3088
|
+
pending.command === "get_subagent_messages" ||
|
|
3089
|
+
pending.command === "get_state",
|
|
2703
3090
|
)
|
|
2704
3091
|
) {
|
|
2705
3092
|
this.fail(new Error("OMP RPC frame exceeds the semantic byte limit"));
|
|
@@ -2805,12 +3192,16 @@ class OmpRpcProcess {
|
|
|
2805
3192
|
const pending = typeof frame.id === "string" ? this.pending.get(frame.id) : undefined;
|
|
2806
3193
|
if (
|
|
2807
3194
|
frame.type === "response" &&
|
|
2808
|
-
(pending?.command === "get_messages" ||
|
|
3195
|
+
(pending?.command === "get_messages" ||
|
|
3196
|
+
pending?.command === "get_messages_page" ||
|
|
3197
|
+
pending?.command === "get_subagent_messages" ||
|
|
3198
|
+
pending?.command === "get_state")
|
|
2809
3199
|
) {
|
|
2810
3200
|
this.receiveResponse(frame);
|
|
2811
3201
|
return;
|
|
2812
3202
|
}
|
|
2813
3203
|
}
|
|
3204
|
+
if (this.receiveUnsupportedCommandResponse(value)) return;
|
|
2814
3205
|
if (rawByteLength > MAX_SEMANTIC_FRAME_BYTES) {
|
|
2815
3206
|
this.fail(new Error("OMP RPC frame exceeds the semantic byte limit"));
|
|
2816
3207
|
return;
|
|
@@ -2845,6 +3236,30 @@ class OmpRpcProcess {
|
|
|
2845
3236
|
return true;
|
|
2846
3237
|
}
|
|
2847
3238
|
|
|
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
|
+
|
|
2848
3263
|
private receiveResponse(frame: Record<string, unknown>): void {
|
|
2849
3264
|
const rawId = typeof frame.id === "string" ? frame.id : undefined;
|
|
2850
3265
|
const knownPending = rawId ? this.pending.get(rawId) : undefined;
|
|
@@ -2877,10 +3292,14 @@ class OmpRpcProcess {
|
|
|
2877
3292
|
}
|
|
2878
3293
|
const isBranchHistory = pending.command === "get_branch_messages";
|
|
2879
3294
|
const isHistory =
|
|
2880
|
-
pending.command === "get_messages" ||
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
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;
|
|
2884
3303
|
const boundedFrame =
|
|
2885
3304
|
responseData === response.data.data ? frame : { ...frame, data: responseData };
|
|
2886
3305
|
const responseItemLimit = isBranchHistory
|
|
@@ -2903,17 +3322,21 @@ class OmpRpcProcess {
|
|
|
2903
3322
|
: pending.command === "get_available_models"
|
|
2904
3323
|
? 16_384
|
|
2905
3324
|
: 2_048;
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
) {
|
|
3325
|
+
const violation = jsonBoundViolation(
|
|
3326
|
+
boundedFrame,
|
|
3327
|
+
responseByteLimit,
|
|
3328
|
+
responseItemLimit,
|
|
3329
|
+
MAX_IMAGE_DATA_LENGTH,
|
|
3330
|
+
responseNodeLimit,
|
|
3331
|
+
);
|
|
3332
|
+
if (violation) {
|
|
2915
3333
|
this.takePending(response.data.id)?.reject(
|
|
2916
|
-
new
|
|
3334
|
+
new OmpRpcResponseLimitError(
|
|
3335
|
+
rpcDiagnosticCommand(pending.command),
|
|
3336
|
+
violation.dimension,
|
|
3337
|
+
violation.actual,
|
|
3338
|
+
violation.limit,
|
|
3339
|
+
),
|
|
2917
3340
|
);
|
|
2918
3341
|
return;
|
|
2919
3342
|
}
|
|
@@ -2934,7 +3357,16 @@ class OmpRpcProcess {
|
|
|
2934
3357
|
settled.reject(new Error("OMP RPC response is invalid"));
|
|
2935
3358
|
}
|
|
2936
3359
|
} else {
|
|
2937
|
-
|
|
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));
|
|
2938
3370
|
}
|
|
2939
3371
|
}
|
|
2940
3372
|
|
|
@@ -3189,7 +3621,7 @@ class OmpRpcProcess {
|
|
|
3189
3621
|
this.activeToolCallIds.add(event.toolCallId);
|
|
3190
3622
|
return true;
|
|
3191
3623
|
}
|
|
3192
|
-
if (event.type === "tool_execution_update") {
|
|
3624
|
+
if (event.type === "tool_execution_update" || event.type === "tool_stream_update") {
|
|
3193
3625
|
return this.activeToolCallIds.has(event.toolCallId);
|
|
3194
3626
|
}
|
|
3195
3627
|
if (event.type === "tool_execution_end") {
|
|
@@ -3400,6 +3832,7 @@ class OmpRpcSession implements OmpRuntimeSession {
|
|
|
3400
3832
|
readonly inheritedRedactionValues: readonly string[],
|
|
3401
3833
|
readonly supportsTypedToolApprovals: boolean,
|
|
3402
3834
|
) {}
|
|
3835
|
+
private messagePagingSupport: "unknown" | "supported" | "unsupported" = "unknown";
|
|
3403
3836
|
|
|
3404
3837
|
onEvent(listener: (event: OmpRpcEvent) => void): () => void {
|
|
3405
3838
|
return this.process.onEvent(listener);
|
|
@@ -3413,6 +3846,195 @@ class OmpRpcSession implements OmpRuntimeSession {
|
|
|
3413
3846
|
return OmpSessionStatsSchema.parse(await this.process.request({ type: "get_session_stats" }));
|
|
3414
3847
|
}
|
|
3415
3848
|
|
|
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
|
+
|
|
3416
4038
|
async compact(customInstructions?: string): Promise<OmpCompactionResult> {
|
|
3417
4039
|
const instructions =
|
|
3418
4040
|
customInstructions === undefined
|
|
@@ -3508,16 +4130,6 @@ class OmpRpcSession implements OmpRuntimeSession {
|
|
|
3508
4130
|
return result.data;
|
|
3509
4131
|
}
|
|
3510
4132
|
|
|
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
|
-
|
|
3521
4133
|
async setHostTools(tools: readonly OmpHostToolDefinition[]): Promise<string[]> {
|
|
3522
4134
|
const safeTools = z.array(OmpHostToolDefinitionSchema).max(MAX_HOST_TOOLS).parse(tools);
|
|
3523
4135
|
if (safeTools.length === 0) return [];
|
|
@@ -3627,13 +4239,14 @@ export class OmpRpcRuntime implements OmpRuntime {
|
|
|
3627
4239
|
...transcript,
|
|
3628
4240
|
messages: z
|
|
3629
4241
|
.array(OmpMessageSchema)
|
|
3630
|
-
.max(
|
|
4242
|
+
.max(MAX_REPLAY_MESSAGES)
|
|
3631
4243
|
.parse(sanitizeMessageListMetadata(transcript.messages)),
|
|
3632
4244
|
};
|
|
3633
4245
|
}
|
|
3634
4246
|
async readPersistedSubagentTranscript(options: {
|
|
3635
4247
|
parentSessionFile: string;
|
|
3636
4248
|
childTranscriptId: string;
|
|
4249
|
+
sessionFile?: string;
|
|
3637
4250
|
cwd: string;
|
|
3638
4251
|
signal?: AbortSignal;
|
|
3639
4252
|
}): Promise<OmpPersistedSubagentMessages> {
|
|
@@ -3642,12 +4255,13 @@ export class OmpRpcRuntime implements OmpRuntime {
|
|
|
3642
4255
|
options.childTranscriptId,
|
|
3643
4256
|
options.cwd,
|
|
3644
4257
|
options.signal,
|
|
4258
|
+
options.sessionFile,
|
|
3645
4259
|
);
|
|
3646
4260
|
return {
|
|
3647
4261
|
...transcript,
|
|
3648
4262
|
messages: z
|
|
3649
4263
|
.array(OmpMessageSchema)
|
|
3650
|
-
.max(
|
|
4264
|
+
.max(MAX_REPLAY_MESSAGES)
|
|
3651
4265
|
.parse(sanitizeMessageListMetadata(transcript.messages)),
|
|
3652
4266
|
};
|
|
3653
4267
|
}
|