@omercnet/paseo-omp 0.3.0 → 0.4.0-next.114.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 +2 -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 +60 -8
- package/server/provider/host-tools.ts +284 -34
- package/server/provider/mcp-transport.ts +2 -1
- package/server/provider/omp-rpc.ts +1011 -107
- 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 +45 -11
- package/server/provider/session.ts +200 -58
- package/server/provider/subsessions.ts +311 -73
- package/server/provider/timeline-projector.ts +34 -11
- 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
|
@@ -11,7 +11,19 @@ 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 type {
|
|
15
|
+
OmpOperationalFailure,
|
|
16
|
+
OmpOperationalFailureReporter,
|
|
17
|
+
} from "../operational-failure-diagnostics";
|
|
18
|
+
import {
|
|
19
|
+
mapOmpModels,
|
|
20
|
+
nativeOmpModelId,
|
|
21
|
+
OMP_MODES,
|
|
22
|
+
ompModelId,
|
|
23
|
+
selectOmpModels,
|
|
24
|
+
thinkingForModel,
|
|
25
|
+
validateOmpModelIdentities,
|
|
26
|
+
} from "./catalog";
|
|
15
27
|
import {
|
|
16
28
|
normalizeOmpSessionConfig,
|
|
17
29
|
type OmpRecoveryOptions,
|
|
@@ -21,6 +33,7 @@ import { OmpHostToolsBridge, type OmpMcpConnector, validateOmpHostToolConfig } f
|
|
|
21
33
|
import { isOmpImageMimeType, isValidImagePayload, OmpImageMaterializer } from "./image";
|
|
22
34
|
import type {
|
|
23
35
|
OmpAvailableCommand,
|
|
36
|
+
OmpBranchResult,
|
|
24
37
|
OmpCompactionResult,
|
|
25
38
|
OmpExtensionUiResponse,
|
|
26
39
|
OmpImage,
|
|
@@ -35,7 +48,7 @@ import type {
|
|
|
35
48
|
OmpToolApprovalCancel,
|
|
36
49
|
OmpToolApprovalRequest,
|
|
37
50
|
} from "./omp-rpc";
|
|
38
|
-
import { buildOmpSpawnRequest } from "./omp-rpc";
|
|
51
|
+
import { buildOmpSpawnRequest, OmpRpcRequestRejectedError } from "./omp-rpc";
|
|
39
52
|
import {
|
|
40
53
|
BoundedStringSet,
|
|
41
54
|
boundedJsonBytes,
|
|
@@ -283,6 +296,7 @@ type ActiveTurn = {
|
|
|
283
296
|
terminalizing: boolean;
|
|
284
297
|
terminalization?: Promise<void>;
|
|
285
298
|
terminalOutcome?: TurnOutcome;
|
|
299
|
+
operationalTerminalStage?: "failed" | "unresolved";
|
|
286
300
|
terminalWake?: VoidDeferred;
|
|
287
301
|
steerReady: VoidDeferred;
|
|
288
302
|
steersInFlight: number;
|
|
@@ -938,6 +952,8 @@ export class OmpProviderSession {
|
|
|
938
952
|
private readonly pendingToolPermissions = new Map<string, PendingToolPermission>();
|
|
939
953
|
private readonly inFlightToolPermissions = new Map<string, PendingToolPermission>();
|
|
940
954
|
private readonly resolvedToolApprovalIds = new BoundedStringSet(MAX_TRACKED_ENTRY_IDS);
|
|
955
|
+
private unsupportedThinkingNoticePending = false;
|
|
956
|
+
private unsupportedThinkingNoticePublished = false;
|
|
941
957
|
|
|
942
958
|
private constructor(
|
|
943
959
|
id: string,
|
|
@@ -962,6 +978,7 @@ export class OmpProviderSession {
|
|
|
962
978
|
private readonly transitionNativeSession: NativeSessionTransition,
|
|
963
979
|
private readonly quarantineRewindCleanup: RewindCleanupQuarantine,
|
|
964
980
|
private readonly retireRewindSession: RewindSessionRetirement,
|
|
981
|
+
private readonly reportOperationalFailure: OmpOperationalFailureReporter,
|
|
965
982
|
scheduler: OmpTimelineScheduler = defaultOmpTimelineScheduler,
|
|
966
983
|
) {
|
|
967
984
|
this.id = id;
|
|
@@ -995,6 +1012,13 @@ export class OmpProviderSession {
|
|
|
995
1012
|
: null;
|
|
996
1013
|
this.bindRuntime(runtime);
|
|
997
1014
|
}
|
|
1015
|
+
private recordOperationalFailure(failure: OmpOperationalFailure): void {
|
|
1016
|
+
try {
|
|
1017
|
+
this.reportOperationalFailure(failure);
|
|
1018
|
+
} catch {
|
|
1019
|
+
// Diagnostics must never affect provider or session flow.
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
998
1022
|
get persistenceSessionId(): string | undefined {
|
|
999
1023
|
return this.persistSession ? this.nativeSessionId : undefined;
|
|
1000
1024
|
}
|
|
@@ -1021,6 +1045,7 @@ export class OmpProviderSession {
|
|
|
1021
1045
|
environment?: NodeJS.ProcessEnv,
|
|
1022
1046
|
mcpConnector?: OmpMcpConnector,
|
|
1023
1047
|
mcpInitializationTimeoutMs?: number,
|
|
1048
|
+
reportOperationalFailure: OmpOperationalFailureReporter = () => {},
|
|
1024
1049
|
): Promise<OmpProviderSession> {
|
|
1025
1050
|
const resumeSessionId = ompPersistenceSessionId(input);
|
|
1026
1051
|
if (resumeSessionId && !input.config.persist) {
|
|
@@ -1056,10 +1081,9 @@ export class OmpProviderSession {
|
|
|
1056
1081
|
}
|
|
1057
1082
|
const startOptions: OmpStartOptions = {
|
|
1058
1083
|
...normalizedConfig,
|
|
1059
|
-
//
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
: {}),
|
|
1084
|
+
// Thinking is authorized only after this runtime reports its exact model catalog.
|
|
1085
|
+
thinkingOption: undefined,
|
|
1086
|
+
...(resumeSessionId ? { systemPrompt: undefined, resumeSessionId } : {}),
|
|
1063
1087
|
signal,
|
|
1064
1088
|
environment,
|
|
1065
1089
|
};
|
|
@@ -1068,6 +1092,7 @@ export class OmpProviderSession {
|
|
|
1068
1092
|
connectMcp: mcpConnector,
|
|
1069
1093
|
signal,
|
|
1070
1094
|
initializationTimeoutMs: mcpInitializationTimeoutMs,
|
|
1095
|
+
reportOperationalFailure,
|
|
1071
1096
|
});
|
|
1072
1097
|
let native: OmpRuntimeSession | undefined;
|
|
1073
1098
|
let cleanupNativeSessionId: string | undefined;
|
|
@@ -1095,6 +1120,7 @@ export class OmpProviderSession {
|
|
|
1095
1120
|
() => ({ available: false, commands: [] }),
|
|
1096
1121
|
),
|
|
1097
1122
|
]);
|
|
1123
|
+
validateOmpModelIdentities(nativeModels);
|
|
1098
1124
|
if (effectiveConfig.persist && !native.canReplayHistory) {
|
|
1099
1125
|
throw new OmpPublicError("OMP session persistence requires negotiated RPC protocol v2");
|
|
1100
1126
|
}
|
|
@@ -1108,12 +1134,10 @@ export class OmpProviderSession {
|
|
|
1108
1134
|
if (resumeSessionId && initialState.sessionId !== resumeSessionId) {
|
|
1109
1135
|
throw new OmpPublicError("OMP resumed a different native session");
|
|
1110
1136
|
}
|
|
1111
|
-
const models = mapOmpModels(nativeModels, new OmpPublicDataSerializer(outputRedactionValues));
|
|
1112
|
-
const nativeModelsByPublicId = new Map(
|
|
1113
|
-
nativeModels.map((model) => [ompModelId(model), model] as const),
|
|
1114
|
-
);
|
|
1115
1137
|
if (!resumeSessionId && input.config.model) {
|
|
1116
|
-
const selected =
|
|
1138
|
+
const selected = selectOmpModels(nativeModels, state.model).find(
|
|
1139
|
+
(model) => ompModelId(model) === input.config.model,
|
|
1140
|
+
);
|
|
1117
1141
|
if (!selected) {
|
|
1118
1142
|
throw new OmpPublicError("OMP model is not advertised by the configured session runtime");
|
|
1119
1143
|
}
|
|
@@ -1122,9 +1146,29 @@ export class OmpProviderSession {
|
|
|
1122
1146
|
state = await native.getState();
|
|
1123
1147
|
}
|
|
1124
1148
|
}
|
|
1149
|
+
let currentModel = state.model
|
|
1150
|
+
? nativeModels.find(
|
|
1151
|
+
(model) => model.provider === state.model?.provider && model.id === state.model.id,
|
|
1152
|
+
)
|
|
1153
|
+
: undefined;
|
|
1154
|
+
if (state.model && !currentModel) {
|
|
1155
|
+
throw new OmpPublicError("OMP runtime selected an unadvertised model");
|
|
1156
|
+
}
|
|
1157
|
+
if (!resumeSessionId && input.config.thinkingOption !== undefined) {
|
|
1158
|
+
if (
|
|
1159
|
+
!thinkingForModel(currentModel).some(
|
|
1160
|
+
(option) => option.id === input.config.thinkingOption,
|
|
1161
|
+
)
|
|
1162
|
+
) {
|
|
1163
|
+
throw new OmpPublicError("OMP thinking level is unavailable for the selected model");
|
|
1164
|
+
}
|
|
1165
|
+
if (state.thinkingLevel !== input.config.thinkingOption) {
|
|
1166
|
+
await native.setThinkingLevel(input.config.thinkingOption);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1125
1169
|
const reconciledConfigRevision = bootstrapConfigRevision;
|
|
1126
1170
|
state = await native.getState();
|
|
1127
|
-
|
|
1171
|
+
currentModel = state.model
|
|
1128
1172
|
? nativeModels.find(
|
|
1129
1173
|
(model) => model.provider === state.model?.provider && model.id === state.model.id,
|
|
1130
1174
|
)
|
|
@@ -1132,21 +1176,21 @@ export class OmpProviderSession {
|
|
|
1132
1176
|
if (state.model && !currentModel) {
|
|
1133
1177
|
throw new OmpPublicError("OMP runtime selected an unadvertised model");
|
|
1134
1178
|
}
|
|
1179
|
+
const selectedNativeModels = selectOmpModels(nativeModels, state.model);
|
|
1180
|
+
const models = mapOmpModels(
|
|
1181
|
+
selectedNativeModels,
|
|
1182
|
+
new OmpPublicDataSerializer(outputRedactionValues),
|
|
1183
|
+
);
|
|
1184
|
+
const nativeModelsByPublicId = new Map(
|
|
1185
|
+
nativeModels.map((model) => [ompModelId(model), model] as const),
|
|
1186
|
+
);
|
|
1135
1187
|
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
|
-
}
|
|
1188
|
+
const applicableLevel = applicableThinkingLevel(currentModel, state.thinkingLevel);
|
|
1189
|
+
const committedThinkingLevel = thinkingOptions.some((option) => option.id === applicableLevel)
|
|
1190
|
+
? applicableLevel
|
|
1191
|
+
: undefined;
|
|
1192
|
+
const unsupportedThinkingLevel =
|
|
1193
|
+
applicableLevel !== undefined && committedThinkingLevel === undefined;
|
|
1150
1194
|
const configState: ProviderConfigState = {
|
|
1151
1195
|
...(state.model ? { model: ompModelId(state.model) } : {}),
|
|
1152
1196
|
mode: normalizedConfig.mode ?? "full",
|
|
@@ -1207,8 +1251,10 @@ export class OmpProviderSession {
|
|
|
1207
1251
|
transitionNativeSession,
|
|
1208
1252
|
quarantineRewindCleanup,
|
|
1209
1253
|
retireRewindSession,
|
|
1254
|
+
reportOperationalFailure,
|
|
1210
1255
|
scheduler,
|
|
1211
1256
|
);
|
|
1257
|
+
session.unsupportedThinkingNoticePending = unsupportedThinkingLevel;
|
|
1212
1258
|
|
|
1213
1259
|
if (bootstrapConfigRevision !== reconciledConfigRevision) {
|
|
1214
1260
|
session.configRefreshDirty = true;
|
|
@@ -1267,6 +1313,7 @@ export class OmpProviderSession {
|
|
|
1267
1313
|
: {}),
|
|
1268
1314
|
...(this.config.title ? { title: this.dataFilter.text(this.config.title, 256) } : {}),
|
|
1269
1315
|
});
|
|
1316
|
+
this.publishUnsupportedThinkingNotice();
|
|
1270
1317
|
this.emit({ type: "session.config", sessionId: this.id, config: this.configState });
|
|
1271
1318
|
if (this.replayHistoryOnOpen) await this.replayHistory(true);
|
|
1272
1319
|
this.publishCommands(this.commandCatalog);
|
|
@@ -1274,6 +1321,22 @@ export class OmpProviderSession {
|
|
|
1274
1321
|
this.readyPublished = true;
|
|
1275
1322
|
if (this.configRefreshDirty) this.scheduleCommittedConfigRefresh();
|
|
1276
1323
|
}
|
|
1324
|
+
private publishUnsupportedThinkingNotice(): void {
|
|
1325
|
+
if (!this.unsupportedThinkingNoticePending || this.unsupportedThinkingNoticePublished) return;
|
|
1326
|
+
this.unsupportedThinkingNoticePending = false;
|
|
1327
|
+
this.unsupportedThinkingNoticePublished = true;
|
|
1328
|
+
this.emit({
|
|
1329
|
+
type: "session.notice",
|
|
1330
|
+
sessionId: this.id,
|
|
1331
|
+
notice: {
|
|
1332
|
+
id: "omp:unsupported-thinking-level",
|
|
1333
|
+
severity: "warning",
|
|
1334
|
+
title: "OMP thinking level unavailable",
|
|
1335
|
+
description:
|
|
1336
|
+
"OMP reported a thinking level that the active model does not advertise. Paseo omitted it from the session configuration.",
|
|
1337
|
+
},
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1277
1340
|
|
|
1278
1341
|
private usageFrom(
|
|
1279
1342
|
state: OmpSessionState | undefined,
|
|
@@ -1592,8 +1655,24 @@ export class OmpProviderSession {
|
|
|
1592
1655
|
replay.signal,
|
|
1593
1656
|
);
|
|
1594
1657
|
messages = transcript.messages;
|
|
1658
|
+
if (transcript.imageReplayWarning) {
|
|
1659
|
+
this.emit({
|
|
1660
|
+
type: "timeline.item",
|
|
1661
|
+
sessionId: this.id,
|
|
1662
|
+
item: {
|
|
1663
|
+
id: "omp:replay-image-unavailable",
|
|
1664
|
+
type: "notification",
|
|
1665
|
+
level: "warning",
|
|
1666
|
+
message: "OMP skipped one or more unavailable images while replaying this session.",
|
|
1667
|
+
},
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1595
1670
|
} catch (error) {
|
|
1596
1671
|
if (replay.signal.aborted) throw error;
|
|
1672
|
+
this.recordOperationalFailure({
|
|
1673
|
+
category: "replay-recovery",
|
|
1674
|
+
stage: "persisted-replay",
|
|
1675
|
+
});
|
|
1597
1676
|
this.emit({
|
|
1598
1677
|
type: "timeline.item",
|
|
1599
1678
|
sessionId: this.id,
|
|
@@ -1667,19 +1746,33 @@ export class OmpProviderSession {
|
|
|
1667
1746
|
throw new OmpPublicError("OMP conversation rewind requires negotiated RPC protocol v2");
|
|
1668
1747
|
}
|
|
1669
1748
|
const entryId = this.projector.resolveRevertToken(input.token);
|
|
1670
|
-
const
|
|
1671
|
-
runtime.getBranchMessages(),
|
|
1672
|
-
runtime.getState(),
|
|
1673
|
-
]);
|
|
1749
|
+
const beforeState = await runtime.getState();
|
|
1674
1750
|
this.requireCurrentRuntime(runtime, generation);
|
|
1675
1751
|
if (this.activeTurn || beforeState.isStreaming || beforeState.isCompacting) {
|
|
1676
1752
|
throw new OmpPublicError("Cannot rewind the OMP conversation while a turn is active");
|
|
1677
1753
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1754
|
+
const beforeAdvertisedModel = beforeState.model
|
|
1755
|
+
? this.nativeModelsByPublicId.get(ompModelId(beforeState.model))
|
|
1756
|
+
: undefined;
|
|
1757
|
+
if (beforeState.model && !beforeAdvertisedModel) {
|
|
1758
|
+
throw new OmpPublicError("OMP runtime selected an unadvertised model");
|
|
1680
1759
|
}
|
|
1760
|
+
const restorableThinkingLevel = thinkingForModel(beforeAdvertisedModel).some(
|
|
1761
|
+
(option) => option.id === beforeState.thinkingLevel,
|
|
1762
|
+
)
|
|
1763
|
+
? beforeState.thinkingLevel
|
|
1764
|
+
: undefined;
|
|
1681
1765
|
branchMutationPossible = true;
|
|
1682
|
-
|
|
1766
|
+
let result: OmpBranchResult;
|
|
1767
|
+
try {
|
|
1768
|
+
result = await runtime.branch(entryId);
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
if (error instanceof OmpRpcRequestRejectedError) {
|
|
1771
|
+
branchMutationPossible = false;
|
|
1772
|
+
throw new OmpPublicError("OMP conversation rewind token is stale");
|
|
1773
|
+
}
|
|
1774
|
+
throw error;
|
|
1775
|
+
}
|
|
1683
1776
|
this.requireCurrentRuntime(runtime, generation);
|
|
1684
1777
|
if (result.cancelled) {
|
|
1685
1778
|
branchMutationPossible = false;
|
|
@@ -1711,13 +1804,10 @@ export class OmpProviderSession {
|
|
|
1711
1804
|
}
|
|
1712
1805
|
await runtime.setModel(beforeState.model.provider, beforeState.model.id);
|
|
1713
1806
|
}
|
|
1714
|
-
if (thinkingChanged) {
|
|
1715
|
-
|
|
1716
|
-
throw new OmpPublicError("OMP changed thinking level while rewinding the conversation");
|
|
1717
|
-
}
|
|
1718
|
-
await runtime.setThinkingLevel(beforeState.thinkingLevel);
|
|
1807
|
+
if (thinkingChanged && restorableThinkingLevel) {
|
|
1808
|
+
await runtime.setThinkingLevel(restorableThinkingLevel);
|
|
1719
1809
|
}
|
|
1720
|
-
if (modelChanged || thinkingChanged) {
|
|
1810
|
+
if (modelChanged || (thinkingChanged && restorableThinkingLevel)) {
|
|
1721
1811
|
state = await runtime.getState();
|
|
1722
1812
|
this.requireCurrentRuntime(runtime, generation);
|
|
1723
1813
|
}
|
|
@@ -1725,7 +1815,7 @@ export class OmpProviderSession {
|
|
|
1725
1815
|
state.sessionId !== this.nativeSessionId ||
|
|
1726
1816
|
state.model?.provider !== beforeState.model?.provider ||
|
|
1727
1817
|
state.model?.id !== beforeState.model?.id ||
|
|
1728
|
-
state.thinkingLevel !==
|
|
1818
|
+
(restorableThinkingLevel !== undefined && state.thinkingLevel !== restorableThinkingLevel)
|
|
1729
1819
|
) {
|
|
1730
1820
|
throw new OmpPublicError("OMP did not preserve session configuration while rewinding");
|
|
1731
1821
|
}
|
|
@@ -1737,6 +1827,7 @@ export class OmpProviderSession {
|
|
|
1737
1827
|
this.publishCommittedConfig(state, runtime, generation);
|
|
1738
1828
|
this.emit({ type: "request.completed", requestId: input.requestId });
|
|
1739
1829
|
} catch (error) {
|
|
1830
|
+
this.recordOperationalFailure({ category: "replay-recovery", stage: "rewind" });
|
|
1740
1831
|
const failure = branchMutationPossible
|
|
1741
1832
|
? { message: "OMP conversation rewind left native state indeterminate" }
|
|
1742
1833
|
: providerError(error, "OMP conversation rewind failed");
|
|
@@ -2325,7 +2416,10 @@ export class OmpProviderSession {
|
|
|
2325
2416
|
const targetModel = targetModelId
|
|
2326
2417
|
? this.nativeModelsByPublicId.get(targetModelId)
|
|
2327
2418
|
: undefined;
|
|
2328
|
-
|
|
2419
|
+
const targetModelPublished = this.configState.models.some(
|
|
2420
|
+
(model) => model.id === targetModelId,
|
|
2421
|
+
);
|
|
2422
|
+
if (input.changes.model !== undefined && (!targetModel || !targetModelPublished)) {
|
|
2329
2423
|
throw new OmpPublicError("OMP model selection is unavailable");
|
|
2330
2424
|
}
|
|
2331
2425
|
if (
|
|
@@ -2572,13 +2666,18 @@ export class OmpProviderSession {
|
|
|
2572
2666
|
throw new OmpCatalogEscape("OMP runtime selected an unadvertised model");
|
|
2573
2667
|
}
|
|
2574
2668
|
const thinkingOptions = thinkingForModel(advertisedModel);
|
|
2575
|
-
const
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
) {
|
|
2580
|
-
|
|
2669
|
+
const applicableLevel = applicableThinkingLevel(advertisedModel, state.thinkingLevel);
|
|
2670
|
+
const committedThinkingLevel = thinkingOptions.some((option) => option.id === applicableLevel)
|
|
2671
|
+
? applicableLevel
|
|
2672
|
+
: undefined;
|
|
2673
|
+
if (applicableLevel !== undefined && committedThinkingLevel === undefined) {
|
|
2674
|
+
this.unsupportedThinkingNoticePending = true;
|
|
2675
|
+
this.publishUnsupportedThinkingNotice();
|
|
2581
2676
|
}
|
|
2677
|
+
const models = mapOmpModels(
|
|
2678
|
+
selectOmpModels([...this.nativeModelsByPublicId.values()], state.model),
|
|
2679
|
+
this.dataFilter,
|
|
2680
|
+
);
|
|
2582
2681
|
this.configRefreshAttempts = 0;
|
|
2583
2682
|
this.cancelConfigRefreshRetry();
|
|
2584
2683
|
const nextConfig: ProviderConfigState = {
|
|
@@ -2587,12 +2686,15 @@ export class OmpProviderSession {
|
|
|
2587
2686
|
...(committedThinkingLevel
|
|
2588
2687
|
? { thinkingOption: committedThinkingLevel }
|
|
2589
2688
|
: { thinkingOption: undefined }),
|
|
2689
|
+
models,
|
|
2590
2690
|
thinkingOptions,
|
|
2591
2691
|
};
|
|
2592
2692
|
const changed =
|
|
2593
2693
|
force ||
|
|
2594
2694
|
nextConfig.model !== this.configState.model ||
|
|
2595
2695
|
nextConfig.thinkingOption !== this.configState.thinkingOption ||
|
|
2696
|
+
nextConfig.models.some((model, index) => model.id !== this.configState.models[index]?.id) ||
|
|
2697
|
+
this.configState.models.length !== nextConfig.models.length ||
|
|
2596
2698
|
nextConfig.thinkingOptions.length !== this.configState.thinkingOptions.length ||
|
|
2597
2699
|
nextConfig.thinkingOptions.some(
|
|
2598
2700
|
(option, index) =>
|
|
@@ -2843,7 +2945,13 @@ export class OmpProviderSession {
|
|
|
2843
2945
|
|
|
2844
2946
|
private async recoverRuntime(): Promise<void> {
|
|
2845
2947
|
if (!this.runtimeDead) return;
|
|
2846
|
-
this.recoveryPromise ??= this.startRecovery()
|
|
2948
|
+
this.recoveryPromise ??= this.startRecovery().catch((error) => {
|
|
2949
|
+
this.recordOperationalFailure({
|
|
2950
|
+
category: "replay-recovery",
|
|
2951
|
+
stage: "runtime-recovery",
|
|
2952
|
+
});
|
|
2953
|
+
throw error;
|
|
2954
|
+
});
|
|
2847
2955
|
try {
|
|
2848
2956
|
await this.recoveryPromise;
|
|
2849
2957
|
} finally {
|
|
@@ -2913,13 +3021,6 @@ export class OmpProviderSession {
|
|
|
2913
3021
|
if (state.model && !advertisedModel) {
|
|
2914
3022
|
throw new Error("OMP recovered with an unadvertised model");
|
|
2915
3023
|
}
|
|
2916
|
-
const recoveredThinkingLevel = applicableThinkingLevel(advertisedModel, state.thinkingLevel);
|
|
2917
|
-
if (
|
|
2918
|
-
recoveredThinkingLevel &&
|
|
2919
|
-
!thinkingForModel(advertisedModel).some((option) => option.id === recoveredThinkingLevel)
|
|
2920
|
-
) {
|
|
2921
|
-
throw new Error("OMP recovered with an unsupported thinking level");
|
|
2922
|
-
}
|
|
2923
3024
|
if (this.closed) throw new Error("OMP session closed while runtime recovery was pending");
|
|
2924
3025
|
if (!this.hostTools.isBoundTo(recovered)) {
|
|
2925
3026
|
throw new Error("OMP host tool bridge detached during recovery");
|
|
@@ -3073,6 +3174,10 @@ export class OmpProviderSession {
|
|
|
3073
3174
|
try {
|
|
3074
3175
|
this.subsessions?.handle(event);
|
|
3075
3176
|
} catch {
|
|
3177
|
+
this.recordOperationalFailure({
|
|
3178
|
+
category: "tool-projector",
|
|
3179
|
+
stage: "subsession-projector",
|
|
3180
|
+
});
|
|
3076
3181
|
this.handleRuntimeFailure("OMP subagent event processing failed");
|
|
3077
3182
|
}
|
|
3078
3183
|
return;
|
|
@@ -3307,6 +3412,10 @@ export class OmpProviderSession {
|
|
|
3307
3412
|
try {
|
|
3308
3413
|
this.subsessions?.observeSessionEvent(this.id, event);
|
|
3309
3414
|
} catch {
|
|
3415
|
+
this.recordOperationalFailure({
|
|
3416
|
+
category: "tool-projector",
|
|
3417
|
+
stage: "subsession-projector",
|
|
3418
|
+
});
|
|
3310
3419
|
this.handleRuntimeFailure("OMP subagent dispatch tracking failed");
|
|
3311
3420
|
return;
|
|
3312
3421
|
}
|
|
@@ -3363,7 +3472,15 @@ export class OmpProviderSession {
|
|
|
3363
3472
|
}
|
|
3364
3473
|
}
|
|
3365
3474
|
if (isNativeTurnActivity(event)) this.markAgentEvidence(turn);
|
|
3366
|
-
|
|
3475
|
+
try {
|
|
3476
|
+
this.projector.project(event, turn.turnId);
|
|
3477
|
+
} catch (error) {
|
|
3478
|
+
this.recordOperationalFailure({
|
|
3479
|
+
category: "tool-projector",
|
|
3480
|
+
stage: "timeline-projector",
|
|
3481
|
+
});
|
|
3482
|
+
throw error;
|
|
3483
|
+
}
|
|
3367
3484
|
if (event.type === "tool_execution_end") this.resumeDeferredAgentEnd();
|
|
3368
3485
|
}
|
|
3369
3486
|
|
|
@@ -4432,11 +4549,13 @@ export class OmpProviderSession {
|
|
|
4432
4549
|
const state = await this.boundedTerminalState(turn, FINAL_USAGE_WAIT_MS);
|
|
4433
4550
|
if (turn.terminal || this.activeTurn !== turn || turn.deferredAgentEnd !== candidate) return;
|
|
4434
4551
|
if (!state) {
|
|
4552
|
+
turn.operationalTerminalStage = "unresolved";
|
|
4435
4553
|
this.handleRuntimeFailure("OMP agent_end state could not be confirmed");
|
|
4436
4554
|
return;
|
|
4437
4555
|
}
|
|
4438
4556
|
turn.deferredAgentEnd = undefined;
|
|
4439
4557
|
if (state.isStreaming || state.isCompacting) return;
|
|
4558
|
+
turn.operationalTerminalStage = "unresolved";
|
|
4440
4559
|
await this.finishTurn(turn, "failed", {
|
|
4441
4560
|
message: "OMP unkeyed agent_end could not be correlated to the current prompt",
|
|
4442
4561
|
});
|
|
@@ -4517,6 +4636,7 @@ export class OmpProviderSession {
|
|
|
4517
4636
|
) {
|
|
4518
4637
|
void this.completeAgentEnd(turn, candidate.event);
|
|
4519
4638
|
} else {
|
|
4639
|
+
turn.operationalTerminalStage = "unresolved";
|
|
4520
4640
|
this.handleRuntimeFailure("OMP agent_end state could not be confirmed");
|
|
4521
4641
|
}
|
|
4522
4642
|
}, AGENT_END_SETTLE_MS);
|
|
@@ -4541,8 +4661,13 @@ export class OmpProviderSession {
|
|
|
4541
4661
|
turn.terminalizing = false;
|
|
4542
4662
|
turn.deferredAgentEnd = candidate;
|
|
4543
4663
|
void this.subsessions.reconcile(this.runtime).catch(() => {
|
|
4664
|
+
this.recordOperationalFailure({
|
|
4665
|
+
category: "tool-projector",
|
|
4666
|
+
stage: "subsession-projector",
|
|
4667
|
+
});
|
|
4544
4668
|
if (!turn.terminal && this.activeTurn === turn) {
|
|
4545
|
-
this.
|
|
4669
|
+
this.subsessions?.terminalize("failed");
|
|
4670
|
+
this.resumeDeferredAgentEnd();
|
|
4546
4671
|
}
|
|
4547
4672
|
});
|
|
4548
4673
|
return true;
|
|
@@ -4625,6 +4750,7 @@ export class OmpProviderSession {
|
|
|
4625
4750
|
return;
|
|
4626
4751
|
}
|
|
4627
4752
|
if (turn.userEchoObserved) {
|
|
4753
|
+
turn.operationalTerminalStage = "unresolved";
|
|
4628
4754
|
this.handleRuntimeFailure("OMP agent_end state could not be confirmed");
|
|
4629
4755
|
return;
|
|
4630
4756
|
}
|
|
@@ -4684,6 +4810,7 @@ export class OmpProviderSession {
|
|
|
4684
4810
|
await this.finishTurn(turn, "completed");
|
|
4685
4811
|
return;
|
|
4686
4812
|
}
|
|
4813
|
+
turn.operationalTerminalStage = outcome === "failed" ? "failed" : "unresolved";
|
|
4687
4814
|
const error =
|
|
4688
4815
|
outcome === "failed" ? "OMP assistant turn failed" : unknownTerminalOutcomeError(event, turn);
|
|
4689
4816
|
this.subsessions?.terminalize("failed");
|
|
@@ -4826,6 +4953,12 @@ export class OmpProviderSession {
|
|
|
4826
4953
|
usageSampled: true,
|
|
4827
4954
|
};
|
|
4828
4955
|
}
|
|
4956
|
+
if (outcome.state === "failed") {
|
|
4957
|
+
this.recordOperationalFailure({
|
|
4958
|
+
category: "terminal-outcome",
|
|
4959
|
+
stage: turn.operationalTerminalStage ?? "failed",
|
|
4960
|
+
});
|
|
4961
|
+
}
|
|
4829
4962
|
this.imageMaterializer.clear();
|
|
4830
4963
|
turn.terminal = true;
|
|
4831
4964
|
if (this.usageSample?.turn === turn) this.usageSample = null;
|
|
@@ -4839,7 +4972,15 @@ export class OmpProviderSession {
|
|
|
4839
4972
|
}
|
|
4840
4973
|
this.publishPendingUsers(turn);
|
|
4841
4974
|
this.resolveTurnPermissions(turn.turnId);
|
|
4842
|
-
|
|
4975
|
+
try {
|
|
4976
|
+
this.projector.finishTurn(turn.turnId, preserveCompactions);
|
|
4977
|
+
} catch (error) {
|
|
4978
|
+
this.recordOperationalFailure({
|
|
4979
|
+
category: "tool-projector",
|
|
4980
|
+
stage: "timeline-projector",
|
|
4981
|
+
});
|
|
4982
|
+
throw error;
|
|
4983
|
+
}
|
|
4843
4984
|
this.unclaimedBranchEntries.length = 0;
|
|
4844
4985
|
this.emit({
|
|
4845
4986
|
type: "session.turn",
|
|
@@ -4897,6 +5038,7 @@ export class OmpProviderSession {
|
|
|
4897
5038
|
this.publishPromptResult(turn, { type: "failed", error: { message } });
|
|
4898
5039
|
if (turn.started) void this.finishTurn(turn, "failed", { message }, true, true);
|
|
4899
5040
|
else {
|
|
5041
|
+
this.recordOperationalFailure({ category: "terminal-outcome", stage: "failed" });
|
|
4900
5042
|
turn.steerReady.resolve();
|
|
4901
5043
|
turn.terminal = true;
|
|
4902
5044
|
this.projector.finishTurn(turn.turnId);
|