@omercnet/paseo-omp 0.2.1 → 0.3.0-next.100.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/CHANGELOG.md +26 -0
- package/README.md +25 -13
- package/SUPPORT.md +7 -3
- package/TESTING.md +21 -18
- package/client/composer-pill-settings.tsx +157 -0
- package/client/external-url.ts +15 -0
- package/client/mcp-authorization.tsx +169 -0
- package/client/mcp-popover.tsx +155 -0
- package/client/memory-panel.tsx +8 -3
- package/client/memory-popover.tsx +8 -4
- package/client/omp-config-surface.tsx +189 -29
- package/client/omp-plugin-manager.tsx +302 -131
- package/client/omp-store-picker.tsx +89 -0
- package/client/omp-store-state.ts +45 -0
- package/client/paseo-types.ts +9 -0
- package/client/provider-diagnostics-state.ts +18 -7
- package/client/quota-popover.tsx +8 -3
- package/client/quota-state.ts +16 -7
- package/client/sessions-popover.tsx +8 -3
- package/docs/alpha-release-checklist.md +6 -8
- package/docs/configuration.md +8 -4
- package/docs/core-provider-issue-audit.md +3 -2
- package/docs/images/mcp-authorization-compact.png +0 -0
- package/docs/images/mcp-controls-wide.png +0 -0
- package/docs/images/plugin-manager.png +0 -0
- package/docs/images/workspace-settings.png +0 -0
- package/docs/installation.md +35 -19
- package/index.client.tsx +339 -123
- package/index.server.ts +44 -14
- package/package.json +7 -8
- package/paseo-plugin.json +2 -2
- package/scripts/prepare-dependencies.mjs +24 -0
- package/server/mcp-browser.ts +95 -0
- package/server/memory.ts +2 -2
- package/server/omp-config.ts +16 -7
- package/server/omp-plugins.ts +70 -21
- package/server/omp-settings.ts +232 -24
- package/server/paths.ts +128 -11
- package/server/provider/catalog.ts +3 -4
- package/server/provider/connection.ts +248 -17
- package/server/provider/host-tools.ts +294 -26
- package/server/provider/omp-rpc.ts +498 -72
- package/server/provider/profile-providers.ts +249 -0
- package/server/provider/registration.ts +11 -0
- package/server/provider/security.ts +8 -10
- package/server/provider/session-descriptors.ts +306 -1
- package/server/provider/session.ts +704 -249
- package/server/provider/subsessions.ts +25 -2
- package/server/provider/timeline-projector.ts +104 -44
- package/server/provider-diagnostics.ts +122 -36
- package/server/quota.ts +3 -2
- package/server/sessions.ts +2 -2
- package/shared/composer-pill-settings.ts +28 -0
- package/shared/external-url.ts +21 -0
- package/shared/hub.ts +3 -3
- package/shared/mcp.ts +47 -0
- package/shared/memory.ts +2 -1
- package/shared/omp-config.ts +5 -1
- package/shared/omp-plugins.ts +74 -33
- package/shared/omp-settings.ts +8 -1
- package/shared/omp-store.ts +58 -0
- package/shared/provider-diagnostics.ts +12 -3
- package/shared/quota.ts +2 -1
- package/shared/sessions.ts +2 -1
|
@@ -94,6 +94,18 @@ const TaskResultDetailsSchema = z.object({
|
|
|
94
94
|
exitCode: z.number().optional(),
|
|
95
95
|
error: z.unknown().optional(),
|
|
96
96
|
aborted: z.boolean().optional(),
|
|
97
|
+
status: z
|
|
98
|
+
.enum([
|
|
99
|
+
"pending",
|
|
100
|
+
"running",
|
|
101
|
+
"completed",
|
|
102
|
+
"failed",
|
|
103
|
+
"error",
|
|
104
|
+
"aborted",
|
|
105
|
+
"canceled",
|
|
106
|
+
"cancelled",
|
|
107
|
+
])
|
|
108
|
+
.optional(),
|
|
97
109
|
}),
|
|
98
110
|
)
|
|
99
111
|
.max(MAX_CHILDREN),
|
|
@@ -192,8 +204,15 @@ function replayChildren(messages: readonly OmpMessage[]): ReplayChildRef[] {
|
|
|
192
204
|
const details = taskResultDetails(message);
|
|
193
205
|
const results = details?.results ?? [];
|
|
194
206
|
for (const result of results) {
|
|
207
|
+
const canceled =
|
|
208
|
+
result.aborted === true ||
|
|
209
|
+
result.status === "aborted" ||
|
|
210
|
+
result.status === "canceled" ||
|
|
211
|
+
result.status === "cancelled";
|
|
195
212
|
const failed =
|
|
196
213
|
message.isError === true ||
|
|
214
|
+
result.status === "failed" ||
|
|
215
|
+
result.status === "error" ||
|
|
197
216
|
Boolean(result.error) ||
|
|
198
217
|
(typeof result.exitCode === "number" && result.exitCode !== 0);
|
|
199
218
|
children.push({
|
|
@@ -201,7 +220,7 @@ function replayChildren(messages: readonly OmpMessage[]): ReplayChildRef[] {
|
|
|
201
220
|
agent: result.agent ?? call?.title,
|
|
202
221
|
description: call?.description,
|
|
203
222
|
parentToolCallId: message.toolCallId,
|
|
204
|
-
status:
|
|
223
|
+
status: canceled ? "canceled" : failed ? "failed" : "completed",
|
|
205
224
|
});
|
|
206
225
|
}
|
|
207
226
|
const resultIds = new Set(results.map((result) => result.id));
|
|
@@ -304,6 +323,7 @@ export class OmpSubsessionProjector {
|
|
|
304
323
|
private readonly scheduler: OmpTimelineScheduler,
|
|
305
324
|
private readonly onActivityChange: () => void,
|
|
306
325
|
private readonly outputRedactionValues: readonly string[],
|
|
326
|
+
private readonly pluginTimelineEnabled = false,
|
|
307
327
|
) {
|
|
308
328
|
this.dataFilter = new OmpPublicDataSerializer(outputRedactionValues);
|
|
309
329
|
}
|
|
@@ -540,6 +560,8 @@ export class OmpSubsessionProjector {
|
|
|
540
560
|
this.emit,
|
|
541
561
|
this.scheduler,
|
|
542
562
|
this.outputRedactionValues,
|
|
563
|
+
false,
|
|
564
|
+
this.pluginTimelineEnabled,
|
|
543
565
|
),
|
|
544
566
|
};
|
|
545
567
|
this.children.set(sessionId, child);
|
|
@@ -552,7 +574,8 @@ export class OmpSubsessionProjector {
|
|
|
552
574
|
type: "session.opened",
|
|
553
575
|
sessionId,
|
|
554
576
|
parentSessionId,
|
|
555
|
-
|
|
577
|
+
toolCallId: ref.parentToolCallId,
|
|
578
|
+
capabilities: ["session.subsession"],
|
|
556
579
|
restoration: "parent",
|
|
557
580
|
cwd: this.cwd,
|
|
558
581
|
title,
|
|
@@ -4,19 +4,22 @@ import type {
|
|
|
4
4
|
ProviderTimelineItem,
|
|
5
5
|
ProviderToolCallDetail,
|
|
6
6
|
} from "@getpaseo/plugin/server/provider";
|
|
7
|
+
import { OMP_MCP_AUTH_TIMELINE_KIND } from "../../shared/mcp";
|
|
7
8
|
import { isOmpImageMimeType, isValidImagePayload, type OmpImageMimeType } from "./image";
|
|
8
|
-
import type
|
|
9
|
+
import { OMP_MAX_CONTENT_PARTS, type OmpMessage, type OmpRpcEvent } from "./omp-rpc";
|
|
9
10
|
import {
|
|
10
11
|
boundedJsonBytes,
|
|
11
12
|
type JsonValue,
|
|
12
13
|
OmpPublicDataSerializer,
|
|
13
14
|
OmpPublicError,
|
|
15
|
+
truncateUtf8,
|
|
14
16
|
utf8Bytes,
|
|
15
17
|
} from "./security";
|
|
16
18
|
|
|
17
19
|
const STREAM_FRAME_MS = 32;
|
|
18
|
-
const MAX_STREAM_CONTENT_BLOCKS = 64;
|
|
19
20
|
const MAX_STREAM_TEXT_LENGTH = 4 * 1024 * 1024;
|
|
21
|
+
const MAX_RAW_STREAM_TEXT_LENGTH = 8 * 1024 * 1024;
|
|
22
|
+
const MAX_REDACTED_STREAM_TEXT_LENGTH = MAX_RAW_STREAM_TEXT_LENGTH * 3;
|
|
20
23
|
const MAX_ACTIVE_TOOLS = 64;
|
|
21
24
|
const MAX_TODOS = 256;
|
|
22
25
|
const MAX_TURN_NATIVE_IDENTITIES = 1_024;
|
|
@@ -54,6 +57,7 @@ type StreamSnapshot = {
|
|
|
54
57
|
nativeIdentity?: string;
|
|
55
58
|
published: boolean;
|
|
56
59
|
retainedBytes: number;
|
|
60
|
+
textBytes: number;
|
|
57
61
|
publishedBytes: number;
|
|
58
62
|
blocks: Map<number, StreamBlockSnapshot>;
|
|
59
63
|
dirtyBlocks: Set<number>;
|
|
@@ -196,6 +200,17 @@ function publishableHttpUrl(value: string | undefined): string | undefined {
|
|
|
196
200
|
return undefined;
|
|
197
201
|
}
|
|
198
202
|
}
|
|
203
|
+
function isOmpOAuthLaunchUrl(value: string | undefined): boolean {
|
|
204
|
+
const url = publishableHttpUrl(value);
|
|
205
|
+
if (!url) return false;
|
|
206
|
+
const parsed = new URL(url);
|
|
207
|
+
return (
|
|
208
|
+
(parsed.hostname === "localhost" ||
|
|
209
|
+
parsed.hostname === "127.0.0.1" ||
|
|
210
|
+
parsed.hostname === "[::1]") &&
|
|
211
|
+
parsed.pathname === "/launch"
|
|
212
|
+
);
|
|
213
|
+
}
|
|
199
214
|
|
|
200
215
|
function todoPublicId(nativeId: string | undefined, index: number): string {
|
|
201
216
|
if (!nativeId) return `omp:todo:${index}`;
|
|
@@ -240,7 +255,7 @@ function nativeImageResult(
|
|
|
240
255
|
boundedJsonBytes(
|
|
241
256
|
value,
|
|
242
257
|
MAX_NATIVE_IMAGE_RESULT_BYTES,
|
|
243
|
-
|
|
258
|
+
OMP_MAX_CONTENT_PARTS,
|
|
244
259
|
8 * 1024 * 1024,
|
|
245
260
|
512,
|
|
246
261
|
) === Number.POSITIVE_INFINITY
|
|
@@ -350,6 +365,7 @@ export class OmpTimelineProjector {
|
|
|
350
365
|
private readonly replayCandidates = new Map<string, ReplayCandidate>();
|
|
351
366
|
private readonly replayOverflowCandidates = new Map<string, string>();
|
|
352
367
|
private projectingReplay = false;
|
|
368
|
+
private readonly replayToolCalls = new Map<string, { toolName: string; args: unknown }>();
|
|
353
369
|
private customSequence = 0;
|
|
354
370
|
private compactionSequence = 0;
|
|
355
371
|
private activeCompaction: CompactionSlot | null = null;
|
|
@@ -366,6 +382,7 @@ export class OmpTimelineProjector {
|
|
|
366
382
|
private closed = false;
|
|
367
383
|
|
|
368
384
|
private readonly dataFilter: OmpPublicDataSerializer;
|
|
385
|
+
private browserAuthorizationIssuer: ((url: string) => string | undefined) | null = null;
|
|
369
386
|
|
|
370
387
|
constructor(
|
|
371
388
|
private readonly sessionId: string,
|
|
@@ -373,10 +390,14 @@ export class OmpTimelineProjector {
|
|
|
373
390
|
private readonly scheduler: OmpTimelineScheduler = defaultOmpTimelineScheduler,
|
|
374
391
|
outputRedactionValues: readonly string[] = [],
|
|
375
392
|
private readonly conversationRevertEnabled = false,
|
|
393
|
+
private readonly pluginTimelineEnabled = false,
|
|
376
394
|
private readonly hostToolLabels: ReadonlyMap<string, string> = new Map(),
|
|
377
395
|
) {
|
|
378
396
|
this.dataFilter = new OmpPublicDataSerializer(outputRedactionValues);
|
|
379
397
|
}
|
|
398
|
+
setBrowserAuthorizationIssuer(issue: ((url: string) => string | undefined) | null): void {
|
|
399
|
+
this.browserAuthorizationIssuer = issue;
|
|
400
|
+
}
|
|
380
401
|
|
|
381
402
|
project(event: OmpRpcEvent, turnId: string, bypassReplayFilter = false): void {
|
|
382
403
|
if (this.closed) return;
|
|
@@ -601,15 +622,40 @@ export class OmpTimelineProjector {
|
|
|
601
622
|
return;
|
|
602
623
|
}
|
|
603
624
|
if (event.type === "extension_ui_request" && event.method === "open_url") {
|
|
604
|
-
const url = publishableHttpUrl(event.
|
|
625
|
+
const url = publishableHttpUrl(event.url);
|
|
605
626
|
if (!url) return;
|
|
606
627
|
this.noticeSequence += 1;
|
|
607
|
-
const
|
|
628
|
+
const instructions = event.instructions
|
|
629
|
+
? this.dataFilter.text(event.instructions, 64 * 1024)
|
|
630
|
+
: undefined;
|
|
631
|
+
const publicUrl = this.dataFilter.text(url, 16_384);
|
|
632
|
+
const isMcpAuthorization = isOmpOAuthLaunchUrl(event.launchUrl);
|
|
633
|
+
if (this.pluginTimelineEnabled && isMcpAuthorization && publishableHttpUrl(publicUrl)) {
|
|
634
|
+
const browserAuthorizationToken = this.browserAuthorizationIssuer?.(publicUrl);
|
|
635
|
+
this.publish({
|
|
636
|
+
type: "plugin",
|
|
637
|
+
id: `omp:ui:${this.noticeSequence}`,
|
|
638
|
+
pluginId: "paseo-omp",
|
|
639
|
+
kind: OMP_MCP_AUTH_TIMELINE_KIND,
|
|
640
|
+
version: 1,
|
|
641
|
+
data: {
|
|
642
|
+
url: publicUrl,
|
|
643
|
+
...(instructions ? { instructions } : {}),
|
|
644
|
+
loopbackCallback: true,
|
|
645
|
+
...(browserAuthorizationToken ? { browserAuthorizationToken } : {}),
|
|
646
|
+
},
|
|
647
|
+
});
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const remoteHint = isMcpAuthorization
|
|
651
|
+
? "Remote client: if the localhost callback cannot connect, paste the final redirect URL or authorization code into the OMP prompt in this chat."
|
|
652
|
+
: undefined;
|
|
653
|
+
const message = [instructions, publicUrl, remoteHint].filter(Boolean).join("\n");
|
|
608
654
|
this.publish({
|
|
609
655
|
type: "notification",
|
|
610
656
|
id: `omp:ui:${this.noticeSequence}`,
|
|
611
657
|
level: "info",
|
|
612
|
-
message
|
|
658
|
+
message,
|
|
613
659
|
});
|
|
614
660
|
return;
|
|
615
661
|
}
|
|
@@ -783,6 +829,7 @@ export class OmpTimelineProjector {
|
|
|
783
829
|
this.replayOverflowCandidates.clear();
|
|
784
830
|
this.projectingReplay = false;
|
|
785
831
|
this.activeToolBytes = 0;
|
|
832
|
+
this.replayToolCalls.clear();
|
|
786
833
|
this.tools.clear();
|
|
787
834
|
this.commandText = "";
|
|
788
835
|
this.commandPublishedText = "";
|
|
@@ -795,6 +842,7 @@ export class OmpTimelineProjector {
|
|
|
795
842
|
const nativeIdentity = assistantIdentity(message);
|
|
796
843
|
this.replaySequence += 1;
|
|
797
844
|
if (message.role === "user") {
|
|
845
|
+
this.replayToolCalls.clear();
|
|
798
846
|
if (this.replayTurnId) this.finishTurn(this.replayTurnId);
|
|
799
847
|
this.replayTurnId = `omp:replay-turn:${this.replaySequence}`;
|
|
800
848
|
const text =
|
|
@@ -823,15 +871,7 @@ export class OmpTimelineProjector {
|
|
|
823
871
|
for (const part of message.content) {
|
|
824
872
|
if (part.type !== "toolCall" || !part.id || !part.name || part.arguments === undefined)
|
|
825
873
|
continue;
|
|
826
|
-
this.
|
|
827
|
-
{
|
|
828
|
-
type: "tool_execution_start",
|
|
829
|
-
toolCallId: part.id,
|
|
830
|
-
toolName: part.name,
|
|
831
|
-
args: part.arguments,
|
|
832
|
-
},
|
|
833
|
-
this.replayTurnId,
|
|
834
|
-
);
|
|
874
|
+
this.replayToolCalls.set(part.id, { toolName: part.name, args: part.arguments });
|
|
835
875
|
}
|
|
836
876
|
}
|
|
837
877
|
} finally {
|
|
@@ -842,13 +882,15 @@ export class OmpTimelineProjector {
|
|
|
842
882
|
}
|
|
843
883
|
this.replayTurnId ??= `omp:replay-turn:${this.replaySequence}`;
|
|
844
884
|
if (message.role === "toolResult") {
|
|
885
|
+
const replayCall = this.replayToolCalls.get(message.toolCallId);
|
|
886
|
+
this.replayToolCalls.delete(message.toolCallId);
|
|
845
887
|
if (!this.tools.has(message.toolCallId)) {
|
|
846
888
|
this.project(
|
|
847
889
|
{
|
|
848
890
|
type: "tool_execution_start",
|
|
849
891
|
toolCallId: message.toolCallId,
|
|
850
|
-
toolName: message.toolName,
|
|
851
|
-
args: null,
|
|
892
|
+
toolName: replayCall?.toolName ?? message.toolName,
|
|
893
|
+
args: replayCall?.args ?? null,
|
|
852
894
|
},
|
|
853
895
|
this.replayTurnId,
|
|
854
896
|
);
|
|
@@ -869,10 +911,7 @@ export class OmpTimelineProjector {
|
|
|
869
911
|
return;
|
|
870
912
|
}
|
|
871
913
|
if (message.role === "bashExecution") {
|
|
872
|
-
|
|
873
|
-
? `$ ${message.command}\n${message.output}`
|
|
874
|
-
: `$ ${message.command}`;
|
|
875
|
-
this.project({ type: "command_output", text }, this.replayTurnId);
|
|
914
|
+
this.publishCustomMessage(message);
|
|
876
915
|
this.finishTurn(this.replayTurnId);
|
|
877
916
|
this.replayTurnId = null;
|
|
878
917
|
}
|
|
@@ -880,6 +919,7 @@ export class OmpTimelineProjector {
|
|
|
880
919
|
|
|
881
920
|
finishReplay(): void {
|
|
882
921
|
if (this.replayTurnId) this.finishTurn(this.replayTurnId);
|
|
922
|
+
this.replayToolCalls.clear();
|
|
883
923
|
this.replayTurnId = null;
|
|
884
924
|
}
|
|
885
925
|
acceptLiveTurn(turnId: string): void {
|
|
@@ -967,13 +1007,28 @@ export class OmpTimelineProjector {
|
|
|
967
1007
|
if (!this.stream || this.closed || this.stream.dirtyBlocks.size === 0) return;
|
|
968
1008
|
const stream = this.stream;
|
|
969
1009
|
if (!stream.nativeIdentity && !finalizeFallback) return;
|
|
970
|
-
const indexes = [...stream.
|
|
1010
|
+
const indexes = [...stream.blocks.keys()].sort((left, right) => left - right);
|
|
1011
|
+
let remainingTextBytes = MAX_STREAM_TEXT_LENGTH;
|
|
971
1012
|
stream.dirtyBlocks.clear();
|
|
972
1013
|
for (const contentIndex of indexes) {
|
|
973
1014
|
const block = stream.blocks.get(contentIndex);
|
|
974
|
-
if (!block
|
|
975
|
-
const publicText =
|
|
976
|
-
|
|
1015
|
+
if (!block) continue;
|
|
1016
|
+
const publicText =
|
|
1017
|
+
block.kind === "image"
|
|
1018
|
+
? block.text
|
|
1019
|
+
: truncateUtf8(
|
|
1020
|
+
this.dataFilter.text(block.text, MAX_REDACTED_STREAM_TEXT_LENGTH),
|
|
1021
|
+
remainingTextBytes,
|
|
1022
|
+
);
|
|
1023
|
+
if (block.kind !== "image") {
|
|
1024
|
+
remainingTextBytes = Math.max(0, remainingTextBytes - utf8Bytes(publicText));
|
|
1025
|
+
}
|
|
1026
|
+
if (
|
|
1027
|
+
block.publishedText === publicText ||
|
|
1028
|
+
(!publicText && block.publishedText === undefined)
|
|
1029
|
+
) {
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
977
1032
|
const nextPublishedBytes = utf8Bytes(publicText);
|
|
978
1033
|
if (
|
|
979
1034
|
stream.retainedBytes + stream.publishedBytes + nextPublishedBytes >
|
|
@@ -1027,9 +1082,11 @@ export class OmpTimelineProjector {
|
|
|
1027
1082
|
if (this.currentTurnId) this.publishCommand(this.currentTurnId);
|
|
1028
1083
|
this.retireCompactions("OMP compaction ended when the session closed");
|
|
1029
1084
|
this.closed = true;
|
|
1085
|
+
this.browserAuthorizationIssuer = null;
|
|
1030
1086
|
this.clearFlushTimer();
|
|
1031
1087
|
this.stream = null;
|
|
1032
1088
|
this.tools.clear();
|
|
1089
|
+
this.replayToolCalls.clear();
|
|
1033
1090
|
this.activeToolBytes = 0;
|
|
1034
1091
|
this.replayCandidates.clear();
|
|
1035
1092
|
this.replayOverflowCandidates.clear();
|
|
@@ -1091,6 +1148,7 @@ export class OmpTimelineProjector {
|
|
|
1091
1148
|
...(nativeIdentity ? { nativeIdentity } : {}),
|
|
1092
1149
|
published: false,
|
|
1093
1150
|
retainedBytes: 0,
|
|
1151
|
+
textBytes: 0,
|
|
1094
1152
|
publishedBytes: 0,
|
|
1095
1153
|
blocks: new Map(),
|
|
1096
1154
|
dirtyBlocks: new Set(),
|
|
@@ -1155,7 +1213,7 @@ export class OmpTimelineProjector {
|
|
|
1155
1213
|
return;
|
|
1156
1214
|
}
|
|
1157
1215
|
if (!Array.isArray(message.content)) return;
|
|
1158
|
-
const blockCount = Math.min(message.content.length,
|
|
1216
|
+
const blockCount = Math.min(message.content.length, OMP_MAX_CONTENT_PARTS);
|
|
1159
1217
|
for (let index = 0; index < blockCount; index += 1) {
|
|
1160
1218
|
const block = blockText(message, index);
|
|
1161
1219
|
if (block) this.setBlock(stream, index, block);
|
|
@@ -1215,28 +1273,22 @@ export class OmpTimelineProjector {
|
|
|
1215
1273
|
if (snapshot.kind === "image" && utf8Bytes(snapshot.text) > MAX_IMAGE_ENCODED_LENGTH + 256)
|
|
1216
1274
|
return;
|
|
1217
1275
|
const previous = stream.blocks.get(contentIndex);
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
if (
|
|
1226
|
-
retainedBytes + stream.publishedBytes > MAX_STREAM_TOTAL_BYTES ||
|
|
1227
|
-
textBytes > MAX_STREAM_TEXT_LENGTH
|
|
1228
|
-
) {
|
|
1229
|
-
return;
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1276
|
+
const previousBytes = previous ? utf8Bytes(previous.text) : 0;
|
|
1277
|
+
const snapshotBytes = utf8Bytes(snapshot.text);
|
|
1278
|
+
const retainedBytes = stream.retainedBytes - previousBytes + snapshotBytes;
|
|
1279
|
+
const textBytes =
|
|
1280
|
+
stream.textBytes -
|
|
1281
|
+
(previous && previous.kind !== "image" ? previousBytes : 0) +
|
|
1282
|
+
(snapshot.kind === "image" ? 0 : snapshotBytes);
|
|
1232
1283
|
if (
|
|
1233
1284
|
retainedBytes + stream.publishedBytes > MAX_STREAM_TOTAL_BYTES ||
|
|
1234
|
-
textBytes >
|
|
1285
|
+
textBytes > MAX_RAW_STREAM_TEXT_LENGTH
|
|
1235
1286
|
) {
|
|
1236
1287
|
return;
|
|
1237
1288
|
}
|
|
1238
1289
|
if (previous?.kind === snapshot.kind && previous.text === snapshot.text) return;
|
|
1239
1290
|
stream.retainedBytes = retainedBytes;
|
|
1291
|
+
stream.textBytes = textBytes;
|
|
1240
1292
|
stream.blocks.set(contentIndex, {
|
|
1241
1293
|
...snapshot,
|
|
1242
1294
|
...(previous?.kind === snapshot.kind ? { publishedText: previous.publishedText } : {}),
|
|
@@ -1248,13 +1300,13 @@ export class OmpTimelineProjector {
|
|
|
1248
1300
|
return (
|
|
1249
1301
|
Number.isSafeInteger(contentIndex) &&
|
|
1250
1302
|
contentIndex >= 0 &&
|
|
1251
|
-
contentIndex <
|
|
1303
|
+
contentIndex < OMP_MAX_CONTENT_PARTS
|
|
1252
1304
|
);
|
|
1253
1305
|
}
|
|
1254
1306
|
|
|
1255
1307
|
private publishCommand(turnId: string): void {
|
|
1256
1308
|
if (!this.commandText) return;
|
|
1257
|
-
const publicText = this.dataFilter.text(this.commandText);
|
|
1309
|
+
const publicText = this.dataFilter.text(this.commandText, MAX_STREAM_TEXT_LENGTH);
|
|
1258
1310
|
if (!publicText || publicText === this.commandPublishedText) return;
|
|
1259
1311
|
this.commandPublishedText = publicText;
|
|
1260
1312
|
this.publish({
|
|
@@ -1340,13 +1392,21 @@ export class OmpTimelineProjector {
|
|
|
1340
1392
|
type: "shell",
|
|
1341
1393
|
command,
|
|
1342
1394
|
...(firstString(details, "cwd") ? { cwd: firstString(details, "cwd") } : {}),
|
|
1343
|
-
...(output ? { output: this.dataFilter.text(output) } : {}),
|
|
1395
|
+
...(output ? { output: this.dataFilter.text(output, MAX_STREAM_TEXT_LENGTH) } : {}),
|
|
1344
1396
|
...(typeof message.exitCode === "number" || message.exitCode === null
|
|
1345
1397
|
? { exitCode: message.exitCode }
|
|
1346
1398
|
: typeof details?.exitCode === "number"
|
|
1347
1399
|
? { exitCode: details.exitCode }
|
|
1348
1400
|
: {}),
|
|
1349
1401
|
},
|
|
1402
|
+
...(message.cancelled !== undefined || message.truncated !== undefined
|
|
1403
|
+
? {
|
|
1404
|
+
metadata: {
|
|
1405
|
+
...(message.cancelled !== undefined ? { cancelled: message.cancelled } : {}),
|
|
1406
|
+
...(message.truncated !== undefined ? { truncated: message.truncated } : {}),
|
|
1407
|
+
},
|
|
1408
|
+
}
|
|
1409
|
+
: {}),
|
|
1350
1410
|
status: message.cancelled ? "canceled" : "completed",
|
|
1351
1411
|
error: null,
|
|
1352
1412
|
});
|