pi-openai-toolkit 0.20.3 → 0.20.4

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 CHANGED
@@ -9,7 +9,7 @@ Remote context windows, Responses compaction, web search, image generation, and
9
9
 
10
10
  ## Install and start
11
11
 
12
- Requires Node.js **22.19.0+** and Pi **0.87.0+**. Current development dependencies pin Pi **0.99.1**; backend features also depend on the selected model, API, and account access.
12
+ Requires Node.js **22.19.0+** and Pi **0.87.0+**. Current development dependencies pin Pi **1.0.0**; backend features also depend on the selected model, API, and account access.
13
13
 
14
14
  ```bash
15
15
  pi install npm:pi-openai-toolkit
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  ## 安装与快速开始
11
11
 
12
- 需要 Node.js **22.19.0+**、Pi **0.87.0+**。当前开发依赖锁定 Pi **0.99.1**;后端功能是否可用,还取决于所选模型、接口和账号权限。
12
+ 需要 Node.js **22.19.0+**、Pi **0.87.0+**。当前开发依赖锁定 Pi **1.0.0**;后端功能是否可用,还取决于所选模型、接口和账号权限。
13
13
 
14
14
  ```bash
15
15
  pi install npm:pi-openai-toolkit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-openai-toolkit",
3
- "version": "0.20.3",
3
+ "version": "0.20.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -129,10 +129,10 @@
129
129
  "@earendil-works/pi-tui": ">=0.87.0"
130
130
  },
131
131
  "devDependencies": {
132
- "@earendil-works/pi-agent-core": "0.99.1",
133
- "@earendil-works/pi-ai": "0.99.1",
134
- "@earendil-works/pi-coding-agent": "0.99.1",
135
- "@earendil-works/pi-tui": "0.99.1",
132
+ "@earendil-works/pi-agent-core": "1.0.0",
133
+ "@earendil-works/pi-ai": "1.0.0",
134
+ "@earendil-works/pi-coding-agent": "1.0.0",
135
+ "@earendil-works/pi-tui": "1.0.0",
136
136
  "@types/node": "24.12.4",
137
137
  "bun": "1.2.23",
138
138
  "typescript": "5.9.3"
@@ -5,6 +5,7 @@ import { resolveToolkitConfig, type ResolvedToolkitConfig } from "../config";
5
5
  import { isAutoModeEligible } from "../auto-mode/policy";
6
6
  import { protectAutoModeSelection } from "../auto-mode/model-selection-guard";
7
7
  import { parseModelSpec } from "../runtime";
8
+ import { THINKING_LEVELS } from "../types";
8
9
  import { resolveCodexContextProvider } from "./codex-provider";
9
10
  import { findLatestNotesCheckpointSinceBoundary, findLatestWindowBoundaryEntry, type CodexContextWindowManager } from "./window-manager";
10
11
  import { isNonEmptyString, isRecord, type CodexContextProvider, type ContextWindowIdentity } from "./types";
@@ -14,7 +15,6 @@ export const MANUAL_COMPACT_ENTRY_TYPE = "pi-openai-toolkit:manual-compact";
14
15
  const PHASES = ["requested", "switching", "checkpointing", "rollover-scheduled", "restoring", "restore-needed", "completed", "failed", "cancelled", "superseded"] as const;
15
16
  type Phase = typeof PHASES[number];
16
17
  const TERMINAL = new Set<Phase>(["completed", "failed", "cancelled", "superseded"]);
17
- const THINKING = new Set<string>(["off", "minimal", "low", "medium", "high", "xhigh"]);
18
18
  const REQUIRED_TOOLS = ["notes", "history", "new_context", "get_context_remaining"];
19
19
 
20
20
  /** Non-LLM recovery state. No auth, model object, transcript or note text belongs here. */
@@ -36,7 +36,7 @@ export function decodeManualCompactRecord(value: unknown): ManualCompactRecord |
36
36
  || ![value.operationId, value.sessionId, value.sourceWindowId, value.anchorId].every(isNonEmptyString)
37
37
  || typeof value.originalModel !== "string" || !parseModelSpec(value.originalModel)
38
38
  || typeof value.targetModel !== "string" || !parseModelSpec(value.targetModel)
39
- || typeof value.originalThinking !== "string" || !THINKING.has(value.originalThinking)
39
+ || typeof value.originalThinking !== "string" || !(THINKING_LEVELS as readonly string[]).includes(value.originalThinking)
40
40
  || !PHASES.includes(value.phase as Phase)
41
41
  || (value.targetWindowId !== undefined && !isNonEmptyString(value.targetWindowId))) return undefined;
42
42
  return value as ManualCompactRecord;
@@ -4,7 +4,22 @@ import { loadToolkitConfig, resolveToolkitConfig } from "../config";
4
4
  import { createResponsesWebSocketProvider } from "./transport";
5
5
 
6
6
  const SOURCE_ID = "pi-openai-toolkit:openai-responses-websocket";
7
- let baseProvider: ReturnType<typeof getApiProvider>;
7
+ type ApiProviderInternal = NonNullable<ReturnType<typeof getApiProvider>>;
8
+ type ResponsesWebSocketRegistryState = {
9
+ baseProvider?: ApiProviderInternal;
10
+ installedProvider?: ApiProviderInternal;
11
+ };
12
+
13
+ const REGISTRY_STATE_KEY = Symbol.for("pi-openai-toolkit.responses-websocket.registry");
14
+
15
+ function registryState(): ResponsesWebSocketRegistryState {
16
+ const global = globalThis as typeof globalThis & { [key: symbol]: ResponsesWebSocketRegistryState | undefined };
17
+ return global[REGISTRY_STATE_KEY] ??= {};
18
+ }
19
+
20
+ function rememberInstalledProvider(state: ResponsesWebSocketRegistryState): void {
21
+ state.installedProvider = getApiProvider("openai-responses");
22
+ }
8
23
 
9
24
  /**
10
25
  * Add the transport branch at the API-adapter boundary instead of registering
@@ -15,16 +30,31 @@ let baseProvider: ReturnType<typeof getApiProvider>;
15
30
  export function installResponsesWebSocketTransport(
16
31
  loadConfig: typeof loadToolkitConfig = loadToolkitConfig,
17
32
  ): boolean {
33
+ const state = registryState();
34
+ const currentProvider = getApiProvider("openai-responses");
35
+ const ownsCurrentProvider = currentProvider !== undefined && currentProvider === state.installedProvider;
36
+ if (currentProvider && !ownsCurrentProvider) state.baseProvider = currentProvider;
37
+ const baseProvider = state.baseProvider ?? currentProvider;
38
+ const resolved = resolveToolkitConfig(loadConfig());
39
+
18
40
  // Re-installation can happen during Pi reloads. Remove only this extension's prior
19
- // adapter so a config change cannot leave a stale enabled wrapper behind.
41
+ // adapter so a config change cannot leave a stale enabled wrapper behind. When the
42
+ // current entry is ours, the saved base is re-registered as a pass-through below
43
+ // for disabled config; this keeps the compat registry populated without resetting
44
+ // providers owned by other extensions.
20
45
  unregisterApiProviders(SOURCE_ID);
21
- const resolved = resolveToolkitConfig(loadConfig());
22
- if (!resolved.policy.responsesWebSocket.enabled || resolved.invalidFeatures.includes("responsesWebSocket")) {
46
+ state.installedProvider = undefined;
47
+ const enabled = resolved.policy.responsesWebSocket.enabled && !resolved.invalidFeatures.includes("responsesWebSocket");
48
+ if (!enabled) {
49
+ if ((ownsCurrentProvider || !currentProvider) && baseProvider) {
50
+ registerApiProvider(baseProvider, SOURCE_ID);
51
+ rememberInstalledProvider(state);
52
+ }
23
53
  return false;
24
54
  }
25
- baseProvider ??= getApiProvider("openai-responses");
26
55
  if (!baseProvider) return false;
27
56
  registerApiProvider(createResponsesWebSocketProvider(baseProvider, true), SOURCE_ID);
57
+ rememberInstalledProvider(state);
28
58
  return true;
29
59
  }
30
60
 
package/src/runtime.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { convertToLlm, type AgentMessage } from "@earendil-works/pi-agent-core";
1
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
2
2
  import type { Api, Message, Model, ModelThinkingLevel, ProviderHeaders } from "@earendil-works/pi-ai";
3
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
3
+ import { convertToLlm, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
4
4
  import { isExactModelAllowed } from "./model-scope";
5
5
  import {
6
6
  CODEX_AFFINITY_SCOPE,