@omercnet/paseo-omp 0.3.0-next.102.1 → 0.3.0-next.104.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 +1 -1
- package/server/provider/catalog.ts +39 -10
- package/server/provider/omp-rpc.ts +60 -19
- package/server/provider/session.ts +116 -54
package/package.json
CHANGED
|
@@ -48,6 +48,27 @@ const THINKING_OPTIONS: readonly ProviderThinkingOption[] = [
|
|
|
48
48
|
{ id: "xhigh", label: "XHigh", description: "Extra-high reasoning" },
|
|
49
49
|
{ id: "max", label: "Max", description: "Maximum reasoning" },
|
|
50
50
|
];
|
|
51
|
+
export const OMP_MAX_CATALOG_MODELS = 256;
|
|
52
|
+
|
|
53
|
+
export function selectOmpModels(
|
|
54
|
+
models: readonly OmpModel[],
|
|
55
|
+
activeModel: OmpModel | null | undefined,
|
|
56
|
+
): OmpModel[] {
|
|
57
|
+
if (models.length <= OMP_MAX_CATALOG_MODELS) return [...models];
|
|
58
|
+
const selected = models.slice(0, OMP_MAX_CATALOG_MODELS);
|
|
59
|
+
if (!activeModel) return selected;
|
|
60
|
+
const active = models.find(
|
|
61
|
+
(model) => model.provider === activeModel.provider && model.id === activeModel.id,
|
|
62
|
+
);
|
|
63
|
+
if (
|
|
64
|
+
!active ||
|
|
65
|
+
selected.some((model) => model.provider === active.provider && model.id === active.id)
|
|
66
|
+
) {
|
|
67
|
+
return selected;
|
|
68
|
+
}
|
|
69
|
+
selected[OMP_MAX_CATALOG_MODELS - 1] = active;
|
|
70
|
+
return selected;
|
|
71
|
+
}
|
|
51
72
|
|
|
52
73
|
export function nativeOmpModelId(model: OmpModel): string {
|
|
53
74
|
if (model.provider.includes("/")) {
|
|
@@ -60,14 +81,9 @@ export function ompModelId(model: OmpModel): string {
|
|
|
60
81
|
const nativeIdentity = `${Buffer.byteLength(model.provider, "utf8")}:${model.provider}${Buffer.byteLength(model.id, "utf8")}:${model.id}`;
|
|
61
82
|
return `omp:model:${createHash("sha256").update(nativeIdentity).digest("hex")}`;
|
|
62
83
|
}
|
|
63
|
-
|
|
64
|
-
export function mapOmpModels(
|
|
65
|
-
models: readonly OmpModel[],
|
|
66
|
-
serializer = new OmpPublicDataSerializer(),
|
|
67
|
-
): ProviderModel[] {
|
|
84
|
+
export function validateOmpModelIdentities(models: readonly OmpModel[]): void {
|
|
68
85
|
const seenIds = new Map<string, string>();
|
|
69
|
-
|
|
70
|
-
const thinkingOptions = thinkingForModel(model);
|
|
86
|
+
for (const model of models) {
|
|
71
87
|
const id = ompModelId(model);
|
|
72
88
|
const nativeIdentity = nativeOmpModelId(model);
|
|
73
89
|
const existing = seenIds.get(id);
|
|
@@ -76,6 +92,17 @@ export function mapOmpModels(
|
|
|
76
92
|
}
|
|
77
93
|
if (existing !== undefined) throw new Error("OMP reported a duplicate model identity");
|
|
78
94
|
seenIds.set(id, nativeIdentity);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function mapOmpModels(
|
|
99
|
+
models: readonly OmpModel[],
|
|
100
|
+
serializer = new OmpPublicDataSerializer(),
|
|
101
|
+
): ProviderModel[] {
|
|
102
|
+
validateOmpModelIdentities(models);
|
|
103
|
+
return models.map((model) => {
|
|
104
|
+
const thinkingOptions = thinkingForModel(model);
|
|
105
|
+
const id = ompModelId(model);
|
|
79
106
|
const provider = serializer.text(model.provider, 256);
|
|
80
107
|
const modelId = serializer.text(model.id, 256);
|
|
81
108
|
const name = model.name ? serializer.text(model.name, 256) : modelId;
|
|
@@ -143,14 +170,16 @@ export async function discoverOmpCatalog(
|
|
|
143
170
|
? [...configuredValues, ...(session.inheritedRedactionValues ?? [])]
|
|
144
171
|
: configuredValues,
|
|
145
172
|
);
|
|
146
|
-
|
|
173
|
+
validateOmpModelIdentities(nativeModels);
|
|
174
|
+
const selectedNativeModels = selectOmpModels(nativeModels, state.model);
|
|
175
|
+
const models = mapOmpModels(selectedNativeModels, serializer);
|
|
147
176
|
if (models.length === 0) throw new Error("OMP reported no available models");
|
|
148
177
|
const defaultModel = state.model ? ompModelId(state.model) : models[0]?.id;
|
|
149
178
|
const currentModel = state.model
|
|
150
|
-
?
|
|
179
|
+
? selectedNativeModels.find(
|
|
151
180
|
(model) => model.provider === state.model?.provider && model.id === state.model.id,
|
|
152
181
|
)
|
|
153
|
-
:
|
|
182
|
+
: selectedNativeModels[0];
|
|
154
183
|
if (state.model && !currentModel) throw new Error("OMP reported an unadvertised active model");
|
|
155
184
|
const thinkingOptions = thinkingForModel(currentModel);
|
|
156
185
|
const defaultThinkingOption = thinkingOptions.some(
|
|
@@ -70,6 +70,7 @@ const MAX_OPTIONAL_METADATA_NODES = 4_096;
|
|
|
70
70
|
const MAX_TASK_CORRELATION_BYTES = 256 * 1024;
|
|
71
71
|
const MAX_TASK_CORRELATION_ITEMS = 1_024;
|
|
72
72
|
const MAX_TASK_CORRELATION_NODES = 4_096;
|
|
73
|
+
const MAX_MODEL_CATALOG_ITEMS = 4_096;
|
|
73
74
|
// Tool-intensive OMP turns legitimately exceed 64 blocks; transport byte/node budgets remain the
|
|
74
75
|
// primary resource bounds.
|
|
75
76
|
export const OMP_MAX_CONTENT_PARTS = 4_096;
|
|
@@ -115,6 +116,17 @@ function boundedString(maxBytes: number, minBytes = 0) {
|
|
|
115
116
|
return bytes >= minBytes && bytes <= maxBytes;
|
|
116
117
|
});
|
|
117
118
|
}
|
|
119
|
+
function boundedOptionalStringArray(maxItems: number, maxBytes: number) {
|
|
120
|
+
return z.unknown().transform((value): string[] | undefined => {
|
|
121
|
+
if (!Array.isArray(value)) return undefined;
|
|
122
|
+
return value
|
|
123
|
+
.filter(
|
|
124
|
+
(item): item is string =>
|
|
125
|
+
typeof item === "string" && item.length > 0 && utf8Bytes(item) <= maxBytes,
|
|
126
|
+
)
|
|
127
|
+
.slice(0, maxItems);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
118
130
|
|
|
119
131
|
const IDENTIFIER = boundedString(MAX_ID_LENGTH, 1);
|
|
120
132
|
const NAME = boundedString(MAX_NAME_LENGTH, 1);
|
|
@@ -191,6 +203,7 @@ function sanitizeLiveDisplayFrame(value: unknown): unknown {
|
|
|
191
203
|
return changed ? { ...frame, messages } : value;
|
|
192
204
|
}
|
|
193
205
|
const OmpThinkingLevelSchema = z.enum(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
206
|
+
const OmpCurrentThinkingLevelSchema = boundedString(32).optional().catch(undefined);
|
|
194
207
|
|
|
195
208
|
function isBoundedJson(
|
|
196
209
|
value: unknown,
|
|
@@ -546,19 +559,33 @@ const OmpAvailableCommandSchema = z.object({
|
|
|
546
559
|
.optional(),
|
|
547
560
|
source: boundedString(64).optional(),
|
|
548
561
|
});
|
|
562
|
+
const OmpThinkingMetadataSchema = z
|
|
563
|
+
.unknown()
|
|
564
|
+
.transform((value): { efforts?: string[]; defaultLevel?: string } | undefined => {
|
|
565
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
566
|
+
const record = value as Record<string, unknown>;
|
|
567
|
+
const efforts = boundedOptionalStringArray(16, 32).parse(record.efforts);
|
|
568
|
+
const defaultLevel = boundedString(32).optional().catch(undefined).parse(record.defaultLevel);
|
|
569
|
+
return {
|
|
570
|
+
...(efforts !== undefined ? { efforts } : {}),
|
|
571
|
+
...(defaultLevel !== undefined ? { defaultLevel } : {}),
|
|
572
|
+
};
|
|
573
|
+
});
|
|
549
574
|
const OmpModelSchema = z.object({
|
|
550
575
|
provider: OMP_PROVIDER_NAME,
|
|
551
576
|
id: NAME,
|
|
552
|
-
name: boundedString(MAX_NAME_LENGTH).optional(),
|
|
553
|
-
reasoning: z.boolean().optional(),
|
|
554
|
-
thinking:
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
.
|
|
560
|
-
|
|
561
|
-
|
|
577
|
+
name: boundedString(MAX_NAME_LENGTH).optional().catch(undefined),
|
|
578
|
+
reasoning: z.boolean().optional().catch(undefined),
|
|
579
|
+
thinking: OmpThinkingMetadataSchema.optional(),
|
|
580
|
+
input: boundedOptionalStringArray(16, MAX_NAME_LENGTH).optional(),
|
|
581
|
+
contextWindow: z
|
|
582
|
+
.number()
|
|
583
|
+
.int()
|
|
584
|
+
.nonnegative()
|
|
585
|
+
.max(100_000_000)
|
|
586
|
+
.nullable()
|
|
587
|
+
.optional()
|
|
588
|
+
.catch(undefined),
|
|
562
589
|
});
|
|
563
590
|
const TokenCountSchema = z.number().int().nonnegative().max(MAX_TOKEN_COUNT);
|
|
564
591
|
const OptionalTokenCountSchema = TokenCountSchema.nullable().optional();
|
|
@@ -610,13 +637,13 @@ const OmpCompactionResultSchema = z.object({
|
|
|
610
637
|
});
|
|
611
638
|
const OmpSessionStateSchema = z.object({
|
|
612
639
|
model: OmpModelSchema.nullable().optional(),
|
|
613
|
-
thinkingLevel:
|
|
640
|
+
thinkingLevel: OmpCurrentThinkingLevelSchema,
|
|
614
641
|
isStreaming: z.boolean(),
|
|
615
642
|
isCompacting: z.boolean(),
|
|
616
643
|
sessionId: IDENTIFIER,
|
|
617
644
|
autoCompactionEnabled: z.boolean().optional(),
|
|
618
|
-
contextUsage: OmpContextUsageSchema.nullable().optional(),
|
|
619
|
-
sessionFile: boundedString(MAX_PATH_LENGTH).optional(),
|
|
645
|
+
contextUsage: OmpContextUsageSchema.nullable().optional().catch(undefined),
|
|
646
|
+
sessionFile: boundedString(MAX_PATH_LENGTH).optional().catch(undefined),
|
|
620
647
|
});
|
|
621
648
|
const OmpReadyFrameSchema = z.object({
|
|
622
649
|
type: z.literal("ready"),
|
|
@@ -1217,7 +1244,7 @@ function runtimeFrameCollectionLimit(frame: Record<string, unknown>): number {
|
|
|
1217
1244
|
return 1_024;
|
|
1218
1245
|
}
|
|
1219
1246
|
const OmpModelsResultSchema = z.object({
|
|
1220
|
-
models: z.array(OmpModelSchema).min(1).max(
|
|
1247
|
+
models: z.array(OmpModelSchema).min(1).max(MAX_MODEL_CATALOG_ITEMS),
|
|
1221
1248
|
});
|
|
1222
1249
|
const OmpPromptAckSchema = z.object({ agentInvoked: z.boolean().optional() }).optional();
|
|
1223
1250
|
const OmpAvailableCommandsResultSchema = z.object({
|
|
@@ -1262,6 +1289,7 @@ const ProtocolNegotiationResultSchema = z.object({
|
|
|
1262
1289
|
});
|
|
1263
1290
|
|
|
1264
1291
|
export type OmpModel = z.infer<typeof OmpModelSchema>;
|
|
1292
|
+
export type OmpBranchResult = z.infer<typeof OmpBranchResultSchema>;
|
|
1265
1293
|
export type OmpSessionState = z.infer<typeof OmpSessionStateSchema>;
|
|
1266
1294
|
export type OmpSessionStats = z.infer<typeof OmpSessionStatsSchema>;
|
|
1267
1295
|
export type OmpCompactionResult = z.infer<typeof OmpCompactionResultSchema>;
|
|
@@ -1369,7 +1397,7 @@ export interface OmpRuntimeSession {
|
|
|
1369
1397
|
respondToExtensionUi(response: OmpExtensionUiResponse): Promise<void>;
|
|
1370
1398
|
respondToToolApproval(response: OmpToolApprovalResponse): Promise<void>;
|
|
1371
1399
|
getBranchMessages(): Promise<Array<{ entryId: string; text: string }>>;
|
|
1372
|
-
branch(entryId: string): Promise<
|
|
1400
|
+
branch(entryId: string): Promise<OmpBranchResult>;
|
|
1373
1401
|
readonly canReplayHistory: boolean;
|
|
1374
1402
|
getMessages(): Promise<OmpMessage[]>;
|
|
1375
1403
|
abort(): Promise<void>;
|
|
@@ -1416,6 +1444,13 @@ export interface OmpRpcRuntimeOptions {
|
|
|
1416
1444
|
) => OmpSessionDescriptor[] | Promise<OmpSessionDescriptor[]>;
|
|
1417
1445
|
}
|
|
1418
1446
|
|
|
1447
|
+
export class OmpRpcRequestRejectedError extends Error {
|
|
1448
|
+
constructor() {
|
|
1449
|
+
super("OMP RPC request failed");
|
|
1450
|
+
this.name = "OmpRpcRequestRejectedError";
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1419
1454
|
type PendingRequest = {
|
|
1420
1455
|
resolve(value: unknown): void;
|
|
1421
1456
|
reject(error: Error): void;
|
|
@@ -2472,13 +2507,19 @@ class OmpRpcProcess {
|
|
|
2472
2507
|
: response.data.data;
|
|
2473
2508
|
const boundedFrame =
|
|
2474
2509
|
responseData === response.data.data ? frame : { ...frame, data: responseData };
|
|
2475
|
-
const responseItemLimit = isBranchHistory
|
|
2510
|
+
const responseItemLimit = isBranchHistory
|
|
2511
|
+
? 1_024
|
|
2512
|
+
: isHistory
|
|
2513
|
+
? 100_000
|
|
2514
|
+
: pending.command === "get_available_models"
|
|
2515
|
+
? MAX_MODEL_CATALOG_ITEMS
|
|
2516
|
+
: MAX_ARRAY_ITEMS;
|
|
2476
2517
|
const responseByteLimit =
|
|
2477
2518
|
isBranchHistory || isHistory
|
|
2478
2519
|
? Math.min(MAX_REASSEMBLED_FRAME_BYTES, this.reassembledFrameLimit)
|
|
2479
2520
|
: 2 * 1024 * 1024;
|
|
2480
|
-
//
|
|
2481
|
-
// node
|
|
2521
|
+
// Model catalogs are truncated before publication, while the transport still enforces
|
|
2522
|
+
// aggregate byte and node budgets over the complete response.
|
|
2482
2523
|
const responseNodeLimit = isBranchHistory
|
|
2483
2524
|
? 4_096
|
|
2484
2525
|
: isHistory
|
|
@@ -2517,7 +2558,7 @@ class OmpRpcProcess {
|
|
|
2517
2558
|
settled.reject(new Error("OMP RPC response is invalid"));
|
|
2518
2559
|
}
|
|
2519
2560
|
} else {
|
|
2520
|
-
settled.reject(new
|
|
2561
|
+
settled.reject(new OmpRpcRequestRejectedError());
|
|
2521
2562
|
}
|
|
2522
2563
|
}
|
|
2523
2564
|
|
|
@@ -11,7 +11,15 @@ import type {
|
|
|
11
11
|
ProviderUsage,
|
|
12
12
|
} from "@getpaseo/plugin/server/provider";
|
|
13
13
|
import { getForgeDefinitionOrNeutral } from "@getpaseo/protocol/forge-manifest";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
mapOmpModels,
|
|
16
|
+
nativeOmpModelId,
|
|
17
|
+
OMP_MODES,
|
|
18
|
+
ompModelId,
|
|
19
|
+
selectOmpModels,
|
|
20
|
+
thinkingForModel,
|
|
21
|
+
validateOmpModelIdentities,
|
|
22
|
+
} from "./catalog";
|
|
15
23
|
import {
|
|
16
24
|
normalizeOmpSessionConfig,
|
|
17
25
|
type OmpRecoveryOptions,
|
|
@@ -21,6 +29,7 @@ import { OmpHostToolsBridge, type OmpMcpConnector, validateOmpHostToolConfig } f
|
|
|
21
29
|
import { isOmpImageMimeType, isValidImagePayload, OmpImageMaterializer } from "./image";
|
|
22
30
|
import type {
|
|
23
31
|
OmpAvailableCommand,
|
|
32
|
+
OmpBranchResult,
|
|
24
33
|
OmpCompactionResult,
|
|
25
34
|
OmpExtensionUiResponse,
|
|
26
35
|
OmpImage,
|
|
@@ -35,7 +44,7 @@ import type {
|
|
|
35
44
|
OmpToolApprovalCancel,
|
|
36
45
|
OmpToolApprovalRequest,
|
|
37
46
|
} from "./omp-rpc";
|
|
38
|
-
import { buildOmpSpawnRequest } from "./omp-rpc";
|
|
47
|
+
import { buildOmpSpawnRequest, OmpRpcRequestRejectedError } from "./omp-rpc";
|
|
39
48
|
import {
|
|
40
49
|
BoundedStringSet,
|
|
41
50
|
boundedJsonBytes,
|
|
@@ -938,6 +947,8 @@ export class OmpProviderSession {
|
|
|
938
947
|
private readonly pendingToolPermissions = new Map<string, PendingToolPermission>();
|
|
939
948
|
private readonly inFlightToolPermissions = new Map<string, PendingToolPermission>();
|
|
940
949
|
private readonly resolvedToolApprovalIds = new BoundedStringSet(MAX_TRACKED_ENTRY_IDS);
|
|
950
|
+
private unsupportedThinkingNoticePending = false;
|
|
951
|
+
private unsupportedThinkingNoticePublished = false;
|
|
941
952
|
|
|
942
953
|
private constructor(
|
|
943
954
|
id: string,
|
|
@@ -1056,10 +1067,9 @@ export class OmpProviderSession {
|
|
|
1056
1067
|
}
|
|
1057
1068
|
const startOptions: OmpStartOptions = {
|
|
1058
1069
|
...normalizedConfig,
|
|
1059
|
-
//
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
: {}),
|
|
1070
|
+
// Thinking is authorized only after this runtime reports its exact model catalog.
|
|
1071
|
+
thinkingOption: undefined,
|
|
1072
|
+
...(resumeSessionId ? { systemPrompt: undefined, resumeSessionId } : {}),
|
|
1063
1073
|
signal,
|
|
1064
1074
|
environment,
|
|
1065
1075
|
};
|
|
@@ -1095,6 +1105,7 @@ export class OmpProviderSession {
|
|
|
1095
1105
|
() => ({ available: false, commands: [] }),
|
|
1096
1106
|
),
|
|
1097
1107
|
]);
|
|
1108
|
+
validateOmpModelIdentities(nativeModels);
|
|
1098
1109
|
if (effectiveConfig.persist && !native.canReplayHistory) {
|
|
1099
1110
|
throw new OmpPublicError("OMP session persistence requires negotiated RPC protocol v2");
|
|
1100
1111
|
}
|
|
@@ -1108,12 +1119,10 @@ export class OmpProviderSession {
|
|
|
1108
1119
|
if (resumeSessionId && initialState.sessionId !== resumeSessionId) {
|
|
1109
1120
|
throw new OmpPublicError("OMP resumed a different native session");
|
|
1110
1121
|
}
|
|
1111
|
-
const models = mapOmpModels(nativeModels, new OmpPublicDataSerializer(outputRedactionValues));
|
|
1112
|
-
const nativeModelsByPublicId = new Map(
|
|
1113
|
-
nativeModels.map((model) => [ompModelId(model), model] as const),
|
|
1114
|
-
);
|
|
1115
1122
|
if (!resumeSessionId && input.config.model) {
|
|
1116
|
-
const selected =
|
|
1123
|
+
const selected = selectOmpModels(nativeModels, state.model).find(
|
|
1124
|
+
(model) => ompModelId(model) === input.config.model,
|
|
1125
|
+
);
|
|
1117
1126
|
if (!selected) {
|
|
1118
1127
|
throw new OmpPublicError("OMP model is not advertised by the configured session runtime");
|
|
1119
1128
|
}
|
|
@@ -1122,9 +1131,29 @@ export class OmpProviderSession {
|
|
|
1122
1131
|
state = await native.getState();
|
|
1123
1132
|
}
|
|
1124
1133
|
}
|
|
1134
|
+
let currentModel = state.model
|
|
1135
|
+
? nativeModels.find(
|
|
1136
|
+
(model) => model.provider === state.model?.provider && model.id === state.model.id,
|
|
1137
|
+
)
|
|
1138
|
+
: undefined;
|
|
1139
|
+
if (state.model && !currentModel) {
|
|
1140
|
+
throw new OmpPublicError("OMP runtime selected an unadvertised model");
|
|
1141
|
+
}
|
|
1142
|
+
if (!resumeSessionId && input.config.thinkingOption !== undefined) {
|
|
1143
|
+
if (
|
|
1144
|
+
!thinkingForModel(currentModel).some(
|
|
1145
|
+
(option) => option.id === input.config.thinkingOption,
|
|
1146
|
+
)
|
|
1147
|
+
) {
|
|
1148
|
+
throw new OmpPublicError("OMP thinking level is unavailable for the selected model");
|
|
1149
|
+
}
|
|
1150
|
+
if (state.thinkingLevel !== input.config.thinkingOption) {
|
|
1151
|
+
await native.setThinkingLevel(input.config.thinkingOption);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1125
1154
|
const reconciledConfigRevision = bootstrapConfigRevision;
|
|
1126
1155
|
state = await native.getState();
|
|
1127
|
-
|
|
1156
|
+
currentModel = state.model
|
|
1128
1157
|
? nativeModels.find(
|
|
1129
1158
|
(model) => model.provider === state.model?.provider && model.id === state.model.id,
|
|
1130
1159
|
)
|
|
@@ -1132,21 +1161,21 @@ export class OmpProviderSession {
|
|
|
1132
1161
|
if (state.model && !currentModel) {
|
|
1133
1162
|
throw new OmpPublicError("OMP runtime selected an unadvertised model");
|
|
1134
1163
|
}
|
|
1164
|
+
const selectedNativeModels = selectOmpModels(nativeModels, state.model);
|
|
1165
|
+
const models = mapOmpModels(
|
|
1166
|
+
selectedNativeModels,
|
|
1167
|
+
new OmpPublicDataSerializer(outputRedactionValues),
|
|
1168
|
+
);
|
|
1169
|
+
const nativeModelsByPublicId = new Map(
|
|
1170
|
+
nativeModels.map((model) => [ompModelId(model), model] as const),
|
|
1171
|
+
);
|
|
1135
1172
|
const thinkingOptions = thinkingForModel(currentModel);
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
throw new OmpPublicError("OMP thinking level is unavailable for the selected model");
|
|
1143
|
-
}
|
|
1144
|
-
if (
|
|
1145
|
-
committedThinkingLevel &&
|
|
1146
|
-
!thinkingOptions.some((option) => option.id === committedThinkingLevel)
|
|
1147
|
-
) {
|
|
1148
|
-
throw new OmpPublicError("OMP runtime selected an unsupported thinking level");
|
|
1149
|
-
}
|
|
1173
|
+
const applicableLevel = applicableThinkingLevel(currentModel, state.thinkingLevel);
|
|
1174
|
+
const committedThinkingLevel = thinkingOptions.some((option) => option.id === applicableLevel)
|
|
1175
|
+
? applicableLevel
|
|
1176
|
+
: undefined;
|
|
1177
|
+
const unsupportedThinkingLevel =
|
|
1178
|
+
applicableLevel !== undefined && committedThinkingLevel === undefined;
|
|
1150
1179
|
const configState: ProviderConfigState = {
|
|
1151
1180
|
...(state.model ? { model: ompModelId(state.model) } : {}),
|
|
1152
1181
|
mode: normalizedConfig.mode ?? "full",
|
|
@@ -1209,6 +1238,7 @@ export class OmpProviderSession {
|
|
|
1209
1238
|
retireRewindSession,
|
|
1210
1239
|
scheduler,
|
|
1211
1240
|
);
|
|
1241
|
+
session.unsupportedThinkingNoticePending = unsupportedThinkingLevel;
|
|
1212
1242
|
|
|
1213
1243
|
if (bootstrapConfigRevision !== reconciledConfigRevision) {
|
|
1214
1244
|
session.configRefreshDirty = true;
|
|
@@ -1267,6 +1297,7 @@ export class OmpProviderSession {
|
|
|
1267
1297
|
: {}),
|
|
1268
1298
|
...(this.config.title ? { title: this.dataFilter.text(this.config.title, 256) } : {}),
|
|
1269
1299
|
});
|
|
1300
|
+
this.publishUnsupportedThinkingNotice();
|
|
1270
1301
|
this.emit({ type: "session.config", sessionId: this.id, config: this.configState });
|
|
1271
1302
|
if (this.replayHistoryOnOpen) await this.replayHistory(true);
|
|
1272
1303
|
this.publishCommands(this.commandCatalog);
|
|
@@ -1274,6 +1305,22 @@ export class OmpProviderSession {
|
|
|
1274
1305
|
this.readyPublished = true;
|
|
1275
1306
|
if (this.configRefreshDirty) this.scheduleCommittedConfigRefresh();
|
|
1276
1307
|
}
|
|
1308
|
+
private publishUnsupportedThinkingNotice(): void {
|
|
1309
|
+
if (!this.unsupportedThinkingNoticePending || this.unsupportedThinkingNoticePublished) return;
|
|
1310
|
+
this.unsupportedThinkingNoticePending = false;
|
|
1311
|
+
this.unsupportedThinkingNoticePublished = true;
|
|
1312
|
+
this.emit({
|
|
1313
|
+
type: "session.notice",
|
|
1314
|
+
sessionId: this.id,
|
|
1315
|
+
notice: {
|
|
1316
|
+
id: "omp:unsupported-thinking-level",
|
|
1317
|
+
severity: "warning",
|
|
1318
|
+
title: "OMP thinking level unavailable",
|
|
1319
|
+
description:
|
|
1320
|
+
"OMP reported a thinking level that the active model does not advertise. Paseo omitted it from the session configuration.",
|
|
1321
|
+
},
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1277
1324
|
|
|
1278
1325
|
private usageFrom(
|
|
1279
1326
|
state: OmpSessionState | undefined,
|
|
@@ -1679,19 +1726,33 @@ export class OmpProviderSession {
|
|
|
1679
1726
|
throw new OmpPublicError("OMP conversation rewind requires negotiated RPC protocol v2");
|
|
1680
1727
|
}
|
|
1681
1728
|
const entryId = this.projector.resolveRevertToken(input.token);
|
|
1682
|
-
const
|
|
1683
|
-
runtime.getBranchMessages(),
|
|
1684
|
-
runtime.getState(),
|
|
1685
|
-
]);
|
|
1729
|
+
const beforeState = await runtime.getState();
|
|
1686
1730
|
this.requireCurrentRuntime(runtime, generation);
|
|
1687
1731
|
if (this.activeTurn || beforeState.isStreaming || beforeState.isCompacting) {
|
|
1688
1732
|
throw new OmpPublicError("Cannot rewind the OMP conversation while a turn is active");
|
|
1689
1733
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1734
|
+
const beforeAdvertisedModel = beforeState.model
|
|
1735
|
+
? this.nativeModelsByPublicId.get(ompModelId(beforeState.model))
|
|
1736
|
+
: undefined;
|
|
1737
|
+
if (beforeState.model && !beforeAdvertisedModel) {
|
|
1738
|
+
throw new OmpPublicError("OMP runtime selected an unadvertised model");
|
|
1692
1739
|
}
|
|
1740
|
+
const restorableThinkingLevel = thinkingForModel(beforeAdvertisedModel).some(
|
|
1741
|
+
(option) => option.id === beforeState.thinkingLevel,
|
|
1742
|
+
)
|
|
1743
|
+
? beforeState.thinkingLevel
|
|
1744
|
+
: undefined;
|
|
1693
1745
|
branchMutationPossible = true;
|
|
1694
|
-
|
|
1746
|
+
let result: OmpBranchResult;
|
|
1747
|
+
try {
|
|
1748
|
+
result = await runtime.branch(entryId);
|
|
1749
|
+
} catch (error) {
|
|
1750
|
+
if (error instanceof OmpRpcRequestRejectedError) {
|
|
1751
|
+
branchMutationPossible = false;
|
|
1752
|
+
throw new OmpPublicError("OMP conversation rewind token is stale");
|
|
1753
|
+
}
|
|
1754
|
+
throw error;
|
|
1755
|
+
}
|
|
1695
1756
|
this.requireCurrentRuntime(runtime, generation);
|
|
1696
1757
|
if (result.cancelled) {
|
|
1697
1758
|
branchMutationPossible = false;
|
|
@@ -1723,13 +1784,10 @@ export class OmpProviderSession {
|
|
|
1723
1784
|
}
|
|
1724
1785
|
await runtime.setModel(beforeState.model.provider, beforeState.model.id);
|
|
1725
1786
|
}
|
|
1726
|
-
if (thinkingChanged) {
|
|
1727
|
-
|
|
1728
|
-
throw new OmpPublicError("OMP changed thinking level while rewinding the conversation");
|
|
1729
|
-
}
|
|
1730
|
-
await runtime.setThinkingLevel(beforeState.thinkingLevel);
|
|
1787
|
+
if (thinkingChanged && restorableThinkingLevel) {
|
|
1788
|
+
await runtime.setThinkingLevel(restorableThinkingLevel);
|
|
1731
1789
|
}
|
|
1732
|
-
if (modelChanged || thinkingChanged) {
|
|
1790
|
+
if (modelChanged || (thinkingChanged && restorableThinkingLevel)) {
|
|
1733
1791
|
state = await runtime.getState();
|
|
1734
1792
|
this.requireCurrentRuntime(runtime, generation);
|
|
1735
1793
|
}
|
|
@@ -1737,7 +1795,7 @@ export class OmpProviderSession {
|
|
|
1737
1795
|
state.sessionId !== this.nativeSessionId ||
|
|
1738
1796
|
state.model?.provider !== beforeState.model?.provider ||
|
|
1739
1797
|
state.model?.id !== beforeState.model?.id ||
|
|
1740
|
-
state.thinkingLevel !==
|
|
1798
|
+
(restorableThinkingLevel !== undefined && state.thinkingLevel !== restorableThinkingLevel)
|
|
1741
1799
|
) {
|
|
1742
1800
|
throw new OmpPublicError("OMP did not preserve session configuration while rewinding");
|
|
1743
1801
|
}
|
|
@@ -2337,7 +2395,10 @@ export class OmpProviderSession {
|
|
|
2337
2395
|
const targetModel = targetModelId
|
|
2338
2396
|
? this.nativeModelsByPublicId.get(targetModelId)
|
|
2339
2397
|
: undefined;
|
|
2340
|
-
|
|
2398
|
+
const targetModelPublished = this.configState.models.some(
|
|
2399
|
+
(model) => model.id === targetModelId,
|
|
2400
|
+
);
|
|
2401
|
+
if (input.changes.model !== undefined && (!targetModel || !targetModelPublished)) {
|
|
2341
2402
|
throw new OmpPublicError("OMP model selection is unavailable");
|
|
2342
2403
|
}
|
|
2343
2404
|
if (
|
|
@@ -2584,13 +2645,18 @@ export class OmpProviderSession {
|
|
|
2584
2645
|
throw new OmpCatalogEscape("OMP runtime selected an unadvertised model");
|
|
2585
2646
|
}
|
|
2586
2647
|
const thinkingOptions = thinkingForModel(advertisedModel);
|
|
2587
|
-
const
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
) {
|
|
2592
|
-
|
|
2648
|
+
const applicableLevel = applicableThinkingLevel(advertisedModel, state.thinkingLevel);
|
|
2649
|
+
const committedThinkingLevel = thinkingOptions.some((option) => option.id === applicableLevel)
|
|
2650
|
+
? applicableLevel
|
|
2651
|
+
: undefined;
|
|
2652
|
+
if (applicableLevel !== undefined && committedThinkingLevel === undefined) {
|
|
2653
|
+
this.unsupportedThinkingNoticePending = true;
|
|
2654
|
+
this.publishUnsupportedThinkingNotice();
|
|
2593
2655
|
}
|
|
2656
|
+
const models = mapOmpModels(
|
|
2657
|
+
selectOmpModels([...this.nativeModelsByPublicId.values()], state.model),
|
|
2658
|
+
this.dataFilter,
|
|
2659
|
+
);
|
|
2594
2660
|
this.configRefreshAttempts = 0;
|
|
2595
2661
|
this.cancelConfigRefreshRetry();
|
|
2596
2662
|
const nextConfig: ProviderConfigState = {
|
|
@@ -2599,12 +2665,15 @@ export class OmpProviderSession {
|
|
|
2599
2665
|
...(committedThinkingLevel
|
|
2600
2666
|
? { thinkingOption: committedThinkingLevel }
|
|
2601
2667
|
: { thinkingOption: undefined }),
|
|
2668
|
+
models,
|
|
2602
2669
|
thinkingOptions,
|
|
2603
2670
|
};
|
|
2604
2671
|
const changed =
|
|
2605
2672
|
force ||
|
|
2606
2673
|
nextConfig.model !== this.configState.model ||
|
|
2607
2674
|
nextConfig.thinkingOption !== this.configState.thinkingOption ||
|
|
2675
|
+
nextConfig.models.some((model, index) => model.id !== this.configState.models[index]?.id) ||
|
|
2676
|
+
this.configState.models.length !== nextConfig.models.length ||
|
|
2608
2677
|
nextConfig.thinkingOptions.length !== this.configState.thinkingOptions.length ||
|
|
2609
2678
|
nextConfig.thinkingOptions.some(
|
|
2610
2679
|
(option, index) =>
|
|
@@ -2925,13 +2994,6 @@ export class OmpProviderSession {
|
|
|
2925
2994
|
if (state.model && !advertisedModel) {
|
|
2926
2995
|
throw new Error("OMP recovered with an unadvertised model");
|
|
2927
2996
|
}
|
|
2928
|
-
const recoveredThinkingLevel = applicableThinkingLevel(advertisedModel, state.thinkingLevel);
|
|
2929
|
-
if (
|
|
2930
|
-
recoveredThinkingLevel &&
|
|
2931
|
-
!thinkingForModel(advertisedModel).some((option) => option.id === recoveredThinkingLevel)
|
|
2932
|
-
) {
|
|
2933
|
-
throw new Error("OMP recovered with an unsupported thinking level");
|
|
2934
|
-
}
|
|
2935
2997
|
if (this.closed) throw new Error("OMP session closed while runtime recovery was pending");
|
|
2936
2998
|
if (!this.hostTools.isBoundTo(recovered)) {
|
|
2937
2999
|
throw new Error("OMP host tool bridge detached during recovery");
|