repoburg 1.3.188 → 1.3.196
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/agent-pane/.prettierignore +5 -0
- package/agent-pane/.prettierrc +8 -0
- package/agent-pane/backend/coverage/base.css +224 -0
- package/agent-pane/backend/coverage/block-navigation.js +87 -0
- package/agent-pane/backend/coverage/clover.xml +887 -0
- package/agent-pane/backend/coverage/coverage-final.json +10 -0
- package/agent-pane/backend/coverage/favicon.png +0 -0
- package/agent-pane/backend/coverage/index.html +176 -0
- package/agent-pane/backend/coverage/prettify.css +1 -0
- package/agent-pane/backend/coverage/prettify.js +2 -0
- package/agent-pane/backend/coverage/sort-arrow-sprite.png +0 -0
- package/agent-pane/backend/coverage/sorter.js +210 -0
- package/agent-pane/backend/coverage/src/codex/codex-provider.ts.html +1402 -0
- package/agent-pane/backend/coverage/src/codex/index.html +131 -0
- package/agent-pane/backend/coverage/src/codex/item-mapper.ts.html +331 -0
- package/agent-pane/backend/coverage/src/db/index.html +131 -0
- package/agent-pane/backend/coverage/src/db/index.ts.html +436 -0
- package/agent-pane/backend/coverage/src/db/schema.ts.html +175 -0
- package/agent-pane/backend/coverage/src/index.html +116 -0
- package/agent-pane/backend/coverage/src/index.ts.html +262 -0
- package/agent-pane/backend/coverage/src/routes/index.html +146 -0
- package/agent-pane/backend/coverage/src/routes/sessions.ts.html +991 -0
- package/agent-pane/backend/coverage/src/routes/turns.ts.html +256 -0
- package/agent-pane/backend/coverage/src/routes/workspaces.ts.html +406 -0
- package/agent-pane/backend/coverage/src/ws/index.html +116 -0
- package/agent-pane/backend/coverage/src/ws/index.ts.html +238 -0
- package/agent-pane/backend/data.db +0 -0
- package/agent-pane/backend/data.db-shm +0 -0
- package/agent-pane/backend/data.db-wal +0 -0
- package/agent-pane/backend/dist/index.js +70869 -0
- package/agent-pane/backend/package-lock.json +4529 -1013
- package/agent-pane/backend/package.json +10 -6
- package/agent-pane/backend/src/claude/claude-message-mapper.test.ts +163 -0
- package/agent-pane/backend/src/claude/claude-message-mapper.ts +154 -0
- package/agent-pane/backend/src/claude/claude-provider.test.ts +65 -0
- package/agent-pane/backend/src/claude/claude-provider.ts +482 -0
- package/agent-pane/backend/src/claude/claude-types.ts +33 -0
- package/agent-pane/backend/src/codex/app-server-client.test.ts +183 -0
- package/agent-pane/backend/src/codex/app-server-client.ts +225 -0
- package/agent-pane/backend/src/codex/app-server-types.ts +259 -0
- package/agent-pane/backend/src/codex/codex-provider-runtime-config.test.ts +56 -0
- package/agent-pane/backend/src/codex/codex-provider.test.ts +60 -0
- package/agent-pane/backend/src/codex/codex-provider.ts +521 -265
- package/agent-pane/backend/src/codex/item-mapper.test.ts +221 -0
- package/agent-pane/backend/src/codex/item-mapper.ts +35 -8
- package/agent-pane/backend/src/codex/shared-app-server.ts +25 -0
- package/agent-pane/backend/src/db/index.test.ts +125 -0
- package/agent-pane/backend/src/db/index.ts +39 -8
- package/agent-pane/backend/src/db/schema.ts +28 -74
- package/agent-pane/backend/src/index.ts +29 -2
- package/agent-pane/backend/src/integration/__fixtures__/cancel-turn.json +29 -0
- package/agent-pane/backend/src/integration/__fixtures__/delta-streaming.json +40 -0
- package/agent-pane/backend/src/integration/__fixtures__/failed-turn.json +16 -0
- package/agent-pane/backend/src/integration/__fixtures__/mcp-tool-call.json +60 -0
- package/agent-pane/backend/src/integration/__fixtures__/missing-item-started.json +34 -0
- package/agent-pane/backend/src/integration/__fixtures__/multi-item-turn.json +85 -0
- package/agent-pane/backend/src/integration/__fixtures__/multi-turn.json +48 -0
- package/agent-pane/backend/src/integration/__fixtures__/simple-turn.json +35 -0
- package/agent-pane/backend/src/integration/delta-streaming.test.ts +130 -0
- package/agent-pane/backend/src/integration/helpers/app-server-mock.ts +272 -0
- package/agent-pane/backend/src/integration/helpers/test-app.ts +108 -0
- package/agent-pane/backend/src/integration/helpers/ws-capture.ts +64 -0
- package/agent-pane/backend/src/integration/item-lifecycle.test.ts +133 -0
- package/agent-pane/backend/src/integration/mcp-tool-call.test.ts +102 -0
- package/agent-pane/backend/src/integration/missing-item-started.test.ts +91 -0
- package/agent-pane/backend/src/integration/multi-turn.test.ts +121 -0
- package/agent-pane/backend/src/integration/session-operations.test.ts +168 -0
- package/agent-pane/backend/src/integration/title-generation.test.ts +109 -0
- package/agent-pane/backend/src/integration/turn-cancel.test.ts +111 -0
- package/agent-pane/backend/src/integration/turn-failure.test.ts +97 -0
- package/agent-pane/backend/src/integration/turn-lifecycle.test.ts +141 -0
- package/agent-pane/backend/src/providers/index.ts +14 -0
- package/agent-pane/backend/src/providers/registry.test.ts +92 -0
- package/agent-pane/backend/src/providers/registry.ts +43 -0
- package/agent-pane/backend/src/providers/runtime-provider.ts +25 -0
- package/agent-pane/backend/src/routes/files.ts +31 -0
- package/agent-pane/backend/src/routes/mcp.ts +33 -0
- package/agent-pane/backend/src/routes/models.test.ts +92 -0
- package/agent-pane/backend/src/routes/models.ts +16 -0
- package/agent-pane/backend/src/routes/plugins.ts +40 -0
- package/agent-pane/backend/src/routes/providers.test.ts +69 -0
- package/agent-pane/backend/src/routes/providers.ts +57 -0
- package/agent-pane/backend/src/routes/sessions.test.ts +424 -0
- package/agent-pane/backend/src/routes/sessions.ts +295 -42
- package/agent-pane/backend/src/routes/skills.test.ts +114 -0
- package/agent-pane/backend/src/routes/skills.ts +70 -0
- package/agent-pane/backend/src/routes/turns.test.ts +152 -0
- package/agent-pane/backend/src/routes/turns.ts +36 -16
- package/agent-pane/backend/src/routes/workspaces.test.ts +197 -0
- package/agent-pane/backend/src/routes/workspaces.ts +73 -55
- package/agent-pane/backend/src/ws/index.test.ts +107 -0
- package/agent-pane/backend/src/ws/index.ts +19 -11
- package/agent-pane/backend/tsconfig.json +4 -2
- package/agent-pane/backend/vitest.config.ts +20 -0
- package/agent-pane/eslint.config.js +33 -0
- package/agent-pane/frontend/coverage/base.css +224 -0
- package/agent-pane/frontend/coverage/block-navigation.js +87 -0
- package/agent-pane/frontend/coverage/clover.xml +2962 -0
- package/agent-pane/frontend/coverage/coverage-final.json +37 -0
- package/agent-pane/frontend/coverage/favicon.png +0 -0
- package/agent-pane/frontend/coverage/index.html +251 -0
- package/agent-pane/frontend/coverage/prettify.css +1 -0
- package/agent-pane/frontend/coverage/prettify.js +2 -0
- package/agent-pane/frontend/coverage/sort-arrow-sprite.png +0 -0
- package/agent-pane/frontend/coverage/sorter.js +210 -0
- package/agent-pane/frontend/coverage/src/App.tsx.html +217 -0
- package/agent-pane/frontend/coverage/src/AppInitializer.tsx.html +223 -0
- package/agent-pane/frontend/coverage/src/api/client.ts.html +187 -0
- package/agent-pane/frontend/coverage/src/api/index.html +161 -0
- package/agent-pane/frontend/coverage/src/api/sessions.ts.html +316 -0
- package/agent-pane/frontend/coverage/src/api/websocket.ts.html +544 -0
- package/agent-pane/frontend/coverage/src/api/workspaces.ts.html +199 -0
- package/agent-pane/frontend/coverage/src/components/CollapsibleToolCalls.tsx.html +226 -0
- package/agent-pane/frontend/coverage/src/components/CommandInputBar.tsx.html +382 -0
- package/agent-pane/frontend/coverage/src/components/CommandPalette.tsx.html +715 -0
- package/agent-pane/frontend/coverage/src/components/IdeWorkspace.tsx.html +160 -0
- package/agent-pane/frontend/coverage/src/components/ModelSelector.tsx.html +478 -0
- package/agent-pane/frontend/coverage/src/components/NewSessionDialog.tsx.html +472 -0
- package/agent-pane/frontend/coverage/src/components/RenderArea.tsx.html +574 -0
- package/agent-pane/frontend/coverage/src/components/RightPanel.tsx.html +205 -0
- package/agent-pane/frontend/coverage/src/components/RuntimeIcon.tsx.html +133 -0
- package/agent-pane/frontend/coverage/src/components/SessionTabs.tsx.html +217 -0
- package/agent-pane/frontend/coverage/src/components/StatusBar.tsx.html +271 -0
- package/agent-pane/frontend/coverage/src/components/StatusDot.tsx.html +109 -0
- package/agent-pane/frontend/coverage/src/components/Toast.tsx.html +313 -0
- package/agent-pane/frontend/coverage/src/components/TurnContent.tsx.html +1216 -0
- package/agent-pane/frontend/coverage/src/components/WorkspaceSidebar.tsx.html +1138 -0
- package/agent-pane/frontend/coverage/src/components/index.html +326 -0
- package/agent-pane/frontend/coverage/src/components/panels/ChangesPanel.tsx.html +247 -0
- package/agent-pane/frontend/coverage/src/components/panels/LogsPanel.tsx.html +160 -0
- package/agent-pane/frontend/coverage/src/components/panels/MetricsPanel.tsx.html +427 -0
- package/agent-pane/frontend/coverage/src/components/panels/TurnsPanel.tsx.html +397 -0
- package/agent-pane/frontend/coverage/src/components/panels/index.html +161 -0
- package/agent-pane/frontend/coverage/src/constants/index.html +116 -0
- package/agent-pane/frontend/coverage/src/constants/models.ts.html +406 -0
- package/agent-pane/frontend/coverage/src/hooks/index.html +146 -0
- package/agent-pane/frontend/coverage/src/hooks/useEscapeKey.ts.html +124 -0
- package/agent-pane/frontend/coverage/src/hooks/useOutsideClick.ts.html +142 -0
- package/agent-pane/frontend/coverage/src/hooks/useSessionStatus.ts.html +187 -0
- package/agent-pane/frontend/coverage/src/index.html +146 -0
- package/agent-pane/frontend/coverage/src/main.tsx.html +115 -0
- package/agent-pane/frontend/coverage/src/store/index.html +116 -0
- package/agent-pane/frontend/coverage/src/store/store.ts.html +2404 -0
- package/agent-pane/frontend/coverage/src/test/component-helpers.ts.html +190 -0
- package/agent-pane/frontend/coverage/src/test/fixtures.ts.html +367 -0
- package/agent-pane/frontend/coverage/src/test/index.html +131 -0
- package/agent-pane/frontend/coverage/src/types/index.html +116 -0
- package/agent-pane/frontend/coverage/src/types/index.ts.html +466 -0
- package/agent-pane/frontend/coverage/src/utils/changes.ts.html +202 -0
- package/agent-pane/frontend/coverage/src/utils/duration.ts.html +127 -0
- package/agent-pane/frontend/coverage/src/utils/index.html +131 -0
- package/agent-pane/frontend/dist/assets/index-Bg0PF2aV.css +1 -0
- package/agent-pane/frontend/dist/assets/index-BlybeX5e.js +281 -0
- package/agent-pane/frontend/dist/index.html +19 -0
- package/agent-pane/frontend/package-lock.json +2434 -213
- package/agent-pane/frontend/package.json +10 -3
- package/agent-pane/frontend/src/App.test.tsx +74 -0
- package/agent-pane/frontend/src/App.tsx +1 -1
- package/agent-pane/frontend/src/AppInitializer.test.tsx +142 -0
- package/agent-pane/frontend/src/AppInitializer.tsx +1 -1
- package/agent-pane/frontend/src/api/client.test.ts +126 -0
- package/agent-pane/frontend/src/api/client.ts +22 -29
- package/agent-pane/frontend/src/api/models.ts +7 -0
- package/agent-pane/frontend/src/api/providers.ts +12 -0
- package/agent-pane/frontend/src/api/sessions.test.ts +241 -0
- package/agent-pane/frontend/src/api/sessions.ts +43 -1
- package/agent-pane/frontend/src/api/skills.ts +12 -0
- package/agent-pane/frontend/src/api/websocket.test.ts +288 -0
- package/agent-pane/frontend/src/api/websocket.ts +27 -2
- package/agent-pane/frontend/src/api/workspaces.test.ts +112 -0
- package/agent-pane/frontend/src/api/workspaces.ts +17 -3
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.test.tsx +49 -0
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.tsx +2 -5
- package/agent-pane/frontend/src/components/CommandInputBar.test.tsx +133 -0
- package/agent-pane/frontend/src/components/CommandInputBar.tsx +296 -17
- package/agent-pane/frontend/src/components/CommandPalette.test.tsx +230 -0
- package/agent-pane/frontend/src/components/CommandPalette.tsx +26 -23
- package/agent-pane/frontend/src/components/IdeWorkspace.tsx +1 -1
- package/agent-pane/frontend/src/components/ModelSelector.test.tsx +129 -0
- package/agent-pane/frontend/src/components/ModelSelector.tsx +191 -0
- package/agent-pane/frontend/src/components/NewSessionDialog.test.tsx +95 -0
- package/agent-pane/frontend/src/components/NewSessionDialog.tsx +58 -13
- package/agent-pane/frontend/src/components/RenderArea.test.tsx +160 -0
- package/agent-pane/frontend/src/components/RenderArea.tsx +18 -25
- package/agent-pane/frontend/src/components/RightPanel.test.tsx +43 -0
- package/agent-pane/frontend/src/components/RightPanel.tsx +1 -1
- package/agent-pane/frontend/src/components/RuntimeIcon.test.tsx +32 -0
- package/agent-pane/frontend/src/components/RuntimeIcon.tsx +1 -1
- package/agent-pane/frontend/src/components/SessionTabs.test.tsx +72 -0
- package/agent-pane/frontend/src/components/SessionTabs.tsx +12 -12
- package/agent-pane/frontend/src/components/StatusBar.test.tsx +56 -0
- package/agent-pane/frontend/src/components/StatusBar.tsx +25 -31
- package/agent-pane/frontend/src/components/StatusDot.test.tsx +25 -0
- package/agent-pane/frontend/src/components/StatusDot.tsx +2 -7
- package/agent-pane/frontend/src/components/Toast.test.tsx +79 -0
- package/agent-pane/frontend/src/components/Toast.tsx +2 -6
- package/agent-pane/frontend/src/components/TurnContent.test.tsx +287 -0
- package/agent-pane/frontend/src/components/TurnContent.tsx +107 -24
- package/agent-pane/frontend/src/components/WorkspaceSidebar.test.tsx +297 -0
- package/agent-pane/frontend/src/components/WorkspaceSidebar.tsx +33 -32
- package/agent-pane/frontend/src/components/input/OptionSubPopup.tsx +32 -0
- package/agent-pane/frontend/src/components/input/SkillMentionPopup.test.tsx +63 -0
- package/agent-pane/frontend/src/components/input/SkillMentionPopup.tsx +78 -0
- package/agent-pane/frontend/src/components/input/SlashPopup.test.tsx +96 -0
- package/agent-pane/frontend/src/components/input/SlashPopup.tsx +63 -0
- package/agent-pane/frontend/src/components/panels/ChangesPanel.test.tsx +61 -0
- package/agent-pane/frontend/src/components/panels/ChangesPanel.tsx +12 -39
- package/agent-pane/frontend/src/components/panels/LogsPanel.test.tsx +58 -0
- package/agent-pane/frontend/src/components/panels/LogsPanel.tsx +1 -1
- package/agent-pane/frontend/src/components/panels/MetricsPanel.test.tsx +71 -0
- package/agent-pane/frontend/src/components/panels/MetricsPanel.tsx +63 -31
- package/agent-pane/frontend/src/components/panels/TurnsPanel.test.tsx +83 -0
- package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +66 -7
- package/agent-pane/frontend/src/constants/models.test.ts +97 -0
- package/agent-pane/frontend/src/constants/models.ts +129 -0
- package/agent-pane/frontend/src/hooks/useEscapeKey.test.ts +42 -0
- package/agent-pane/frontend/src/hooks/useEscapeKey.ts +13 -0
- package/agent-pane/frontend/src/hooks/useOutsideClick.test.ts +92 -0
- package/agent-pane/frontend/src/hooks/useOutsideClick.ts +19 -0
- package/agent-pane/frontend/src/hooks/useProvider.ts +44 -0
- package/agent-pane/frontend/src/hooks/useSessionStatus.test.ts +114 -0
- package/agent-pane/frontend/src/hooks/useSessionStatus.ts +34 -0
- package/agent-pane/frontend/src/main.test.tsx +37 -0
- package/agent-pane/frontend/src/main.tsx +1 -1
- package/agent-pane/frontend/src/store/sessionSlice.ts +417 -0
- package/agent-pane/frontend/src/store/store.test.ts +1284 -0
- package/agent-pane/frontend/src/store/store.ts +16 -617
- package/agent-pane/frontend/src/store/uiSlice.ts +83 -0
- package/agent-pane/frontend/src/store/workspaceSlice.ts +77 -0
- package/agent-pane/frontend/src/store/wsSlice.ts +292 -0
- package/agent-pane/frontend/src/styles/app.css +113 -7
- package/agent-pane/frontend/src/test/component-helpers.ts +36 -0
- package/agent-pane/frontend/src/test/fixtures.ts +96 -0
- package/agent-pane/frontend/src/test-setup.ts +5 -0
- package/agent-pane/frontend/src/types/index.test.ts +128 -0
- package/agent-pane/frontend/src/types/index.ts +80 -104
- package/agent-pane/frontend/src/utils/changes.test.ts +81 -0
- package/agent-pane/frontend/src/utils/changes.ts +39 -0
- package/agent-pane/frontend/src/utils/duration.test.ts +71 -0
- package/agent-pane/frontend/src/utils/duration.ts +14 -0
- package/agent-pane/frontend/tsconfig.json +4 -1
- package/agent-pane/frontend/vite.config.ts +6 -0
- package/agent-pane/frontend/vitest.config.ts +29 -0
- package/agent-pane/package-lock.json +1527 -0
- package/agent-pane/package.json +19 -0
- package/agent-pane/shared/provider-types.ts +53 -0
- package/agent-pane/shared/types.ts +107 -0
- package/agentpane-branch-session.png +0 -0
- package/agentpane-expanded-all.png +0 -0
- package/agentpane-fixed-rendering.png +0 -0
- package/agentpane-focus-check.png +0 -0
- package/agentpane-initial.png +0 -0
- package/agentpane-mcp-detail.png +0 -0
- package/agentpane-no-focus-ring.png +0 -0
- package/agentpane-soft-focus.png +0 -0
- package/backend/dist/src/llm-provider/ollama-llm.provider.js +1 -0
- package/backend/dist/src/llm-provider/ollama-llm.provider.js.map +1 -1
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/data.db +0 -0
- package/empty-state.png +0 -0
- package/frontend-home.png +0 -0
- package/model-selector-open.png +0 -0
- package/package.json +1 -1
- package/slash-popup-working.png +0 -0
- package/agent-pane/frontend/src/components/DiffViewer.tsx +0 -27
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/** JSON-RPC 2.0 client over stdio for the Codex app-server. */
|
|
2
|
+
|
|
3
|
+
import { spawn, type ChildProcess } from 'node:child_process';
|
|
4
|
+
import { EventEmitter } from 'node:events';
|
|
5
|
+
import type { InitializeParams, InitializeResult } from './app-server-types.ts';
|
|
6
|
+
|
|
7
|
+
/** Request timeout in milliseconds. */
|
|
8
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
9
|
+
|
|
10
|
+
/** Pending request entry. */
|
|
11
|
+
interface PendingRequest {
|
|
12
|
+
resolve: (value: unknown) => void;
|
|
13
|
+
reject: (error: Error) => void;
|
|
14
|
+
timer: ReturnType<typeof setTimeout>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Options for constructing an AppServerClient. */
|
|
18
|
+
export interface AppServerClientOptions {
|
|
19
|
+
/** Path to the codex binary. Defaults to 'codex'. */
|
|
20
|
+
binaryPath?: string;
|
|
21
|
+
/** Working directory for the app-server process. */
|
|
22
|
+
cwd: string;
|
|
23
|
+
/** Environment variables for the child process. */
|
|
24
|
+
env?: Record<string, string>;
|
|
25
|
+
/** Additional launch args (e.g. ['--config', 'key=value']). */
|
|
26
|
+
launchArgs?: string[];
|
|
27
|
+
/** Client info sent in the initialize handshake. */
|
|
28
|
+
clientInfo?: { name: string; title: string; version: string };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* JSON-RPC 2.0 client that communicates with `codex app-server` over stdio.
|
|
33
|
+
*
|
|
34
|
+
* Each message is one JSON object per line. Requests have an `id` field;
|
|
35
|
+
* notifications do not. Responses are matched by `id`.
|
|
36
|
+
*/
|
|
37
|
+
export class AppServerClient {
|
|
38
|
+
private readonly proc: ChildProcess;
|
|
39
|
+
private nextId = 1;
|
|
40
|
+
private readonly pending = new Map<number, PendingRequest>();
|
|
41
|
+
private readonly notificationHandlers = new Map<string, Array<(params: unknown) => void>>();
|
|
42
|
+
private readonly requestHandlers = new Map<string, Array<(params: unknown) => Promise<unknown>>>();
|
|
43
|
+
private buffer = '';
|
|
44
|
+
private closed = false;
|
|
45
|
+
private readonly clientInfo: { name: string; title: string; version: string };
|
|
46
|
+
readonly events = new EventEmitter();
|
|
47
|
+
|
|
48
|
+
constructor(options: AppServerClientOptions) {
|
|
49
|
+
const binary = options.binaryPath ?? 'codex';
|
|
50
|
+
const args = ['app-server', ...(options.launchArgs ?? [])];
|
|
51
|
+
|
|
52
|
+
this.proc = spawn(binary, args, {
|
|
53
|
+
cwd: options.cwd,
|
|
54
|
+
env: { ...process.env, ...options.env },
|
|
55
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
this.clientInfo = options.clientInfo ?? { name: 'agentpane', title: 'AgentPane', version: '1.0.0' };
|
|
59
|
+
|
|
60
|
+
this.proc.stdout?.setEncoding('utf-8');
|
|
61
|
+
this.proc.stdout?.on('data', (chunk: string) => this.onStdoutData(chunk));
|
|
62
|
+
this.proc.stderr?.on('data', (chunk: Buffer) => {
|
|
63
|
+
// Log stderr lines for debugging — app-server may emit diagnostic info
|
|
64
|
+
const text = chunk.toString().trim();
|
|
65
|
+
if (text) console.error('[app-server] stderr:', text);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
this.proc.on('exit', (code, signal) => {
|
|
69
|
+
this.onProcessExit(code, signal);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Perform the initialize handshake. Must be called before any other method. */
|
|
74
|
+
async initialize(): Promise<InitializeResult> {
|
|
75
|
+
const params: InitializeParams = {
|
|
76
|
+
clientInfo: this.clientInfo,
|
|
77
|
+
capabilities: { experimentalApi: true },
|
|
78
|
+
};
|
|
79
|
+
const result = await this.request<InitializeResult>('initialize', params);
|
|
80
|
+
this.notify('initialized');
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Send a JSON-RPC request and wait for the response. */
|
|
85
|
+
request<T>(method: string, params?: unknown): Promise<T> {
|
|
86
|
+
return new Promise<T>((resolve, reject) => {
|
|
87
|
+
if (this.closed) {
|
|
88
|
+
reject(new Error('AppServerClient is closed'));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const id = this.nextId++;
|
|
93
|
+
const message = JSON.stringify({ id, method, params }) + '\n';
|
|
94
|
+
|
|
95
|
+
const timer = setTimeout(() => {
|
|
96
|
+
this.pending.delete(id);
|
|
97
|
+
reject(new Error(`Request '${method}' (id=${id}) timed out after ${REQUEST_TIMEOUT_MS}ms`));
|
|
98
|
+
}, REQUEST_TIMEOUT_MS);
|
|
99
|
+
|
|
100
|
+
this.pending.set(id, {
|
|
101
|
+
resolve: resolve as (value: unknown) => void,
|
|
102
|
+
reject,
|
|
103
|
+
timer,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this.write(message);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Send a JSON-RPC notification (no response expected). */
|
|
111
|
+
notify(method: string, params?: unknown): void {
|
|
112
|
+
if (this.closed) return;
|
|
113
|
+
const message = JSON.stringify({ method, params }) + '\n';
|
|
114
|
+
this.write(message);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Register a handler for a server→client notification. */
|
|
118
|
+
onNotification(method: string, handler: (params: unknown) => void): void {
|
|
119
|
+
const handlers = this.notificationHandlers.get(method) ?? [];
|
|
120
|
+
handlers.push(handler);
|
|
121
|
+
this.notificationHandlers.set(method, handlers);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Register a handler for a server→client request. */
|
|
125
|
+
onRequest(method: string, handler: (params: unknown) => Promise<unknown>): void {
|
|
126
|
+
const handlers = this.requestHandlers.get(method) ?? [];
|
|
127
|
+
handlers.push(handler);
|
|
128
|
+
this.requestHandlers.set(method, handlers);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Check if the client is closed (process exited or close() called). */
|
|
132
|
+
isClosed(): boolean {
|
|
133
|
+
return this.closed;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Close the client and kill the app-server process. */
|
|
137
|
+
async close(): Promise<void> {
|
|
138
|
+
if (this.closed) return;
|
|
139
|
+
this.closed = true;
|
|
140
|
+
|
|
141
|
+
// Reject all pending requests
|
|
142
|
+
for (const [id, entry] of this.pending) {
|
|
143
|
+
clearTimeout(entry.timer);
|
|
144
|
+
entry.reject(new Error('AppServerClient closed'));
|
|
145
|
+
this.pending.delete(id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Kill the process
|
|
149
|
+
this.proc.kill('SIGTERM');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// --- Internal methods ---
|
|
153
|
+
|
|
154
|
+
private write(message: string): void {
|
|
155
|
+
this.proc.stdin?.write(message);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private onStdoutData(chunk: string): void {
|
|
159
|
+
this.buffer += chunk;
|
|
160
|
+
const lines = this.buffer.split('\n');
|
|
161
|
+
this.buffer = lines.pop() ?? '';
|
|
162
|
+
|
|
163
|
+
for (const line of lines) {
|
|
164
|
+
const trimmed = line.trim();
|
|
165
|
+
if (!trimmed) continue;
|
|
166
|
+
this.handleMessage(trimmed);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private handleMessage(raw: string): void {
|
|
171
|
+
let msg: Record<string, unknown>;
|
|
172
|
+
try {
|
|
173
|
+
msg = JSON.parse(raw) as Record<string, unknown>;
|
|
174
|
+
} catch {
|
|
175
|
+
// Ignore malformed JSON lines
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Response to a request (has `id` + `result` or `error`)
|
|
180
|
+
if ('id' in msg && typeof msg.id === 'number') {
|
|
181
|
+
const pending = this.pending.get(msg.id);
|
|
182
|
+
if (!pending) return;
|
|
183
|
+
|
|
184
|
+
clearTimeout(pending.timer);
|
|
185
|
+
this.pending.delete(msg.id);
|
|
186
|
+
|
|
187
|
+
if (msg.error) {
|
|
188
|
+
const err = msg.error as { code: number; message: string };
|
|
189
|
+
pending.reject(new Error(`JSON-RPC error ${err.code}: ${err.message}`));
|
|
190
|
+
} else {
|
|
191
|
+
pending.resolve(msg.result);
|
|
192
|
+
}
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Notification (has `method` but no `id`)
|
|
197
|
+
if ('method' in msg && typeof msg.method === 'string') {
|
|
198
|
+
const handlers = this.notificationHandlers.get(msg.method);
|
|
199
|
+
if (handlers) {
|
|
200
|
+
for (const handler of handlers) {
|
|
201
|
+
handler(msg.params);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
this.events.emit('notification', msg.method, msg.params);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private onProcessExit(code: number | null, signal: NodeJS.Signals | null): void {
|
|
210
|
+
if (this.closed) return;
|
|
211
|
+
this.closed = true;
|
|
212
|
+
|
|
213
|
+
const reason = signal ? `signal ${signal}` : `code ${code}`;
|
|
214
|
+
const error = new Error(`App-server process exited (${reason})`);
|
|
215
|
+
|
|
216
|
+
for (const [, entry] of this.pending) {
|
|
217
|
+
clearTimeout(entry.timer);
|
|
218
|
+
entry.reject(error);
|
|
219
|
+
}
|
|
220
|
+
this.pending.clear();
|
|
221
|
+
|
|
222
|
+
this.events.emit('closed', reason);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/** TypeScript interfaces for the Codex app-server JSON-RPC protocol. */
|
|
2
|
+
|
|
3
|
+
// --- Skill metadata (from `skills/list`) ---
|
|
4
|
+
|
|
5
|
+
export interface AppServerSkill {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
path: string;
|
|
9
|
+
scope: 'user' | 'repo' | 'system' | 'admin';
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
displayName?: string;
|
|
12
|
+
shortDescription?: string;
|
|
13
|
+
interface?: {
|
|
14
|
+
displayName?: string;
|
|
15
|
+
shortDescription?: string;
|
|
16
|
+
brandColor?: string;
|
|
17
|
+
defaultPrompt?: string;
|
|
18
|
+
iconSmall?: string | null;
|
|
19
|
+
iconLarge?: string | null;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SkillsListResult {
|
|
24
|
+
data: Array<{
|
|
25
|
+
cwd: string;
|
|
26
|
+
skills: AppServerSkill[];
|
|
27
|
+
errors: string[];
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// --- Model metadata (from `model/list`) ---
|
|
32
|
+
|
|
33
|
+
export interface AppServerModel {
|
|
34
|
+
id: string;
|
|
35
|
+
model: string;
|
|
36
|
+
displayName: string;
|
|
37
|
+
description: string;
|
|
38
|
+
defaultReasoningEffort: string;
|
|
39
|
+
supportedReasoningEfforts: Array<{
|
|
40
|
+
reasoningEffort: string;
|
|
41
|
+
description: string;
|
|
42
|
+
}>;
|
|
43
|
+
inputModalities?: string[];
|
|
44
|
+
isDefault?: boolean;
|
|
45
|
+
hidden?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ModelListResult {
|
|
49
|
+
data: AppServerModel[];
|
|
50
|
+
nextCursor: string | null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --- Plugin metadata (from `plugin/list`) ---
|
|
54
|
+
|
|
55
|
+
export interface AppServerPlugin {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
installed: boolean;
|
|
59
|
+
enabled: boolean;
|
|
60
|
+
availability: string;
|
|
61
|
+
localVersion?: string | null;
|
|
62
|
+
version?: string | null;
|
|
63
|
+
interface?: {
|
|
64
|
+
displayName?: string;
|
|
65
|
+
shortDescription?: string;
|
|
66
|
+
longDescription?: string;
|
|
67
|
+
brandColor?: string;
|
|
68
|
+
developerName?: string;
|
|
69
|
+
category?: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface PluginListResult {
|
|
74
|
+
marketplaces: Array<{
|
|
75
|
+
name: string;
|
|
76
|
+
plugins: AppServerPlugin[];
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// --- MCP server metadata (from `mcpServerStatus/list`) ---
|
|
81
|
+
|
|
82
|
+
export interface AppServerMcpServer {
|
|
83
|
+
name: string;
|
|
84
|
+
serverInfo?: {
|
|
85
|
+
name?: string;
|
|
86
|
+
title?: string | null;
|
|
87
|
+
version?: string;
|
|
88
|
+
description?: string | null;
|
|
89
|
+
};
|
|
90
|
+
authStatus?: string;
|
|
91
|
+
tools?: Record<string, unknown>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface McpListResult {
|
|
95
|
+
data: AppServerMcpServer[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// --- File search (from `fuzzyFileSearch`) ---
|
|
99
|
+
|
|
100
|
+
export interface AppServerFileSearchResult {
|
|
101
|
+
file_name: string;
|
|
102
|
+
path: string;
|
|
103
|
+
match_type: 'file' | 'directory';
|
|
104
|
+
score: number;
|
|
105
|
+
root: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface FileSearchResult {
|
|
109
|
+
results: AppServerFileSearchResult[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// --- Thread / Turn ---
|
|
113
|
+
|
|
114
|
+
export interface ThreadStartParams {
|
|
115
|
+
cwd: string;
|
|
116
|
+
approvalPolicy: string;
|
|
117
|
+
sandbox: string;
|
|
118
|
+
model?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface ThreadStartResult {
|
|
122
|
+
thread: { id: string };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface ThreadResumeParams {
|
|
126
|
+
threadId: string;
|
|
127
|
+
cwd: string;
|
|
128
|
+
approvalPolicy: string;
|
|
129
|
+
sandbox: string;
|
|
130
|
+
model?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface TurnStartParams {
|
|
134
|
+
threadId: string;
|
|
135
|
+
input: Array<{ type: 'text'; text: string } | { type: 'image'; url: string }>;
|
|
136
|
+
approvalPolicy: string;
|
|
137
|
+
sandbox: { type: 'read-only' | 'workspace-write' | 'danger-full-access' };
|
|
138
|
+
model?: string;
|
|
139
|
+
effort?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface TurnStartResult {
|
|
143
|
+
turn: { id: string };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface TurnInterruptParams {
|
|
147
|
+
threadId: string;
|
|
148
|
+
turnId: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --- Notification params ---
|
|
152
|
+
|
|
153
|
+
export interface ItemNotification {
|
|
154
|
+
threadId: string;
|
|
155
|
+
turnId: string;
|
|
156
|
+
item: AppServerItem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface TurnCompletedNotification {
|
|
160
|
+
threadId: string;
|
|
161
|
+
turnId: string;
|
|
162
|
+
usage?: unknown;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface TurnFailedNotification {
|
|
166
|
+
threadId: string;
|
|
167
|
+
turnId: string;
|
|
168
|
+
error: { message: string };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Params for `item/agentMessage/delta` — incremental text fragment. */
|
|
172
|
+
export interface AgentMessageDeltaNotification {
|
|
173
|
+
threadId: string;
|
|
174
|
+
turnId: string;
|
|
175
|
+
itemId: string;
|
|
176
|
+
delta: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** CamelCase token usage from app-server (needs mapping to snake_case TokenUsage). */
|
|
180
|
+
export interface AppServerTokenUsageData {
|
|
181
|
+
totalTokens: number;
|
|
182
|
+
inputTokens: number;
|
|
183
|
+
cachedInputTokens: number;
|
|
184
|
+
cacheWriteInputTokens: number;
|
|
185
|
+
outputTokens: number;
|
|
186
|
+
reasoningOutputTokens: number;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Params for `thread/tokenUsage/updated` — token usage for a turn. */
|
|
190
|
+
export interface TokenUsageNotification {
|
|
191
|
+
threadId: string;
|
|
192
|
+
turnId: string;
|
|
193
|
+
tokenUsage: {
|
|
194
|
+
total: AppServerTokenUsageData;
|
|
195
|
+
last: AppServerTokenUsageData;
|
|
196
|
+
modelContextWindow: number;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Map app-server camelCase token usage to shared snake_case TokenUsage. */
|
|
201
|
+
export function mapTokenUsage(data: AppServerTokenUsageData): {
|
|
202
|
+
input_tokens: number;
|
|
203
|
+
cached_input_tokens: number;
|
|
204
|
+
cache_write_input_tokens: number;
|
|
205
|
+
output_tokens: number;
|
|
206
|
+
reasoning_output_tokens: number;
|
|
207
|
+
} {
|
|
208
|
+
return {
|
|
209
|
+
input_tokens: data.inputTokens,
|
|
210
|
+
cached_input_tokens: data.cachedInputTokens,
|
|
211
|
+
cache_write_input_tokens: data.cacheWriteInputTokens,
|
|
212
|
+
output_tokens: data.outputTokens,
|
|
213
|
+
reasoning_output_tokens: data.reasoningOutputTokens,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// --- Generic item type (replaces SDK ThreadItem) ---
|
|
218
|
+
|
|
219
|
+
export interface AppServerItem {
|
|
220
|
+
id: string;
|
|
221
|
+
type: string;
|
|
222
|
+
text?: string;
|
|
223
|
+
summary?: unknown[];
|
|
224
|
+
content?: unknown[];
|
|
225
|
+
command?: string;
|
|
226
|
+
aggregated_output?: string;
|
|
227
|
+
exit_code?: number | null;
|
|
228
|
+
status?: 'in_progress' | 'completed' | 'failed';
|
|
229
|
+
changes?: unknown;
|
|
230
|
+
server?: string;
|
|
231
|
+
tool?: string;
|
|
232
|
+
arguments?: unknown;
|
|
233
|
+
result?: unknown;
|
|
234
|
+
error?: string | null;
|
|
235
|
+
query?: string;
|
|
236
|
+
items?: unknown[];
|
|
237
|
+
message?: string;
|
|
238
|
+
[key: string]: unknown;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// --- Initialize handshake ---
|
|
242
|
+
|
|
243
|
+
export interface InitializeParams {
|
|
244
|
+
clientInfo: {
|
|
245
|
+
name: string;
|
|
246
|
+
title: string;
|
|
247
|
+
version: string;
|
|
248
|
+
};
|
|
249
|
+
capabilities: {
|
|
250
|
+
experimentalApi: boolean;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface InitializeResult {
|
|
255
|
+
userAgent: string;
|
|
256
|
+
codexHome: string;
|
|
257
|
+
platformFamily: string;
|
|
258
|
+
platformOs: string;
|
|
259
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock the codex-provider module to get access to getThreadOptions
|
|
4
|
+
// We mock everything except getThreadOptions which we want to test
|
|
5
|
+
|
|
6
|
+
vi.mock('../db/index.ts', () => ({
|
|
7
|
+
getDb: vi.fn(),
|
|
8
|
+
initDb: vi.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock('../ws/index.ts', () => ({
|
|
12
|
+
broadcast: vi.fn(),
|
|
13
|
+
initWs: vi.fn(),
|
|
14
|
+
handleUpgrade: vi.fn(),
|
|
15
|
+
getWss: vi.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
vi.mock('./item-mapper.ts', () => ({
|
|
19
|
+
mapItemData: vi.fn(),
|
|
20
|
+
getItemStatus: vi.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
// Import after mocks are set up
|
|
24
|
+
import type { Session } from '@agentpane/shared';
|
|
25
|
+
|
|
26
|
+
// We need to test getThreadOptions indirectly since it's not exported.
|
|
27
|
+
// Instead, we test the behavior by checking that runtime_config is parsed correctly.
|
|
28
|
+
// Since getThreadOptions is private, we test the parsing logic here.
|
|
29
|
+
|
|
30
|
+
describe('SessionRuntimeConfig parsing', () => {
|
|
31
|
+
function parseRuntimeConfig(session: Pick<Session, 'runtime_config'>): Record<string, unknown> | null {
|
|
32
|
+
if (!session.runtime_config) return null;
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(session.runtime_config) as Record<string, unknown>;
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
it('returns null when runtime_config is null', () => {
|
|
41
|
+
expect(parseRuntimeConfig({ runtime_config: null })).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns parsed config when valid JSON', () => {
|
|
45
|
+
const config = { sandboxMode: 'read-only', webSearchMode: 'live', networkAccessEnabled: true };
|
|
46
|
+
expect(parseRuntimeConfig({ runtime_config: JSON.stringify(config) })).toEqual(config);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('returns null when invalid JSON', () => {
|
|
50
|
+
expect(parseRuntimeConfig({ runtime_config: 'invalid json' })).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns empty object when runtime_config is empty string JSON', () => {
|
|
54
|
+
expect(parseRuntimeConfig({ runtime_config: '{}' })).toEqual({});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// Mock the app-server client to avoid spawning real processes
|
|
4
|
+
vi.mock('../codex/app-server-client.ts', () => ({
|
|
5
|
+
AppServerClient: vi.fn().mockImplementation(() => ({
|
|
6
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
7
|
+
request: vi.fn().mockResolvedValue({ data: [] }),
|
|
8
|
+
onNotification: vi.fn(),
|
|
9
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
10
|
+
isClosed: vi.fn().mockReturnValue(false),
|
|
11
|
+
events: { once: vi.fn(), on: vi.fn() },
|
|
12
|
+
})),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// Mock the shared app-server to avoid spawning a real process
|
|
16
|
+
vi.mock('../codex/shared-app-server.ts', () => ({
|
|
17
|
+
getSharedAppServer: vi.fn().mockReturnValue({
|
|
18
|
+
request: vi.fn().mockResolvedValue({ data: [] }),
|
|
19
|
+
}),
|
|
20
|
+
closeSharedAppServer: vi.fn().mockResolvedValue(undefined),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
// Mock broadcast to avoid WebSocket setup
|
|
24
|
+
vi.mock('../ws/index.ts', () => ({
|
|
25
|
+
broadcast: vi.fn(),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
import { codexProvider } from './codex-provider.ts';
|
|
29
|
+
import type { RuntimeProvider } from '../providers/runtime-provider.ts';
|
|
30
|
+
|
|
31
|
+
describe('codexProvider', () => {
|
|
32
|
+
it('implements all RuntimeProvider methods', () => {
|
|
33
|
+
const provider: RuntimeProvider = codexProvider;
|
|
34
|
+
expect(typeof provider.startTurn).toBe('function');
|
|
35
|
+
expect(typeof provider.interruptTurn).toBe('function');
|
|
36
|
+
expect(typeof provider.clearThread).toBe('function');
|
|
37
|
+
expect(typeof provider.clearAllThreads).toBe('function');
|
|
38
|
+
expect(typeof provider.getModels).toBe('function');
|
|
39
|
+
expect(typeof provider.shutdown).toBe('function');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('getModels returns an array', async () => {
|
|
43
|
+
const models = await codexProvider.getModels();
|
|
44
|
+
expect(Array.isArray(models)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('shutdown calls closeSharedAppServer and clearAllThreads', async () => {
|
|
48
|
+
await codexProvider.shutdown();
|
|
49
|
+
// No error thrown — shutdown completed
|
|
50
|
+
// closeSharedAppServer and clearAllThreads are mocked, so this just verifies no crash
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('clearAllThreads does not throw', () => {
|
|
54
|
+
expect(() => codexProvider.clearAllThreads()).not.toThrow();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('clearThread does not throw for unknown session', () => {
|
|
58
|
+
expect(() => codexProvider.clearThread('nonexistent')).not.toThrow();
|
|
59
|
+
});
|
|
60
|
+
});
|