@kodelyth/codex 2026.5.42 → 2026.6.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 +16 -1
- package/doctor-contract-api.test.ts +0 -44
- package/doctor-contract-api.ts +0 -68
- package/harness.ts +0 -72
- package/index.test.ts +0 -230
- package/index.ts +0 -66
- package/media-understanding-provider.test.ts +0 -486
- package/media-understanding-provider.ts +0 -521
- package/prompt-overlay-runtime-contract.test.ts +0 -48
- package/prompt-overlay.ts +0 -21
- package/provider-catalog.ts +0 -83
- package/provider-discovery.ts +0 -45
- package/provider.test.ts +0 -384
- package/provider.ts +0 -243
- package/src/app-server/app-inventory-cache.test.ts +0 -176
- package/src/app-server/app-inventory-cache.ts +0 -324
- package/src/app-server/approval-bridge.test.ts +0 -1471
- package/src/app-server/approval-bridge.ts +0 -1211
- package/src/app-server/auth-bridge.test.ts +0 -1449
- package/src/app-server/auth-bridge.ts +0 -614
- package/src/app-server/auth-profile-runtime-contract.test.ts +0 -239
- package/src/app-server/capabilities.ts +0 -27
- package/src/app-server/client-factory.ts +0 -24
- package/src/app-server/client.test.ts +0 -563
- package/src/app-server/client.ts +0 -715
- package/src/app-server/compact.test.ts +0 -710
- package/src/app-server/compact.ts +0 -500
- package/src/app-server/computer-use.test.ts +0 -788
- package/src/app-server/computer-use.ts +0 -683
- package/src/app-server/config.test.ts +0 -879
- package/src/app-server/config.ts +0 -1038
- package/src/app-server/context-engine-projection.test.ts +0 -252
- package/src/app-server/context-engine-projection.ts +0 -403
- package/src/app-server/delivery-no-reply-runtime-contract.test.ts +0 -80
- package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
- package/src/app-server/dynamic-tool-profile.ts +0 -69
- package/src/app-server/dynamic-tools.test.ts +0 -1302
- package/src/app-server/dynamic-tools.ts +0 -623
- package/src/app-server/elicitation-bridge.test.ts +0 -1056
- package/src/app-server/elicitation-bridge.ts +0 -783
- package/src/app-server/event-projector.test.ts +0 -2668
- package/src/app-server/event-projector.ts +0 -2057
- package/src/app-server/image-payload-sanitizer.test.ts +0 -49
- package/src/app-server/image-payload-sanitizer.ts +0 -167
- package/src/app-server/klaw-owned-tool-runtime-contract.test.ts +0 -456
- package/src/app-server/local-runtime-attribution.ts +0 -39
- package/src/app-server/managed-binary.test.ts +0 -139
- package/src/app-server/managed-binary.ts +0 -193
- package/src/app-server/models.test.ts +0 -246
- package/src/app-server/models.ts +0 -172
- package/src/app-server/native-hook-relay.test.ts +0 -271
- package/src/app-server/native-hook-relay.ts +0 -150
- package/src/app-server/native-subagent-task-mirror.test.ts +0 -573
- package/src/app-server/native-subagent-task-mirror.ts +0 -497
- package/src/app-server/outcome-fallback-runtime-contract.test.ts +0 -404
- package/src/app-server/plugin-activation.test.ts +0 -336
- package/src/app-server/plugin-activation.ts +0 -283
- package/src/app-server/plugin-app-cache-key.ts +0 -74
- package/src/app-server/plugin-approval-roundtrip.ts +0 -122
- package/src/app-server/plugin-inventory.test.ts +0 -355
- package/src/app-server/plugin-inventory.ts +0 -357
- package/src/app-server/plugin-thread-config.test.ts +0 -865
- package/src/app-server/plugin-thread-config.ts +0 -455
- package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
- package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
- package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
- package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
- package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
- package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
- package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
- package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
- package/src/app-server/protocol-validators.test.ts +0 -75
- package/src/app-server/protocol-validators.ts +0 -203
- package/src/app-server/protocol.ts +0 -520
- package/src/app-server/rate-limit-cache.ts +0 -48
- package/src/app-server/rate-limits.test.ts +0 -202
- package/src/app-server/rate-limits.ts +0 -583
- package/src/app-server/request.ts +0 -73
- package/src/app-server/run-attempt.context-engine.test.ts +0 -1004
- package/src/app-server/run-attempt.test.ts +0 -9477
- package/src/app-server/run-attempt.ts +0 -4683
- package/src/app-server/run-attempt.vision-tools.test.ts +0 -35
- package/src/app-server/schema-normalization-runtime-contract.test.ts +0 -206
- package/src/app-server/session-binding.test.ts +0 -303
- package/src/app-server/session-binding.ts +0 -398
- package/src/app-server/session-history.ts +0 -44
- package/src/app-server/shared-client.test.ts +0 -589
- package/src/app-server/shared-client.ts +0 -289
- package/src/app-server/side-question.test.ts +0 -1175
- package/src/app-server/side-question.ts +0 -1007
- package/src/app-server/test-support.ts +0 -48
- package/src/app-server/thread-lifecycle.test.ts +0 -447
- package/src/app-server/thread-lifecycle.ts +0 -939
- package/src/app-server/thread-lifecycle.user-mcp-servers.test.ts +0 -442
- package/src/app-server/timeout.ts +0 -9
- package/src/app-server/tool-progress-normalization.ts +0 -77
- package/src/app-server/trajectory.test.ts +0 -205
- package/src/app-server/trajectory.ts +0 -365
- package/src/app-server/transcript-mirror.test.ts +0 -524
- package/src/app-server/transcript-mirror.ts +0 -208
- package/src/app-server/transcript-repair-runtime-contract.test.ts +0 -44
- package/src/app-server/transport-stdio.test.ts +0 -171
- package/src/app-server/transport-stdio.ts +0 -107
- package/src/app-server/transport-websocket.test.ts +0 -69
- package/src/app-server/transport-websocket.ts +0 -90
- package/src/app-server/transport.ts +0 -117
- package/src/app-server/user-input-bridge.test.ts +0 -249
- package/src/app-server/user-input-bridge.ts +0 -316
- package/src/app-server/version.ts +0 -4
- package/src/app-server/vision-tools.ts +0 -12
- package/src/command-account.ts +0 -544
- package/src/command-formatters.ts +0 -425
- package/src/command-handlers.ts +0 -2004
- package/src/command-rpc.test.ts +0 -16
- package/src/command-rpc.ts +0 -142
- package/src/commands.test.ts +0 -3312
- package/src/commands.ts +0 -65
- package/src/conversation-binding-data.ts +0 -124
- package/src/conversation-binding.test.ts +0 -599
- package/src/conversation-binding.ts +0 -561
- package/src/conversation-control.test.ts +0 -126
- package/src/conversation-control.ts +0 -303
- package/src/conversation-turn-collector.test.ts +0 -191
- package/src/conversation-turn-collector.ts +0 -186
- package/src/conversation-turn-input.test.ts +0 -141
- package/src/conversation-turn-input.ts +0 -106
- package/src/manifest.test.ts +0 -20
- package/src/migration/apply.ts +0 -501
- package/src/migration/helpers.ts +0 -55
- package/src/migration/plan.ts +0 -461
- package/src/migration/provider.test.ts +0 -1741
- package/src/migration/provider.ts +0 -41
- package/src/migration/source.ts +0 -643
- package/src/migration/targets.ts +0 -25
- package/src/node-cli-sessions.test.ts +0 -180
- package/src/node-cli-sessions.ts +0 -711
- package/test-api.ts +0 -82
- package/tsconfig.json +0 -16
|
@@ -1,442 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import type { EmbeddedRunAttemptParams } from "klaw/plugin-sdk/agent-harness-runtime";
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
-
import type { CodexAppServerRuntimeOptions } from "./config.js";
|
|
7
|
-
import { readCodexAppServerBinding, writeCodexAppServerBinding } from "./session-binding.js";
|
|
8
|
-
import { startOrResumeThread } from "./thread-lifecycle.js";
|
|
9
|
-
|
|
10
|
-
function threadStartResult(threadId = "thread-1"): Record<string, unknown> {
|
|
11
|
-
return {
|
|
12
|
-
thread: {
|
|
13
|
-
id: threadId,
|
|
14
|
-
sessionId: "session-1",
|
|
15
|
-
forkedFromId: null,
|
|
16
|
-
preview: "",
|
|
17
|
-
ephemeral: false,
|
|
18
|
-
modelProvider: "openai",
|
|
19
|
-
createdAt: 1,
|
|
20
|
-
updatedAt: 1,
|
|
21
|
-
status: { type: "idle" },
|
|
22
|
-
path: null,
|
|
23
|
-
cwd: "/tmp",
|
|
24
|
-
cliVersion: "0.125.0",
|
|
25
|
-
source: "unknown",
|
|
26
|
-
agentNickname: null,
|
|
27
|
-
agentRole: null,
|
|
28
|
-
gitInfo: null,
|
|
29
|
-
name: null,
|
|
30
|
-
turns: [],
|
|
31
|
-
},
|
|
32
|
-
model: "gpt-5.4-codex",
|
|
33
|
-
modelProvider: "openai",
|
|
34
|
-
serviceTier: null,
|
|
35
|
-
cwd: "/tmp",
|
|
36
|
-
instructionSources: [],
|
|
37
|
-
approvalPolicy: "never",
|
|
38
|
-
approvalsReviewer: "user",
|
|
39
|
-
sandbox: { type: "dangerFullAccess" },
|
|
40
|
-
permissionProfile: null,
|
|
41
|
-
reasoningEffort: null,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function threadResumeResult(threadId = "thread-existing"): Record<string, unknown> {
|
|
46
|
-
return threadStartResult(threadId);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function createAppServerOptions(): CodexAppServerRuntimeOptions {
|
|
50
|
-
return {
|
|
51
|
-
start: {
|
|
52
|
-
transport: "stdio",
|
|
53
|
-
command: "codex",
|
|
54
|
-
args: ["app-server"],
|
|
55
|
-
headers: {},
|
|
56
|
-
},
|
|
57
|
-
codeModeOnly: false,
|
|
58
|
-
requestTimeoutMs: 60_000,
|
|
59
|
-
turnCompletionIdleTimeoutMs: 60_000,
|
|
60
|
-
approvalPolicy: "never",
|
|
61
|
-
approvalsReviewer: "user",
|
|
62
|
-
sandbox: "workspace-write",
|
|
63
|
-
} as unknown as CodexAppServerRuntimeOptions;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function createParams(
|
|
67
|
-
sessionFile: string,
|
|
68
|
-
workspaceDir: string,
|
|
69
|
-
configOverrides?: EmbeddedRunAttemptParams["config"],
|
|
70
|
-
): EmbeddedRunAttemptParams {
|
|
71
|
-
return {
|
|
72
|
-
prompt: "hello",
|
|
73
|
-
sessionId: "session-1",
|
|
74
|
-
sessionKey: "agent:main:session-1",
|
|
75
|
-
sessionFile,
|
|
76
|
-
workspaceDir,
|
|
77
|
-
runId: "run-1",
|
|
78
|
-
provider: "codex",
|
|
79
|
-
modelId: "gpt-5.4-codex",
|
|
80
|
-
thinkLevel: "medium",
|
|
81
|
-
disableTools: true,
|
|
82
|
-
timeoutMs: 5_000,
|
|
83
|
-
authStorage: {} as never,
|
|
84
|
-
authProfileStore: { version: 1, profiles: {} },
|
|
85
|
-
modelRegistry: {} as never,
|
|
86
|
-
config: configOverrides,
|
|
87
|
-
} as unknown as EmbeddedRunAttemptParams;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
describe("startOrResumeThread — user mcp.servers projection (regression: #80814)", () => {
|
|
91
|
-
let tempDir = "";
|
|
92
|
-
|
|
93
|
-
beforeEach(async () => {
|
|
94
|
-
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "klaw-80814-"));
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
afterEach(async () => {
|
|
98
|
-
if (tempDir) {
|
|
99
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("projects cfg.mcp.servers into the thread/start config patch under mcp_servers", async () => {
|
|
104
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
105
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
106
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
107
|
-
if (method === "thread/start") {
|
|
108
|
-
return threadStartResult();
|
|
109
|
-
}
|
|
110
|
-
throw new Error(`unexpected method: ${method}`);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
await startOrResumeThread({
|
|
114
|
-
client: { request } as never,
|
|
115
|
-
params: createParams(sessionFile, workspaceDir, {
|
|
116
|
-
mcp: {
|
|
117
|
-
servers: {
|
|
118
|
-
outlook: {
|
|
119
|
-
transport: "stdio",
|
|
120
|
-
command: "node",
|
|
121
|
-
args: ["/opt/outlook-mcp/dist/index.js"],
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
} as unknown as EmbeddedRunAttemptParams["config"]),
|
|
126
|
-
cwd: workspaceDir,
|
|
127
|
-
dynamicTools: [],
|
|
128
|
-
appServer: createAppServerOptions(),
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const startCall = request.mock.calls.find(([method]) => method === "thread/start");
|
|
132
|
-
const startParams = startCall?.[1] as { config?: { mcp_servers?: Record<string, unknown> } };
|
|
133
|
-
expect(startParams?.config?.mcp_servers).toBeDefined();
|
|
134
|
-
expect(startParams.config!.mcp_servers).toMatchObject({
|
|
135
|
-
outlook: { command: "node", args: ["/opt/outlook-mcp/dist/index.js"] },
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("projects only Codex user MCP servers scoped to the current agent", async () => {
|
|
140
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
141
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
142
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
143
|
-
if (method === "thread/start") {
|
|
144
|
-
return threadStartResult();
|
|
145
|
-
}
|
|
146
|
-
throw new Error(`unexpected method: ${method}`);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
await startOrResumeThread({
|
|
150
|
-
client: { request } as never,
|
|
151
|
-
params: createParams(sessionFile, workspaceDir, {
|
|
152
|
-
mcp: {
|
|
153
|
-
servers: {
|
|
154
|
-
atlas: {
|
|
155
|
-
transport: "streamable-http",
|
|
156
|
-
url: "https://atlas.example.com/mcp",
|
|
157
|
-
codex: {
|
|
158
|
-
agents: ["atlas"],
|
|
159
|
-
defaultToolsApprovalMode: "approve",
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
apolo: {
|
|
163
|
-
transport: "streamable-http",
|
|
164
|
-
url: "https://apolo.example.com/mcp",
|
|
165
|
-
codex: {
|
|
166
|
-
agents: ["apolo"],
|
|
167
|
-
defaultToolsApprovalMode: "approve",
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
} as unknown as EmbeddedRunAttemptParams["config"]),
|
|
173
|
-
agentId: "atlas",
|
|
174
|
-
cwd: workspaceDir,
|
|
175
|
-
dynamicTools: [],
|
|
176
|
-
appServer: createAppServerOptions(),
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const startCall = request.mock.calls.find(([method]) => method === "thread/start");
|
|
180
|
-
const startParams = startCall?.[1] as { config?: { mcp_servers?: Record<string, unknown> } };
|
|
181
|
-
expect(startParams?.config?.mcp_servers).toStrictEqual({
|
|
182
|
-
atlas: {
|
|
183
|
-
url: "https://atlas.example.com/mcp",
|
|
184
|
-
default_tools_approval_mode: "approve",
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("omits mcp_servers from the start config when cfg has no user MCP servers", async () => {
|
|
190
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
191
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
192
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
193
|
-
if (method === "thread/start") {
|
|
194
|
-
return threadStartResult();
|
|
195
|
-
}
|
|
196
|
-
throw new Error(`unexpected method: ${method}`);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
await startOrResumeThread({
|
|
200
|
-
client: { request } as never,
|
|
201
|
-
params: createParams(sessionFile, workspaceDir),
|
|
202
|
-
cwd: workspaceDir,
|
|
203
|
-
dynamicTools: [],
|
|
204
|
-
appServer: createAppServerOptions(),
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
const startCall = request.mock.calls.find(([method]) => method === "thread/start");
|
|
208
|
-
const startParams = startCall?.[1] as { config?: { mcp_servers?: Record<string, unknown> } };
|
|
209
|
-
expect(startParams?.config?.mcp_servers).toBeUndefined();
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("omits user MCP servers when runtime policy disables native tool surfaces", async () => {
|
|
213
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
214
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
215
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
216
|
-
if (method === "thread/start") {
|
|
217
|
-
return threadStartResult();
|
|
218
|
-
}
|
|
219
|
-
throw new Error(`unexpected method: ${method}`);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
await startOrResumeThread({
|
|
223
|
-
client: { request } as never,
|
|
224
|
-
params: createParams(sessionFile, workspaceDir, {
|
|
225
|
-
mcp: {
|
|
226
|
-
servers: {
|
|
227
|
-
notes: {
|
|
228
|
-
transport: "stdio",
|
|
229
|
-
command: "node",
|
|
230
|
-
args: ["/opt/notes-mcp/dist/index.js"],
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
} as unknown as EmbeddedRunAttemptParams["config"]),
|
|
235
|
-
cwd: workspaceDir,
|
|
236
|
-
dynamicTools: [],
|
|
237
|
-
appServer: createAppServerOptions(),
|
|
238
|
-
nativeCodeModeEnabled: false,
|
|
239
|
-
userMcpServersEnabled: false,
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
const startCall = request.mock.calls.find(([method]) => method === "thread/start");
|
|
243
|
-
const startParams = startCall?.[1] as { config?: { mcp_servers?: Record<string, unknown> } };
|
|
244
|
-
expect(startParams?.config?.mcp_servers).toBeUndefined();
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it("starts a new thread when an existing binding lacks the matching user MCP fingerprint", async () => {
|
|
248
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
249
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
250
|
-
|
|
251
|
-
await writeCodexAppServerBinding(sessionFile, {
|
|
252
|
-
threadId: "thread-existing",
|
|
253
|
-
cwd: workspaceDir,
|
|
254
|
-
model: "gpt-5.4-codex",
|
|
255
|
-
modelProvider: "openai",
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
259
|
-
if (method === "thread/start") {
|
|
260
|
-
return threadStartResult("thread-restarted");
|
|
261
|
-
}
|
|
262
|
-
throw new Error(`unexpected method: ${method}`);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
await startOrResumeThread({
|
|
266
|
-
client: { request } as never,
|
|
267
|
-
params: createParams(sessionFile, workspaceDir, {
|
|
268
|
-
mcp: {
|
|
269
|
-
servers: {
|
|
270
|
-
notes: {
|
|
271
|
-
transport: "stdio",
|
|
272
|
-
command: "node",
|
|
273
|
-
args: ["/opt/notes-mcp/dist/index.js"],
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
} as unknown as EmbeddedRunAttemptParams["config"]),
|
|
278
|
-
cwd: workspaceDir,
|
|
279
|
-
dynamicTools: [],
|
|
280
|
-
appServer: createAppServerOptions(),
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
expect(request.mock.calls.some(([method]) => method === "thread/resume")).toBe(false);
|
|
284
|
-
const startCall = request.mock.calls.find(([method]) => method === "thread/start");
|
|
285
|
-
const startParams = startCall?.[1] as {
|
|
286
|
-
config?: { mcp_servers?: Record<string, unknown> };
|
|
287
|
-
};
|
|
288
|
-
expect(startParams?.config?.mcp_servers).toBeDefined();
|
|
289
|
-
expect(startParams.config!.mcp_servers).toMatchObject({
|
|
290
|
-
notes: { command: "node", args: ["/opt/notes-mcp/dist/index.js"] },
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it("does not resume an existing native thread when runtime policy disables native tools", async () => {
|
|
295
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
296
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
297
|
-
await writeCodexAppServerBinding(sessionFile, {
|
|
298
|
-
threadId: "thread-native",
|
|
299
|
-
cwd: workspaceDir,
|
|
300
|
-
model: "gpt-5.4-codex",
|
|
301
|
-
modelProvider: "openai",
|
|
302
|
-
});
|
|
303
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
304
|
-
if (method === "thread/start") {
|
|
305
|
-
return threadStartResult("thread-restricted");
|
|
306
|
-
}
|
|
307
|
-
if (method === "thread/resume") {
|
|
308
|
-
return threadResumeResult("thread-native");
|
|
309
|
-
}
|
|
310
|
-
throw new Error(`unexpected method: ${method}`);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
await startOrResumeThread({
|
|
314
|
-
client: { request } as never,
|
|
315
|
-
params: createParams(sessionFile, workspaceDir),
|
|
316
|
-
cwd: workspaceDir,
|
|
317
|
-
dynamicTools: [],
|
|
318
|
-
appServer: createAppServerOptions(),
|
|
319
|
-
nativeCodeModeEnabled: false,
|
|
320
|
-
userMcpServersEnabled: false,
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
expect(request.mock.calls.map(([method]) => method)).toEqual(["thread/start"]);
|
|
324
|
-
const startParams = request.mock.calls[0]?.[1] as {
|
|
325
|
-
environments?: unknown[];
|
|
326
|
-
config?: {
|
|
327
|
-
"features.code_mode"?: boolean;
|
|
328
|
-
"features.code_mode_only"?: boolean;
|
|
329
|
-
mcp_servers?: Record<string, unknown>;
|
|
330
|
-
};
|
|
331
|
-
};
|
|
332
|
-
expect(startParams?.environments).toEqual([]);
|
|
333
|
-
expect(startParams?.config?.["features.code_mode"]).toBe(false);
|
|
334
|
-
expect(startParams?.config?.["features.code_mode_only"]).toBe(false);
|
|
335
|
-
expect(startParams?.config?.mcp_servers).toBeUndefined();
|
|
336
|
-
const preservedBinding = await readCodexAppServerBinding(sessionFile);
|
|
337
|
-
expect(preservedBinding?.threadId).toBe("thread-native");
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it("starts a new thread without user MCP servers when runtime policy disables them", async () => {
|
|
341
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
342
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
343
|
-
const config = {
|
|
344
|
-
mcp: {
|
|
345
|
-
servers: {
|
|
346
|
-
notes: {
|
|
347
|
-
transport: "stdio",
|
|
348
|
-
command: "node",
|
|
349
|
-
args: ["/opt/notes-mcp/dist/index.js"],
|
|
350
|
-
},
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
} as unknown as EmbeddedRunAttemptParams["config"];
|
|
354
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
355
|
-
if (method === "thread/start") {
|
|
356
|
-
return threadStartResult("thread-started");
|
|
357
|
-
}
|
|
358
|
-
if (method === "thread/resume") {
|
|
359
|
-
return threadResumeResult("thread-existing");
|
|
360
|
-
}
|
|
361
|
-
throw new Error(`unexpected method: ${method}`);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
await startOrResumeThread({
|
|
365
|
-
client: { request } as never,
|
|
366
|
-
params: createParams(sessionFile, workspaceDir, config),
|
|
367
|
-
cwd: workspaceDir,
|
|
368
|
-
dynamicTools: [],
|
|
369
|
-
appServer: createAppServerOptions(),
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
request.mockClear();
|
|
373
|
-
|
|
374
|
-
await startOrResumeThread({
|
|
375
|
-
client: { request } as never,
|
|
376
|
-
params: createParams(sessionFile, workspaceDir, config),
|
|
377
|
-
cwd: workspaceDir,
|
|
378
|
-
dynamicTools: [],
|
|
379
|
-
appServer: createAppServerOptions(),
|
|
380
|
-
nativeCodeModeEnabled: false,
|
|
381
|
-
userMcpServersEnabled: false,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
expect(request.mock.calls.map(([method]) => method)).toEqual(["thread/start"]);
|
|
385
|
-
const startParams = request.mock.calls[0]?.[1] as {
|
|
386
|
-
config?: { mcp_servers?: Record<string, unknown> };
|
|
387
|
-
};
|
|
388
|
-
expect(startParams?.config?.mcp_servers).toBeUndefined();
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
it("resends user MCP config when resuming a thread with the matching fingerprint", async () => {
|
|
392
|
-
const sessionFile = path.join(tempDir, "session.jsonl");
|
|
393
|
-
const workspaceDir = path.join(tempDir, "workspace");
|
|
394
|
-
const config = {
|
|
395
|
-
mcp: {
|
|
396
|
-
servers: {
|
|
397
|
-
notes: {
|
|
398
|
-
transport: "stdio",
|
|
399
|
-
command: "node",
|
|
400
|
-
args: ["/opt/notes-mcp/dist/index.js"],
|
|
401
|
-
},
|
|
402
|
-
},
|
|
403
|
-
},
|
|
404
|
-
} as unknown as EmbeddedRunAttemptParams["config"];
|
|
405
|
-
const request = vi.fn(async (method: string, _params: unknown) => {
|
|
406
|
-
if (method === "thread/start") {
|
|
407
|
-
return threadStartResult("thread-with-user-mcp");
|
|
408
|
-
}
|
|
409
|
-
if (method === "thread/resume") {
|
|
410
|
-
return threadResumeResult("thread-with-user-mcp");
|
|
411
|
-
}
|
|
412
|
-
throw new Error(`unexpected method: ${method}`);
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
await startOrResumeThread({
|
|
416
|
-
client: { request } as never,
|
|
417
|
-
params: createParams(sessionFile, workspaceDir, config),
|
|
418
|
-
cwd: workspaceDir,
|
|
419
|
-
dynamicTools: [],
|
|
420
|
-
appServer: createAppServerOptions(),
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
request.mockClear();
|
|
424
|
-
|
|
425
|
-
await startOrResumeThread({
|
|
426
|
-
client: { request } as never,
|
|
427
|
-
params: createParams(sessionFile, workspaceDir, config),
|
|
428
|
-
cwd: workspaceDir,
|
|
429
|
-
dynamicTools: [],
|
|
430
|
-
appServer: createAppServerOptions(),
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
const resumeCall = request.mock.calls.find(([method]) => method === "thread/resume");
|
|
434
|
-
const resumeParams = resumeCall?.[1] as {
|
|
435
|
-
config?: { mcp_servers?: Record<string, unknown> };
|
|
436
|
-
};
|
|
437
|
-
expect(resumeCall).toBeDefined();
|
|
438
|
-
expect(resumeParams?.config?.mcp_servers).toMatchObject({
|
|
439
|
-
notes: { command: "node", args: ["/opt/notes-mcp/dist/index.js"] },
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { withTimeout as withSharedTimeout } from "klaw/plugin-sdk/security-runtime";
|
|
2
|
-
|
|
3
|
-
export async function withTimeout<T>(
|
|
4
|
-
promise: Promise<T>,
|
|
5
|
-
timeoutMs: number,
|
|
6
|
-
timeoutMessage: string,
|
|
7
|
-
): Promise<T> {
|
|
8
|
-
return await withSharedTimeout(promise, timeoutMs, { message: timeoutMessage });
|
|
9
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
inferToolMetaFromArgs,
|
|
3
|
-
type EmbeddedRunAttemptParams,
|
|
4
|
-
type ToolProgressDetailMode,
|
|
5
|
-
} from "klaw/plugin-sdk/agent-harness-runtime";
|
|
6
|
-
import { redactSensitiveFieldValue, redactToolPayloadText } from "klaw/plugin-sdk/logging-core";
|
|
7
|
-
import {
|
|
8
|
-
isJsonObject,
|
|
9
|
-
type CodexDynamicToolCallParams,
|
|
10
|
-
type CodexDynamicToolCallResponse,
|
|
11
|
-
type JsonValue,
|
|
12
|
-
} from "./protocol.js";
|
|
13
|
-
|
|
14
|
-
export function resolveCodexToolProgressDetailMode(
|
|
15
|
-
value: EmbeddedRunAttemptParams["toolProgressDetail"],
|
|
16
|
-
): ToolProgressDetailMode {
|
|
17
|
-
return value === "raw" ? "raw" : "explain";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function sanitizeCodexAgentEventValue(
|
|
21
|
-
value: unknown,
|
|
22
|
-
seen = new WeakSet<object>(),
|
|
23
|
-
): unknown {
|
|
24
|
-
if (typeof value === "string") {
|
|
25
|
-
return redactToolPayloadText(value);
|
|
26
|
-
}
|
|
27
|
-
if (Array.isArray(value)) {
|
|
28
|
-
if (seen.has(value)) {
|
|
29
|
-
return "[Circular]";
|
|
30
|
-
}
|
|
31
|
-
seen.add(value);
|
|
32
|
-
return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
|
|
33
|
-
}
|
|
34
|
-
if (value && typeof value === "object") {
|
|
35
|
-
if (seen.has(value)) {
|
|
36
|
-
return "[Circular]";
|
|
37
|
-
}
|
|
38
|
-
seen.add(value);
|
|
39
|
-
const out: Record<string, unknown> = {};
|
|
40
|
-
for (const [key, child] of Object.entries(value as Record<string, unknown>)) {
|
|
41
|
-
out[key] =
|
|
42
|
-
typeof child === "string"
|
|
43
|
-
? redactSensitiveFieldValue(key, child)
|
|
44
|
-
: sanitizeCodexAgentEventValue(child, seen);
|
|
45
|
-
}
|
|
46
|
-
return out;
|
|
47
|
-
}
|
|
48
|
-
return value;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function sanitizeCodexAgentEventRecord(
|
|
52
|
-
value: Record<string, unknown>,
|
|
53
|
-
): Record<string, unknown> {
|
|
54
|
-
return sanitizeCodexAgentEventValue(value) as Record<string, unknown>;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function sanitizeCodexToolArguments(
|
|
58
|
-
value: JsonValue | undefined,
|
|
59
|
-
): Record<string, unknown> | undefined {
|
|
60
|
-
if (!isJsonObject(value)) {
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
return sanitizeCodexAgentEventRecord(value);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function sanitizeCodexToolResponse(
|
|
67
|
-
response: CodexDynamicToolCallResponse,
|
|
68
|
-
): Record<string, unknown> {
|
|
69
|
-
return sanitizeCodexAgentEventRecord(response as unknown as Record<string, unknown>);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function inferCodexDynamicToolMeta(
|
|
73
|
-
call: Pick<CodexDynamicToolCallParams, "tool" | "arguments">,
|
|
74
|
-
detailMode: ToolProgressDetailMode,
|
|
75
|
-
): string | undefined {
|
|
76
|
-
return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
|
|
77
|
-
}
|