pi-openai-toolkit 0.19.0 → 0.20.0

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/src/config/v2.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * {
14
14
  * "schemaVersion": 2,
15
15
  * "$schema": "…metadata only…",
16
- * "defaults": { "context": …, "webSearch": …, "imageGeneration": …, "autoMode": … },
16
+ * "defaults": { "context": …, "webSearch": …, "imageGeneration": …, "responsesWebSocket": …, "autoMode": … },
17
17
  * "models": { "provider/model-id": { "context": …, "webSearch": …, "autoMode": …, "compatibility": … } },
18
18
  * "diagnostics": { … }
19
19
  * }
@@ -29,7 +29,8 @@
29
29
  * raw values and unknown subtrees are never copied into diagnostics.
30
30
  * - `defaults.compatibility` is not a recognized scope: the standard transport is internal and
31
31
  * the Codex gateway transport stays an exact-model opt-in.
32
- * - Image output settings (`defaults.imageGeneration`) and `diagnostics` are global only.
32
+ * - Image output settings (`defaults.imageGeneration`), Responses WebSocket transport
33
+ * (`defaults.responsesWebSocket`), and `diagnostics` are global only.
33
34
  * - Exact model keys and model references use the legacy `provider/model-id` grammar: no
34
35
  * whitespace, globs or bracket patterns. Keys are trimmed like the legacy decoder, and two
35
36
  * raw keys that normalize to the same key are rejected instead of resolved last-wins.
@@ -120,7 +121,7 @@ const DIAGNOSTIC_LEVELS = ["error", "warn", "info", "debug"] as const;
120
121
  const LEGACY_ROOT_KEYS = ["compaction", "webSearch", "imageGeneration", "autoMode"] as const;
121
122
 
122
123
  const ROOT_KEYS = new Set(["$schema", "schemaVersion", "defaults", "models", "diagnostics"]);
123
- const DEFAULTS_KEYS = new Set(["context", "webSearch", "imageGeneration", "autoMode"]);
124
+ const DEFAULTS_KEYS = new Set(["context", "webSearch", "imageGeneration", "responsesWebSocket", "autoMode"]);
124
125
  const MODEL_OVERRIDE_KEYS = new Set(["context", "webSearch", "autoMode", "compatibility"]);
125
126
  const CONTEXT_KEYS = new Set(["mode", "remoteCompaction", "nativeFallback", "remoteWindows"]);
126
127
  const REMOTE_COMPACTION_KEYS = new Set(["model", "inputSource", "allowContinuityBreak", "apis"]);
@@ -128,6 +129,7 @@ const NATIVE_FALLBACK_KEYS = new Set(["enabled", "model", "thinkingLevel"]);
128
129
  const REMOTE_WINDOWS_KEYS = new Set(["leaveManagedMode", "reminderThresholdPercent"]);
129
130
  const WEB_SEARCH_KEYS = new Set(["route"]);
130
131
  const IMAGE_GENERATION_KEYS = new Set(["enabled", "defaultModel", "allowedModels"]);
132
+ const RESPONSES_WEBSOCKET_KEYS = new Set(["enabled"]);
131
133
  const AUTO_MODE_KEYS = new Set([
132
134
  "available",
133
135
  "reviewerModel",
@@ -170,6 +172,7 @@ const FEATURE_NAMES: ReadonlyMap<string, ConfigFeature> = new Map<string, Config
170
172
  ["compaction", "context"],
171
173
  ["webSearch", "webSearch"],
172
174
  ["imageGeneration", "imageGeneration"],
175
+ ["responsesWebSocket", "responsesWebSocket"],
173
176
  ["autoMode", "autoMode"],
174
177
  ["compatibility", "compatibility"],
175
178
  ["diagnostics", "diagnostics"],
@@ -194,6 +197,7 @@ const POLICY_LEAVES: readonly { leaf: string; feature: ConfigFeature }[] = [
194
197
  { leaf: "imageGeneration.enabled", feature: "imageGeneration" },
195
198
  { leaf: "imageGeneration.defaultModel", feature: "imageGeneration" },
196
199
  { leaf: "imageGeneration.allowedModels", feature: "imageGeneration" },
200
+ { leaf: "responsesWebSocket.enabled", feature: "responsesWebSocket" },
197
201
  { leaf: "autoMode.available", feature: "autoMode" },
198
202
  { leaf: "autoMode.reviewerModel", feature: "autoMode" },
199
203
  { leaf: "autoMode.gate", feature: "autoMode" },
@@ -841,6 +845,25 @@ function applyImageGenerationSettings(
841
845
  );
842
846
  }
843
847
 
848
+ function applyResponsesWebSocketSettings(
849
+ raw: Record<string, unknown>,
850
+ scope: Scope,
851
+ state: ResolutionState,
852
+ ): void {
853
+ reportUnknownKeys(raw, RESPONSES_WEBSOCKET_KEYS, scope, state, "responsesWebSocket");
854
+
855
+ applyField(
856
+ raw,
857
+ { key: "enabled", leaf: "responsesWebSocket.enabled", feature: "responsesWebSocket" },
858
+ scope,
859
+ state,
860
+ readBoolean,
861
+ (value) => {
862
+ state.policy.responsesWebSocket.enabled = value;
863
+ },
864
+ );
865
+ }
866
+
844
867
  function applyAutoModeSettings(
845
868
  raw: Record<string, unknown>,
846
869
  scope: Scope,
@@ -1149,6 +1172,15 @@ function applyImageGenerationScope(
1149
1172
  if (group) applyImageGenerationSettings(group.raw, group.scope, state);
1150
1173
  }
1151
1174
 
1175
+ function applyResponsesWebSocketScope(
1176
+ container: Record<string, unknown>,
1177
+ scope: Scope,
1178
+ state: ResolutionState,
1179
+ ): void {
1180
+ const group = openGroup(container, "responsesWebSocket", scope, state, "responsesWebSocket");
1181
+ if (group) applyResponsesWebSocketSettings(group.raw, group.scope, state);
1182
+ }
1183
+
1152
1184
  function applyAutoModeScope(
1153
1185
  container: Record<string, unknown>,
1154
1186
  scope: Scope,
@@ -1190,6 +1222,7 @@ function applyDefaults(doc: Record<string, unknown>, state: ResolutionState): vo
1190
1222
  applyContextScope(raw, scope, state);
1191
1223
  applyWebSearchScope(raw, scope, state);
1192
1224
  applyImageGenerationScope(raw, scope, state);
1225
+ applyResponsesWebSocketScope(raw, scope, state);
1193
1226
  applyAutoModeScope(raw, scope, state);
1194
1227
  }
1195
1228
 
package/src/config.ts CHANGED
@@ -208,6 +208,7 @@ function fromLegacy(loaded: LoadedToolkitConfig, model: WebSearchModel | undefin
208
208
  defaultModel: config.imageGeneration.models[0],
209
209
  allowedModels: [...config.imageGeneration.models],
210
210
  };
211
+ policy.responsesWebSocket = { enabled: false };
211
212
  const { enabled, models, reviewerModel, classifier, ...auto } = config.autoMode;
212
213
  policy.autoMode = {
213
214
  ...structuredClone(auto),
@@ -0,0 +1,32 @@
1
+ import { getApiProvider, registerApiProvider, unregisterApiProviders } from "@earendil-works/pi-ai/compat";
2
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ import { loadToolkitConfig, resolveToolkitConfig } from "../config";
4
+ import { createResponsesWebSocketProvider } from "./transport";
5
+
6
+ const SOURCE_ID = "pi-openai-toolkit:openai-responses-websocket";
7
+
8
+ /**
9
+ * Add the transport branch at the API-adapter boundary instead of registering
10
+ * a provider. Existing provider ids, models, credentials, and base URLs remain
11
+ * untouched; only openai-responses models eligible for transport=auto/websocket
12
+ * are handled by the wrapper.
13
+ */
14
+ export function installResponsesWebSocketTransport(
15
+ loadConfig: typeof loadToolkitConfig = loadToolkitConfig,
16
+ ): boolean {
17
+ // Re-installation can happen during Pi reloads. Remove only this extension's prior
18
+ // adapter so a config change cannot leave a stale enabled wrapper behind.
19
+ unregisterApiProviders(SOURCE_ID);
20
+ const resolved = resolveToolkitConfig(loadConfig());
21
+ if (!resolved.policy.responsesWebSocket.enabled || resolved.invalidFeatures.includes("responsesWebSocket")) {
22
+ return false;
23
+ }
24
+ const original = getApiProvider("openai-responses");
25
+ if (!original) return false;
26
+ registerApiProvider(createResponsesWebSocketProvider(original, true), SOURCE_ID);
27
+ return true;
28
+ }
29
+
30
+ export default function responsesWebSocketExtension(_pi: ExtensionAPI): void {
31
+ installResponsesWebSocketTransport();
32
+ }