@omercnet/paseo-omp 0.3.0 → 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/README.md +19 -5
- package/client/omp-config-surface.tsx +243 -24
- package/client/omp-config-views.ts +24 -0
- package/client/omp-model-picker-state.ts +145 -0
- package/client/omp-model-picker.tsx +282 -0
- package/client/omp-routing-editor.tsx +307 -0
- package/client/support-diagnostics-state.ts +45 -0
- package/index.server.ts +27 -2
- package/package.json +4 -8
- package/paseo-plugin.json +1 -1
- package/server/omp-models.ts +59 -0
- package/server/omp-settings.ts +30 -20
- package/server/operational-failure-diagnostics.ts +76 -0
- package/server/package-version.ts +2 -0
- package/server/protocol-violation-diagnostics.ts +169 -0
- package/server/provider/catalog.ts +39 -10
- package/server/provider/connection.ts +88 -9
- package/server/provider/host-tools.ts +284 -34
- package/server/provider/mcp-transport.ts +2 -1
- package/server/provider/omp-rpc.ts +1785 -267
- package/server/provider/profile-providers.ts +7 -2
- package/server/provider/registration.ts +12 -2
- package/server/provider/security.ts +8 -10
- package/server/provider/session-descriptors.ts +97 -29
- package/server/provider/session.ts +212 -59
- package/server/provider/subsessions.ts +334 -74
- package/server/provider/timeline-projector.ts +125 -32
- package/server/support-diagnostics.ts +284 -0
- package/shared/omp-models.ts +49 -0
- package/shared/omp-settings.ts +227 -3
- package/shared/support-diagnostics.ts +32 -0
- package/CHANGELOG.md +0 -113
- package/SUPPORT.md +0 -44
- package/TESTING.md +0 -150
- package/docs/alpha-release-checklist.md +0 -68
- package/docs/configuration.md +0 -126
- package/docs/core-provider-issue-audit.md +0 -109
- 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 +0 -89
- package/tsconfig.json +0 -16
|
@@ -6,17 +6,25 @@ import type {
|
|
|
6
6
|
} from "@getpaseo/plugin/server/provider";
|
|
7
7
|
import { OMP_MCP_AUTH_TIMELINE_KIND } from "../../shared/mcp";
|
|
8
8
|
import { isOmpImageMimeType, isValidImagePayload, type OmpImageMimeType } from "./image";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
OMP_MAX_CONTENT_PARTS,
|
|
11
|
+
OMP_MESSAGE_REPLAY_POLICIES,
|
|
12
|
+
type OmpMessage,
|
|
13
|
+
type OmpRpcEvent,
|
|
14
|
+
} from "./omp-rpc";
|
|
10
15
|
import {
|
|
11
16
|
boundedJsonBytes,
|
|
12
17
|
type JsonValue,
|
|
13
18
|
OmpPublicDataSerializer,
|
|
14
19
|
OmpPublicError,
|
|
20
|
+
truncateUtf8,
|
|
15
21
|
utf8Bytes,
|
|
16
22
|
} from "./security";
|
|
17
23
|
|
|
18
24
|
const STREAM_FRAME_MS = 32;
|
|
19
25
|
const MAX_STREAM_TEXT_LENGTH = 4 * 1024 * 1024;
|
|
26
|
+
const MAX_RAW_STREAM_TEXT_LENGTH = 8 * 1024 * 1024;
|
|
27
|
+
const MAX_REDACTED_STREAM_TEXT_LENGTH = MAX_RAW_STREAM_TEXT_LENGTH * 3;
|
|
20
28
|
const MAX_ACTIVE_TOOLS = 64;
|
|
21
29
|
const MAX_TODOS = 256;
|
|
22
30
|
const MAX_TURN_NATIVE_IDENTITIES = 1_024;
|
|
@@ -131,6 +139,10 @@ function imageBlock(data: string, mimeType: string): StreamBlockSnapshot | undef
|
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
type OmpAssistantMessage = Extract<OmpMessage, { role: "assistant" }>;
|
|
142
|
+
type OmpCustomDisplayMessage = Extract<
|
|
143
|
+
OmpMessage,
|
|
144
|
+
{ role: "bashExecution" | "pythonExecution" | "custom" | "hookMessage" }
|
|
145
|
+
>;
|
|
134
146
|
|
|
135
147
|
function blockText(
|
|
136
148
|
message: OmpAssistantMessage,
|
|
@@ -414,16 +426,35 @@ export class OmpTimelineProjector {
|
|
|
414
426
|
return;
|
|
415
427
|
}
|
|
416
428
|
}
|
|
429
|
+
if (
|
|
430
|
+
(event.type === "message_start" ||
|
|
431
|
+
event.type === "message_update" ||
|
|
432
|
+
event.type === "message_end") &&
|
|
433
|
+
OMP_MESSAGE_REPLAY_POLICIES[event.message.role] === "ignore"
|
|
434
|
+
) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
417
437
|
if (
|
|
418
438
|
event.type === "todo_reminder" ||
|
|
419
439
|
event.type === "todo_auto_clear" ||
|
|
440
|
+
event.type === "goal_updated" ||
|
|
441
|
+
event.type === "auto_retry_start" ||
|
|
442
|
+
event.type === "auto_retry_end" ||
|
|
443
|
+
event.type === "retry_fallback_applied" ||
|
|
444
|
+
event.type === "retry_fallback_succeeded" ||
|
|
420
445
|
event.type === "notice" ||
|
|
421
446
|
event.type === "extension_ui_request" ||
|
|
422
447
|
event.type === "auto_compaction_start" ||
|
|
423
448
|
event.type === "auto_compaction_end" ||
|
|
424
449
|
event.type === "compaction_start" ||
|
|
425
450
|
event.type === "compaction_end" ||
|
|
426
|
-
event.type === "advisor_yielded"
|
|
451
|
+
event.type === "advisor_yielded" ||
|
|
452
|
+
event.type === "config_warnings_changed" ||
|
|
453
|
+
event.type === "advisor_cost_changed" ||
|
|
454
|
+
event.type === "ttsr_triggered" ||
|
|
455
|
+
event.type === "irc_message" ||
|
|
456
|
+
event.type === "model_changed" ||
|
|
457
|
+
event.type === "thinking_level_changed"
|
|
427
458
|
) {
|
|
428
459
|
this.projectPassive(event);
|
|
429
460
|
return;
|
|
@@ -446,7 +477,12 @@ export class OmpTimelineProjector {
|
|
|
446
477
|
this.scheduleFlush();
|
|
447
478
|
return;
|
|
448
479
|
case "message_end":
|
|
449
|
-
if (
|
|
480
|
+
if (
|
|
481
|
+
event.message.role === "custom" ||
|
|
482
|
+
event.message.role === "hookMessage" ||
|
|
483
|
+
event.message.role === "bashExecution" ||
|
|
484
|
+
event.message.role === "pythonExecution"
|
|
485
|
+
) {
|
|
450
486
|
this.publishCustomMessage(event.message);
|
|
451
487
|
return;
|
|
452
488
|
}
|
|
@@ -496,12 +532,13 @@ export class OmpTimelineProjector {
|
|
|
496
532
|
if (!snapshot.silent) this.publishTool(snapshot, "running");
|
|
497
533
|
return;
|
|
498
534
|
}
|
|
499
|
-
case "tool_execution_update":
|
|
535
|
+
case "tool_execution_update":
|
|
536
|
+
case "tool_stream_update": {
|
|
500
537
|
const previous = this.tools.get(event.toolCallId);
|
|
501
538
|
if (!previous) return;
|
|
502
539
|
if (previous.turnId !== turnId || previous.generation !== this.runtimeGeneration) return;
|
|
503
540
|
const output = this.dataFilter.json(
|
|
504
|
-
event.partialResult,
|
|
541
|
+
event.type === "tool_execution_update" ? event.partialResult : event.update,
|
|
505
542
|
MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
|
|
506
543
|
MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
|
|
507
544
|
);
|
|
@@ -597,6 +634,10 @@ export class OmpTimelineProjector {
|
|
|
597
634
|
this.publishRetryFallback(event);
|
|
598
635
|
return;
|
|
599
636
|
}
|
|
637
|
+
if (event.type === "irc_message") {
|
|
638
|
+
this.publishCustomMessage(event.message as OmpCustomDisplayMessage);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
600
641
|
if (event.type === "notice") {
|
|
601
642
|
this.noticeSequence += 1;
|
|
602
643
|
this.publish({
|
|
@@ -836,8 +877,9 @@ export class OmpTimelineProjector {
|
|
|
836
877
|
|
|
837
878
|
projectReplayMessage(message: OmpMessage): void {
|
|
838
879
|
if (this.closed) return;
|
|
839
|
-
const nativeIdentity = assistantIdentity(message);
|
|
840
880
|
this.replaySequence += 1;
|
|
881
|
+
if (OMP_MESSAGE_REPLAY_POLICIES[message.role] === "ignore") return;
|
|
882
|
+
|
|
841
883
|
if (message.role === "user") {
|
|
842
884
|
this.replayToolCalls.clear();
|
|
843
885
|
if (this.replayTurnId) this.finishTurn(this.replayTurnId);
|
|
@@ -858,7 +900,9 @@ export class OmpTimelineProjector {
|
|
|
858
900
|
}
|
|
859
901
|
return;
|
|
860
902
|
}
|
|
903
|
+
|
|
861
904
|
if (message.role === "assistant") {
|
|
905
|
+
const nativeIdentity = assistantIdentity(message);
|
|
862
906
|
this.replayTurnId ??= `omp:replay-turn:${this.replaySequence}`;
|
|
863
907
|
this.projectingReplay = true;
|
|
864
908
|
try {
|
|
@@ -877,6 +921,7 @@ export class OmpTimelineProjector {
|
|
|
877
921
|
if (nativeIdentity) this.rememberReplayOccurrence(nativeIdentity, message);
|
|
878
922
|
return;
|
|
879
923
|
}
|
|
924
|
+
|
|
880
925
|
this.replayTurnId ??= `omp:replay-turn:${this.replaySequence}`;
|
|
881
926
|
if (message.role === "toolResult") {
|
|
882
927
|
const replayCall = this.replayToolCalls.get(message.toolCallId);
|
|
@@ -907,13 +952,18 @@ export class OmpTimelineProjector {
|
|
|
907
952
|
);
|
|
908
953
|
return;
|
|
909
954
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
955
|
+
|
|
956
|
+
if (
|
|
957
|
+
message.role === "bashExecution" ||
|
|
958
|
+
message.role === "pythonExecution" ||
|
|
959
|
+
message.role === "custom" ||
|
|
960
|
+
message.role === "hookMessage"
|
|
961
|
+
) {
|
|
962
|
+
this.publishCustomMessage(message);
|
|
963
|
+
if (message.role === "bashExecution" || message.role === "pythonExecution") {
|
|
964
|
+
this.finishTurn(this.replayTurnId);
|
|
965
|
+
this.replayTurnId = null;
|
|
966
|
+
}
|
|
917
967
|
}
|
|
918
968
|
}
|
|
919
969
|
|
|
@@ -1007,13 +1057,28 @@ export class OmpTimelineProjector {
|
|
|
1007
1057
|
if (!this.stream || this.closed || this.stream.dirtyBlocks.size === 0) return;
|
|
1008
1058
|
const stream = this.stream;
|
|
1009
1059
|
if (!stream.nativeIdentity && !finalizeFallback) return;
|
|
1010
|
-
const indexes = [...stream.
|
|
1060
|
+
const indexes = [...stream.blocks.keys()].sort((left, right) => left - right);
|
|
1061
|
+
let remainingTextBytes = MAX_STREAM_TEXT_LENGTH;
|
|
1011
1062
|
stream.dirtyBlocks.clear();
|
|
1012
1063
|
for (const contentIndex of indexes) {
|
|
1013
1064
|
const block = stream.blocks.get(contentIndex);
|
|
1014
|
-
if (!block
|
|
1015
|
-
const publicText =
|
|
1016
|
-
|
|
1065
|
+
if (!block) continue;
|
|
1066
|
+
const publicText =
|
|
1067
|
+
block.kind === "image"
|
|
1068
|
+
? block.text
|
|
1069
|
+
: truncateUtf8(
|
|
1070
|
+
this.dataFilter.text(block.text, MAX_REDACTED_STREAM_TEXT_LENGTH),
|
|
1071
|
+
remainingTextBytes,
|
|
1072
|
+
);
|
|
1073
|
+
if (block.kind !== "image") {
|
|
1074
|
+
remainingTextBytes = Math.max(0, remainingTextBytes - utf8Bytes(publicText));
|
|
1075
|
+
}
|
|
1076
|
+
if (
|
|
1077
|
+
block.publishedText === publicText ||
|
|
1078
|
+
(!publicText && block.publishedText === undefined)
|
|
1079
|
+
) {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1017
1082
|
const nextPublishedBytes = utf8Bytes(publicText);
|
|
1018
1083
|
if (
|
|
1019
1084
|
stream.retainedBytes + stream.publishedBytes + nextPublishedBytes >
|
|
@@ -1267,7 +1332,7 @@ export class OmpTimelineProjector {
|
|
|
1267
1332
|
(snapshot.kind === "image" ? 0 : snapshotBytes);
|
|
1268
1333
|
if (
|
|
1269
1334
|
retainedBytes + stream.publishedBytes > MAX_STREAM_TOTAL_BYTES ||
|
|
1270
|
-
textBytes >
|
|
1335
|
+
textBytes > MAX_RAW_STREAM_TEXT_LENGTH
|
|
1271
1336
|
) {
|
|
1272
1337
|
return;
|
|
1273
1338
|
}
|
|
@@ -1291,7 +1356,7 @@ export class OmpTimelineProjector {
|
|
|
1291
1356
|
|
|
1292
1357
|
private publishCommand(turnId: string): void {
|
|
1293
1358
|
if (!this.commandText) return;
|
|
1294
|
-
const publicText = this.dataFilter.text(this.commandText);
|
|
1359
|
+
const publicText = this.dataFilter.text(this.commandText, MAX_STREAM_TEXT_LENGTH);
|
|
1295
1360
|
if (!publicText || publicText === this.commandPublishedText) return;
|
|
1296
1361
|
this.commandPublishedText = publicText;
|
|
1297
1362
|
this.publish({
|
|
@@ -1335,9 +1400,17 @@ export class OmpTimelineProjector {
|
|
|
1335
1400
|
return undefined;
|
|
1336
1401
|
}
|
|
1337
1402
|
|
|
1338
|
-
private publishCustomMessage(message:
|
|
1339
|
-
if (
|
|
1340
|
-
|
|
1403
|
+
private publishCustomMessage(message: OmpCustomDisplayMessage): void {
|
|
1404
|
+
if (
|
|
1405
|
+
(message.role === "custom" || message.role === "hookMessage") &&
|
|
1406
|
+
message.display === false
|
|
1407
|
+
) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
const rawType =
|
|
1411
|
+
message.role === "custom" || message.role === "hookMessage"
|
|
1412
|
+
? (message.customType ?? message.role)
|
|
1413
|
+
: message.role;
|
|
1341
1414
|
const publicType = this.dataFilter.text(rawType, 256);
|
|
1342
1415
|
const lowerType = rawType.toLowerCase();
|
|
1343
1416
|
const details = jsonRecord(this.dataFilter.json(message.details ?? null));
|
|
@@ -1346,7 +1419,9 @@ export class OmpTimelineProjector {
|
|
|
1346
1419
|
const id = nativeIdentity
|
|
1347
1420
|
? `omp:custom:${createHash("sha256").update(nativeIdentity).digest("base64url").slice(0, 12)}`
|
|
1348
1421
|
: `omp:custom:${this.customSequence}`;
|
|
1349
|
-
const
|
|
1422
|
+
const messageContent =
|
|
1423
|
+
message.role === "custom" || message.role === "hookMessage" ? message.content : undefined;
|
|
1424
|
+
const contentParts = Array.isArray(messageContent) ? messageContent : [];
|
|
1350
1425
|
const imageResult = nativeImageResult(
|
|
1351
1426
|
{
|
|
1352
1427
|
content:
|
|
@@ -1358,16 +1433,26 @@ export class OmpTimelineProjector {
|
|
|
1358
1433
|
this.dataFilter,
|
|
1359
1434
|
);
|
|
1360
1435
|
const content =
|
|
1361
|
-
typeof
|
|
1362
|
-
?
|
|
1436
|
+
typeof messageContent === "string"
|
|
1437
|
+
? messageContent
|
|
1363
1438
|
: contentParts
|
|
1364
1439
|
.flatMap((part) => (part.type === "text" && part.text ? [part.text] : []))
|
|
1365
1440
|
.join("\n\n");
|
|
1366
|
-
|
|
1441
|
+
const execution = message.role === "bashExecution" || message.role === "pythonExecution";
|
|
1442
|
+
if (execution || /bash|shell|python/u.test(lowerType)) {
|
|
1443
|
+
const nativeCommand =
|
|
1444
|
+
message.role === "bashExecution"
|
|
1445
|
+
? message.command
|
|
1446
|
+
: message.role === "pythonExecution"
|
|
1447
|
+
? message.code
|
|
1448
|
+
: undefined;
|
|
1367
1449
|
const command = this.dataFilter.text(
|
|
1368
|
-
|
|
1450
|
+
nativeCommand ?? firstString(details, "command", "input") ?? publicType,
|
|
1369
1451
|
);
|
|
1370
|
-
const output = message.output ?? content;
|
|
1452
|
+
const output = execution ? (message.output ?? content) : content;
|
|
1453
|
+
const exitCode = execution ? message.exitCode : undefined;
|
|
1454
|
+
const cancelled = execution ? message.cancelled : undefined;
|
|
1455
|
+
const truncated = execution ? message.truncated : undefined;
|
|
1371
1456
|
this.publish({
|
|
1372
1457
|
type: "tool_call",
|
|
1373
1458
|
id,
|
|
@@ -1377,14 +1462,22 @@ export class OmpTimelineProjector {
|
|
|
1377
1462
|
type: "shell",
|
|
1378
1463
|
command,
|
|
1379
1464
|
...(firstString(details, "cwd") ? { cwd: firstString(details, "cwd") } : {}),
|
|
1380
|
-
...(output ? { output: this.dataFilter.text(output) } : {}),
|
|
1381
|
-
...(typeof
|
|
1382
|
-
? { exitCode
|
|
1465
|
+
...(output ? { output: this.dataFilter.text(output, MAX_STREAM_TEXT_LENGTH) } : {}),
|
|
1466
|
+
...(typeof exitCode === "number" || exitCode === null
|
|
1467
|
+
? { exitCode }
|
|
1383
1468
|
: typeof details?.exitCode === "number"
|
|
1384
1469
|
? { exitCode: details.exitCode }
|
|
1385
1470
|
: {}),
|
|
1386
1471
|
},
|
|
1387
|
-
|
|
1472
|
+
...(cancelled !== undefined || truncated !== undefined
|
|
1473
|
+
? {
|
|
1474
|
+
metadata: {
|
|
1475
|
+
...(cancelled !== undefined ? { cancelled } : {}),
|
|
1476
|
+
...(truncated !== undefined ? { truncated } : {}),
|
|
1477
|
+
},
|
|
1478
|
+
}
|
|
1479
|
+
: {}),
|
|
1480
|
+
status: cancelled ? "canceled" : "completed",
|
|
1388
1481
|
error: null,
|
|
1389
1482
|
});
|
|
1390
1483
|
if (imageResult) this.publishImages(id, publicType, imageResult.image);
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { arch, platform } from "node:os";
|
|
2
|
+
import type { RpcInput } from "@getpaseo/plugin";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import type { OmpProviderHealth, OmpVersion } from "../shared/provider-diagnostics";
|
|
5
|
+
import {
|
|
6
|
+
type getOmpSupportReport,
|
|
7
|
+
OMP_SUPPORT_REPORT_MAX_BYTES,
|
|
8
|
+
OMP_SUPPORT_REPORT_SCHEMA_VERSION,
|
|
9
|
+
supportReportByteLength,
|
|
10
|
+
} from "../shared/support-diagnostics";
|
|
11
|
+
import type {
|
|
12
|
+
OmpOperationalFailureCollector,
|
|
13
|
+
OmpOperationalFailureSummary,
|
|
14
|
+
} from "./operational-failure-diagnostics";
|
|
15
|
+
import { PASEO_OMP_PACKAGE_VERSION } from "./package-version";
|
|
16
|
+
import type {
|
|
17
|
+
OmpProtocolViolationCollector,
|
|
18
|
+
OmpProtocolViolationSummary,
|
|
19
|
+
} from "./protocol-violation-diagnostics";
|
|
20
|
+
import { resolveGetOmpProviderHealth } from "./provider-diagnostics";
|
|
21
|
+
|
|
22
|
+
const PACKAGE_VERSION = z
|
|
23
|
+
.string()
|
|
24
|
+
.regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]{1,48})?$/u)
|
|
25
|
+
.max(64);
|
|
26
|
+
const NODE_VERSION = z
|
|
27
|
+
.string()
|
|
28
|
+
.regex(/^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]{1,48})?$/u)
|
|
29
|
+
.max(64);
|
|
30
|
+
const KNOWN_PLATFORMS: Record<string, true> = {
|
|
31
|
+
aix: true,
|
|
32
|
+
android: true,
|
|
33
|
+
darwin: true,
|
|
34
|
+
freebsd: true,
|
|
35
|
+
linux: true,
|
|
36
|
+
openbsd: true,
|
|
37
|
+
sunos: true,
|
|
38
|
+
win32: true,
|
|
39
|
+
};
|
|
40
|
+
const KNOWN_ARCHITECTURES: Record<string, true> = {
|
|
41
|
+
arm: true,
|
|
42
|
+
arm64: true,
|
|
43
|
+
ia32: true,
|
|
44
|
+
loong64: true,
|
|
45
|
+
mips: true,
|
|
46
|
+
mipsel: true,
|
|
47
|
+
ppc: true,
|
|
48
|
+
ppc64: true,
|
|
49
|
+
riscv64: true,
|
|
50
|
+
s390: true,
|
|
51
|
+
s390x: true,
|
|
52
|
+
x64: true,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export interface OmpSupportReportData {
|
|
56
|
+
collectedAt: string;
|
|
57
|
+
pluginVersion: string;
|
|
58
|
+
platform: string;
|
|
59
|
+
architecture: string;
|
|
60
|
+
nodeVersion: string;
|
|
61
|
+
scope: "global" | "workspace";
|
|
62
|
+
store: "default" | "named-profile" | "custom-directory";
|
|
63
|
+
health: OmpProviderHealth | null;
|
|
64
|
+
violations: readonly OmpProtocolViolationSummary[];
|
|
65
|
+
operationalFailures: readonly OmpOperationalFailureSummary[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const EXPECTATION_LABELS: Record<
|
|
69
|
+
NonNullable<OmpProtocolViolationSummary["latestExpected"]>,
|
|
70
|
+
string
|
|
71
|
+
> = {
|
|
72
|
+
"complete-json-line": "newline-terminated JSON frame",
|
|
73
|
+
"within-byte-limit": "byte length within negotiated limit",
|
|
74
|
+
"valid-json": "valid UTF-8 JSON",
|
|
75
|
+
"object-envelope": "JSON object envelope",
|
|
76
|
+
"bounded-frame-type": "frame type string up to 64 bytes",
|
|
77
|
+
"valid-response-frame": "valid response envelope",
|
|
78
|
+
"valid-ready-frame": "valid ready handshake",
|
|
79
|
+
"single-ready-frame": "one ready handshake",
|
|
80
|
+
"valid-event-frame": "valid event payload",
|
|
81
|
+
"valid-event-state-transition": "event valid for current stream state",
|
|
82
|
+
"valid-message-event": "valid message event payload",
|
|
83
|
+
"valid-tool-event": "valid tool event payload",
|
|
84
|
+
"valid-lifecycle-event": "valid lifecycle event payload",
|
|
85
|
+
"valid-subagent-event": "valid subagent event payload",
|
|
86
|
+
"valid-configuration-event": "valid configuration event payload",
|
|
87
|
+
"valid-extension-ui-event": "valid extension UI event payload",
|
|
88
|
+
"known-event-type": "supported event type",
|
|
89
|
+
"notice-level-enum": "info, warning, or error",
|
|
90
|
+
"notice-message-string": "bounded string",
|
|
91
|
+
"valid-chunk-frame": "valid chunk metadata",
|
|
92
|
+
"valid-base64-chunk": "valid base64 chunk",
|
|
93
|
+
"first-chunk-index-zero": "first chunk index 0",
|
|
94
|
+
"contiguous-chunk-sequence": "contiguous chunk sequence",
|
|
95
|
+
"declared-chunk-byte-count": "decoded bytes equal declared byte length",
|
|
96
|
+
"chunk-before-deadline": "next chunk before timeout",
|
|
97
|
+
"no-interleaved-frame": "no interleaved frame during chunk assembly",
|
|
98
|
+
"no-remote-frame-error": "no remote frame error",
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
function finiteCount(value: number | null | undefined, maximum = Number.MAX_SAFE_INTEGER): string {
|
|
102
|
+
return value !== null && value !== undefined && Number.isSafeInteger(value) && value >= 0
|
|
103
|
+
? String(Math.min(value, maximum))
|
|
104
|
+
: "unknown";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function usefulReportLine(line: string): boolean {
|
|
108
|
+
return !line.endsWith(": unavailable") && !line.endsWith(": unknown") && !line.endsWith(": 0");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function formatVersion(version: OmpVersion | null): string {
|
|
112
|
+
if (!version) return "unavailable";
|
|
113
|
+
const core = `${version.major}.${version.minor}.${version.patch}`;
|
|
114
|
+
return version.prerelease ? `${core}-${version.prerelease}` : core;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function formatHealth(health: OmpProviderHealth | null): string[] {
|
|
118
|
+
if (!health) return ["collection.provider_health: failed"];
|
|
119
|
+
return [
|
|
120
|
+
"collection.provider_health: complete",
|
|
121
|
+
`omp.installed: ${health.binary.installed}`,
|
|
122
|
+
`omp.version: ${formatVersion(health.binary.version)}`,
|
|
123
|
+
`omp.version_probe: ${health.binary.versionStatus}`,
|
|
124
|
+
`omp.process_cleanup: ${health.binary.processCleanupFailed ? "failed" : "ok"}`,
|
|
125
|
+
`compatibility.rpc_ui: ${!health.rpcUi.checked || health.rpcUi.supported === null ? "unknown" : health.rpcUi.supported ? "supported" : "not-advertised"}`,
|
|
126
|
+
`compatibility.lsp: ${health.lsp.status}`,
|
|
127
|
+
`mcp.status: ${health.mcp.status}`,
|
|
128
|
+
`mcp.server_count: ${finiteCount(health.mcp.serverCount, 4_096)}`,
|
|
129
|
+
`mcp.reason: ${health.mcp.reason ?? "unknown"}`,
|
|
130
|
+
`storage.agent_root: ${health.roots.agentRootState}`,
|
|
131
|
+
`storage.config: ${health.roots.configState}`,
|
|
132
|
+
`storage.session_root: ${health.roots.sessionRootState}`,
|
|
133
|
+
`storage.agent_database: ${health.databases.agentDbState}`,
|
|
134
|
+
`storage.history_database: ${health.databases.historyDbState}`,
|
|
135
|
+
`storage.memory_backend: ${health.memoryBackend ?? "unknown"}`,
|
|
136
|
+
`hub.status: ${health.process.status}`,
|
|
137
|
+
`hub.tracked_count: ${finiteCount(health.process.trackedCount, 100_000)}`,
|
|
138
|
+
`hub.active_count: ${finiteCount(health.process.activeCount, 100_000)}`,
|
|
139
|
+
`hub.historical_count: ${finiteCount(health.process.historicalCount, 100_000)}`,
|
|
140
|
+
`hub.unknown_count: ${finiteCount(health.process.unknownCount, 100_000)}`,
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Stable line ordering is part of the pasted support-report contract. */
|
|
145
|
+
export function formatOmpSupportReport(data: OmpSupportReportData): string {
|
|
146
|
+
const lines = [
|
|
147
|
+
"OMP support diagnostics",
|
|
148
|
+
`schema_version: ${OMP_SUPPORT_REPORT_SCHEMA_VERSION}`,
|
|
149
|
+
`collected_at_utc: ${data.collectedAt}`,
|
|
150
|
+
`paseo_omp.version: ${data.pluginVersion}`,
|
|
151
|
+
`runtime.platform: ${data.platform}`,
|
|
152
|
+
`runtime.architecture: ${data.architecture}`,
|
|
153
|
+
`runtime.node: ${data.nodeVersion}`,
|
|
154
|
+
`selection.scope: ${data.scope}`,
|
|
155
|
+
`selection.store: ${data.store}`,
|
|
156
|
+
"selection.applies_to: provider-health",
|
|
157
|
+
"diagnostic_counters.scope: plugin-process-all-stores-workspaces",
|
|
158
|
+
"diagnostic_counters.lifetime: since-plugin-load",
|
|
159
|
+
...formatHealth(data.health),
|
|
160
|
+
];
|
|
161
|
+
for (const violation of data.violations) {
|
|
162
|
+
if (violation.occurrenceCount === 0) continue;
|
|
163
|
+
const prefix = `protocol.${violation.category}`;
|
|
164
|
+
lines.push(
|
|
165
|
+
`${prefix}.occurrence_count: ${finiteCount(violation.occurrenceCount)}`,
|
|
166
|
+
`${prefix}.batch_count: ${finiteCount(violation.batchCount)}`,
|
|
167
|
+
`${prefix}.max_batch_count: ${finiteCount(violation.maxOccurrenceCount)}`,
|
|
168
|
+
);
|
|
169
|
+
for (const [reason, count] of Object.entries(violation.reasonCounts)) {
|
|
170
|
+
if (count > 0) lines.push(`${prefix}.reason.${reason}.occurrence_count: ${count}`);
|
|
171
|
+
}
|
|
172
|
+
lines.push(
|
|
173
|
+
`${prefix}.latest_reason: ${violation.latestReason ?? "unknown"}`,
|
|
174
|
+
`${prefix}.latest_phase: ${violation.latestPhase ?? "unknown"}`,
|
|
175
|
+
`${prefix}.latest_event_type: ${violation.latestEventType ?? "unknown"}`,
|
|
176
|
+
`${prefix}.first_at_utc: ${violation.firstAt ?? "unavailable"}`,
|
|
177
|
+
`${prefix}.last_at_utc: ${violation.lastAt ?? "unavailable"}`,
|
|
178
|
+
`${prefix}.latest_frame_type: ${violation.latestFrameType ?? "unknown"}`,
|
|
179
|
+
`${prefix}.latest_field: ${violation.latestField ?? "unknown"}`,
|
|
180
|
+
`${prefix}.latest_expected: ${violation.latestExpected ? EXPECTATION_LABELS[violation.latestExpected] : "unknown"}`,
|
|
181
|
+
`${prefix}.latest_actual_type: ${violation.latestActualType ?? "unknown"}`,
|
|
182
|
+
`${prefix}.max_byte_size: ${finiteCount(violation.maxByteSize)}`,
|
|
183
|
+
`${prefix}.latest_limit_bytes: ${finiteCount(violation.latestLimitBytes)}`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
for (const failure of data.operationalFailures) {
|
|
187
|
+
if (failure.occurrenceCount === 0) continue;
|
|
188
|
+
const prefix = `operational.${failure.category}.${failure.stage}`;
|
|
189
|
+
lines.push(
|
|
190
|
+
`${prefix}.occurrence_count: ${finiteCount(failure.occurrenceCount)}`,
|
|
191
|
+
`${prefix}.first_at_utc: ${failure.firstAt ?? "unavailable"}`,
|
|
192
|
+
`${prefix}.last_at_utc: ${failure.lastAt ?? "unavailable"}`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
const report = `${lines.filter(usefulReportLine).join("\n")}\n`;
|
|
196
|
+
if (supportReportByteLength(report) > OMP_SUPPORT_REPORT_MAX_BYTES) {
|
|
197
|
+
return [
|
|
198
|
+
"OMP support diagnostics",
|
|
199
|
+
`schema_version: ${OMP_SUPPORT_REPORT_SCHEMA_VERSION}`,
|
|
200
|
+
`collected_at_utc: ${data.collectedAt}`,
|
|
201
|
+
"collection_status: failed",
|
|
202
|
+
"collection_error: report-size-limit",
|
|
203
|
+
"",
|
|
204
|
+
].join("\n");
|
|
205
|
+
}
|
|
206
|
+
return report;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function collectedAt(now: () => Date): string {
|
|
210
|
+
try {
|
|
211
|
+
const value = now();
|
|
212
|
+
return Number.isFinite(value.getTime()) ? value.toISOString() : "unavailable";
|
|
213
|
+
} catch {
|
|
214
|
+
return "unavailable";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function resolveGetOmpSupportReport(
|
|
219
|
+
input: RpcInput<typeof getOmpSupportReport>,
|
|
220
|
+
violations: OmpProtocolViolationCollector,
|
|
221
|
+
operationalFailures: OmpOperationalFailureCollector,
|
|
222
|
+
dependencies: {
|
|
223
|
+
loadHealth?: typeof resolveGetOmpProviderHealth;
|
|
224
|
+
loadPluginVersion?: () => Promise<string>;
|
|
225
|
+
now?: () => Date;
|
|
226
|
+
platform?: () => string;
|
|
227
|
+
architecture?: () => string;
|
|
228
|
+
nodeVersion?: string;
|
|
229
|
+
} = {},
|
|
230
|
+
): Promise<{ report: string }> {
|
|
231
|
+
const timestamp = collectedAt(dependencies.now ?? (() => new Date()));
|
|
232
|
+
try {
|
|
233
|
+
const [healthResult, pluginVersionResult] = await Promise.allSettled([
|
|
234
|
+
(dependencies.loadHealth ?? resolveGetOmpProviderHealth)(input),
|
|
235
|
+
(dependencies.loadPluginVersion ?? (() => Promise.resolve(PASEO_OMP_PACKAGE_VERSION)))(),
|
|
236
|
+
]);
|
|
237
|
+
const platformValue = (dependencies.platform ?? platform)();
|
|
238
|
+
const architectureValue = (dependencies.architecture ?? arch)();
|
|
239
|
+
const nodeVersionValue = dependencies.nodeVersion ?? process.version;
|
|
240
|
+
return {
|
|
241
|
+
report: formatOmpSupportReport({
|
|
242
|
+
collectedAt: timestamp,
|
|
243
|
+
pluginVersion:
|
|
244
|
+
pluginVersionResult.status === "fulfilled" &&
|
|
245
|
+
PACKAGE_VERSION.safeParse(pluginVersionResult.value).success
|
|
246
|
+
? pluginVersionResult.value
|
|
247
|
+
: "unavailable",
|
|
248
|
+
platform: KNOWN_PLATFORMS[platformValue] ? platformValue : "unknown",
|
|
249
|
+
architecture: KNOWN_ARCHITECTURES[architectureValue] ? architectureValue : "unknown",
|
|
250
|
+
nodeVersion: NODE_VERSION.safeParse(nodeVersionValue).success
|
|
251
|
+
? nodeVersionValue
|
|
252
|
+
: "unavailable",
|
|
253
|
+
scope: input.cwd ? "workspace" : "global",
|
|
254
|
+
store: input.store?.profile
|
|
255
|
+
? "named-profile"
|
|
256
|
+
: input.store?.agentDir
|
|
257
|
+
? "custom-directory"
|
|
258
|
+
: "default",
|
|
259
|
+
health: healthResult.status === "fulfilled" ? healthResult.value : null,
|
|
260
|
+
violations: violations.snapshot(),
|
|
261
|
+
operationalFailures: operationalFailures.snapshot(),
|
|
262
|
+
}),
|
|
263
|
+
};
|
|
264
|
+
} catch {
|
|
265
|
+
return {
|
|
266
|
+
report: formatOmpSupportReport({
|
|
267
|
+
collectedAt: timestamp,
|
|
268
|
+
pluginVersion: "unavailable",
|
|
269
|
+
platform: "unknown",
|
|
270
|
+
architecture: "unknown",
|
|
271
|
+
nodeVersion: "unavailable",
|
|
272
|
+
scope: input.cwd ? "workspace" : "global",
|
|
273
|
+
store: input.store?.profile
|
|
274
|
+
? "named-profile"
|
|
275
|
+
: input.store?.agentDir
|
|
276
|
+
? "custom-directory"
|
|
277
|
+
: "default",
|
|
278
|
+
health: null,
|
|
279
|
+
violations: violations.snapshot(),
|
|
280
|
+
operationalFailures: operationalFailures.snapshot(),
|
|
281
|
+
}),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { defineRpc } from "@getpaseo/plugin";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { OmpWorkspaceCwdSchema } from "./hub";
|
|
4
|
+
import { OmpStoreSchema } from "./omp-store";
|
|
5
|
+
|
|
6
|
+
const OMP_MODEL_TEXT_LIMIT = 256;
|
|
7
|
+
const OMP_MODEL_SELECTOR_LIMIT = OMP_MODEL_TEXT_LIMIT * 2 + 1;
|
|
8
|
+
const OMP_MODEL_LIST_LIMIT = 256;
|
|
9
|
+
const OMP_MODEL_INPUT_LIMIT = 16;
|
|
10
|
+
const OMP_THINKING_LEVEL_LIMIT = 16;
|
|
11
|
+
const OMP_THINKING_LEVEL_TEXT_LIMIT = 32;
|
|
12
|
+
const OMP_CONTEXT_WINDOW_LIMIT = 100_000_000;
|
|
13
|
+
|
|
14
|
+
const OmpModelTextSchema = z.string().min(1).max(OMP_MODEL_TEXT_LIMIT);
|
|
15
|
+
|
|
16
|
+
export const OmpModelCandidateSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
selector: z.string().min(3).max(OMP_MODEL_SELECTOR_LIMIT),
|
|
19
|
+
provider: OmpModelTextSchema,
|
|
20
|
+
id: OmpModelTextSchema,
|
|
21
|
+
name: z.string().max(OMP_MODEL_TEXT_LIMIT).optional(),
|
|
22
|
+
reasoning: z.boolean(),
|
|
23
|
+
input: z.array(OmpModelTextSchema).max(OMP_MODEL_INPUT_LIMIT),
|
|
24
|
+
contextWindow: z
|
|
25
|
+
.number()
|
|
26
|
+
.int()
|
|
27
|
+
.nonnegative()
|
|
28
|
+
.max(OMP_CONTEXT_WINDOW_LIMIT)
|
|
29
|
+
.nullable()
|
|
30
|
+
.optional(),
|
|
31
|
+
thinkingLevels: z
|
|
32
|
+
.array(z.string().max(OMP_THINKING_LEVEL_TEXT_LIMIT))
|
|
33
|
+
.max(OMP_THINKING_LEVEL_LIMIT),
|
|
34
|
+
})
|
|
35
|
+
.strict();
|
|
36
|
+
export type OmpModelCandidate = z.infer<typeof OmpModelCandidateSchema>;
|
|
37
|
+
|
|
38
|
+
export const OmpModelListResultSchema = z
|
|
39
|
+
.object({ models: z.array(OmpModelCandidateSchema).max(OMP_MODEL_LIST_LIMIT) })
|
|
40
|
+
.strict();
|
|
41
|
+
export type OmpModelListResult = z.infer<typeof OmpModelListResultSchema>;
|
|
42
|
+
|
|
43
|
+
export const listOmpModels = defineRpc({
|
|
44
|
+
name: "paseo-omp.list-models",
|
|
45
|
+
input: z
|
|
46
|
+
.object({ store: OmpStoreSchema.optional(), cwd: OmpWorkspaceCwdSchema.optional() })
|
|
47
|
+
.strict(),
|
|
48
|
+
output: OmpModelListResultSchema,
|
|
49
|
+
});
|