@kodelyth/codex 2026.5.42 → 2026.6.2
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 +17 -2
- 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,879 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
import {
|
|
4
|
-
CODEX_APP_SERVER_CONFIG_KEYS,
|
|
5
|
-
CODEX_COMPUTER_USE_CONFIG_KEYS,
|
|
6
|
-
CODEX_PLUGIN_ENTRY_CONFIG_KEYS,
|
|
7
|
-
CODEX_PLUGINS_CONFIG_KEYS,
|
|
8
|
-
codexAppServerStartOptionsKey,
|
|
9
|
-
readCodexPluginConfig,
|
|
10
|
-
resolveCodexAppServerRuntimeOptions,
|
|
11
|
-
resolveCodexComputerUseConfig,
|
|
12
|
-
resolveCodexPluginsPolicy,
|
|
13
|
-
} from "./config.js";
|
|
14
|
-
|
|
15
|
-
type RuntimeOptionsParams = NonNullable<Parameters<typeof resolveCodexAppServerRuntimeOptions>[0]>;
|
|
16
|
-
|
|
17
|
-
function resolveRuntimeForTest(params: RuntimeOptionsParams = {}) {
|
|
18
|
-
return resolveCodexAppServerRuntimeOptions({ env: {}, requirementsToml: null, ...params });
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function requireRecord(value: unknown, label: string): Record<string, unknown> {
|
|
22
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
23
|
-
throw new Error(`Expected ${label}`);
|
|
24
|
-
}
|
|
25
|
-
return value as Record<string, unknown>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function expectFields(
|
|
29
|
-
value: unknown,
|
|
30
|
-
label: string,
|
|
31
|
-
fields: Record<string, unknown>,
|
|
32
|
-
): Record<string, unknown> {
|
|
33
|
-
const record = requireRecord(value, label);
|
|
34
|
-
for (const [key, expected] of Object.entries(fields)) {
|
|
35
|
-
expect(record[key]).toEqual(expected);
|
|
36
|
-
}
|
|
37
|
-
return record;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function expectRuntimePolicy(
|
|
41
|
-
runtime: unknown,
|
|
42
|
-
fields: {
|
|
43
|
-
approvalPolicy: string;
|
|
44
|
-
sandbox: string;
|
|
45
|
-
approvalsReviewer: string;
|
|
46
|
-
},
|
|
47
|
-
) {
|
|
48
|
-
expectFields(runtime, "runtime policy", fields);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function expectUiHintLabel(manifest: { uiHints: Record<string, unknown> }, key: string) {
|
|
52
|
-
const hint = requireRecord(manifest.uiHints[key], `${key} UI hint`);
|
|
53
|
-
expect(typeof hint.label).toBe("string");
|
|
54
|
-
expect((hint.label as string).length).toBeGreaterThan(0);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
describe("Codex app-server config", () => {
|
|
58
|
-
it("parses typed plugin config before falling back to environment knobs", () => {
|
|
59
|
-
const runtime = resolveRuntimeForTest({
|
|
60
|
-
pluginConfig: {
|
|
61
|
-
appServer: {
|
|
62
|
-
mode: "guardian",
|
|
63
|
-
transport: "websocket",
|
|
64
|
-
url: "ws://127.0.0.1:39175",
|
|
65
|
-
headers: { "X-Test": "yes" },
|
|
66
|
-
approvalPolicy: "on-request",
|
|
67
|
-
sandbox: "danger-full-access",
|
|
68
|
-
approvalsReviewer: "guardian_subagent",
|
|
69
|
-
serviceTier: "flex",
|
|
70
|
-
codeModeOnly: true,
|
|
71
|
-
turnCompletionIdleTimeoutMs: 120_000,
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
env: {
|
|
75
|
-
KLAW_CODEX_APP_SERVER_APPROVAL_POLICY: "never",
|
|
76
|
-
KLAW_CODEX_APP_SERVER_SANDBOX: "read-only",
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
expectFields(runtime, "runtime", {
|
|
81
|
-
approvalPolicy: "on-request",
|
|
82
|
-
sandbox: "danger-full-access",
|
|
83
|
-
approvalsReviewer: "guardian_subagent",
|
|
84
|
-
serviceTier: "flex",
|
|
85
|
-
codeModeOnly: true,
|
|
86
|
-
turnCompletionIdleTimeoutMs: 120_000,
|
|
87
|
-
});
|
|
88
|
-
expectFields(runtime.start, "runtime start", {
|
|
89
|
-
transport: "websocket",
|
|
90
|
-
url: "ws://127.0.0.1:39175",
|
|
91
|
-
headers: { "X-Test": "yes" },
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("ignores app-server environment clearing for websocket transports", () => {
|
|
96
|
-
const runtime = resolveRuntimeForTest({
|
|
97
|
-
pluginConfig: {
|
|
98
|
-
appServer: {
|
|
99
|
-
transport: "websocket",
|
|
100
|
-
url: "ws://127.0.0.1:39175",
|
|
101
|
-
clearEnv: ["OPENAI_API_KEY"],
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
env: {},
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
expect(runtime.start).not.toHaveProperty("clearEnv");
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("normalizes app-server environment variables to clear", () => {
|
|
111
|
-
const runtime = resolveRuntimeForTest({
|
|
112
|
-
pluginConfig: {
|
|
113
|
-
appServer: {
|
|
114
|
-
clearEnv: [" OPENAI_API_KEY ", "", " "],
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
env: {},
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
expectFields(runtime.start, "runtime start", {
|
|
121
|
-
clearEnv: ["OPENAI_API_KEY"],
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("normalizes legacy service tiers without discarding the rest of the config", () => {
|
|
126
|
-
const runtime = resolveRuntimeForTest({
|
|
127
|
-
pluginConfig: {
|
|
128
|
-
appServer: {
|
|
129
|
-
mode: "guardian",
|
|
130
|
-
approvalPolicy: "on-request",
|
|
131
|
-
sandbox: "read-only",
|
|
132
|
-
serviceTier: "fast",
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
env: {},
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
expectFields(runtime, "runtime", {
|
|
139
|
-
approvalPolicy: "on-request",
|
|
140
|
-
sandbox: "read-only",
|
|
141
|
-
approvalsReviewer: "auto_review",
|
|
142
|
-
serviceTier: "priority",
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("passes through non-empty Codex app-server service tiers for forward compatibility", () => {
|
|
147
|
-
const runtime = resolveCodexAppServerRuntimeOptions({
|
|
148
|
-
pluginConfig: {
|
|
149
|
-
appServer: {
|
|
150
|
-
serviceTier: "batch-preview",
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
env: {},
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
expect(runtime.serviceTier).toBe("batch-preview");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("rejects malformed plugin config instead of treating freeform strings as control values", () => {
|
|
160
|
-
expect(
|
|
161
|
-
readCodexPluginConfig({
|
|
162
|
-
appServer: {
|
|
163
|
-
approvalPolicy: "always",
|
|
164
|
-
},
|
|
165
|
-
}),
|
|
166
|
-
).toStrictEqual({});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("requires a websocket url when websocket transport is configured", () => {
|
|
170
|
-
expect(() =>
|
|
171
|
-
resolveRuntimeForTest({
|
|
172
|
-
pluginConfig: { appServer: { transport: "websocket" } },
|
|
173
|
-
env: {},
|
|
174
|
-
}),
|
|
175
|
-
).toThrow("appServer.url is required");
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("defaults native Codex approvals to unchained local execution", () => {
|
|
179
|
-
const runtime = resolveRuntimeForTest({
|
|
180
|
-
pluginConfig: {},
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
expectRuntimePolicy(runtime, {
|
|
184
|
-
approvalPolicy: "never",
|
|
185
|
-
sandbox: "danger-full-access",
|
|
186
|
-
approvalsReviewer: "user",
|
|
187
|
-
});
|
|
188
|
-
expect(runtime.codeModeOnly).toBe(false);
|
|
189
|
-
expectFields(runtime.start, "runtime start", {
|
|
190
|
-
command: "codex",
|
|
191
|
-
commandSource: "managed",
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("defaults native Codex approvals to guardian when requirements disallow full access", () => {
|
|
196
|
-
const runtime = resolveRuntimeForTest({
|
|
197
|
-
pluginConfig: {},
|
|
198
|
-
requirementsToml: 'allowed_sandbox_modes = ["read-only", "workspace-write"]\n',
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
expectRuntimePolicy(runtime, {
|
|
202
|
-
approvalPolicy: "on-request",
|
|
203
|
-
sandbox: "workspace-write",
|
|
204
|
-
approvalsReviewer: "auto_review",
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it("uses read-only sandbox for guardian defaults when requirements only allow read-only", () => {
|
|
209
|
-
const runtime = resolveRuntimeForTest({
|
|
210
|
-
pluginConfig: {},
|
|
211
|
-
requirementsToml: 'allowed_sandbox_modes = ["read-only"]\n',
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
expectRuntimePolicy(runtime, {
|
|
215
|
-
approvalPolicy: "on-request",
|
|
216
|
-
sandbox: "read-only",
|
|
217
|
-
approvalsReviewer: "auto_review",
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it("defaults native Codex approvals to guardian when requirements disallow never approval", () => {
|
|
222
|
-
const runtime = resolveRuntimeForTest({
|
|
223
|
-
pluginConfig: {},
|
|
224
|
-
requirementsToml: 'allowed_approval_policies = ["on-request"]\n',
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
expectRuntimePolicy(runtime, {
|
|
228
|
-
approvalPolicy: "on-request",
|
|
229
|
-
sandbox: "workspace-write",
|
|
230
|
-
approvalsReviewer: "auto_review",
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("selects an allowed guardian approval policy when on-request is unavailable", () => {
|
|
235
|
-
const runtime = resolveRuntimeForTest({
|
|
236
|
-
pluginConfig: {},
|
|
237
|
-
requirementsToml: 'allowed_approval_policies = ["on-failure"]\n',
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
expectRuntimePolicy(runtime, {
|
|
241
|
-
approvalPolicy: "on-failure",
|
|
242
|
-
sandbox: "workspace-write",
|
|
243
|
-
approvalsReviewer: "auto_review",
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it("keeps native Codex approvals unchained when requirements allow never approval", () => {
|
|
248
|
-
const runtime = resolveRuntimeForTest({
|
|
249
|
-
pluginConfig: {},
|
|
250
|
-
requirementsToml: 'allowed_approval_policies = ["never"]\n',
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
expectRuntimePolicy(runtime, {
|
|
254
|
-
approvalPolicy: "never",
|
|
255
|
-
sandbox: "danger-full-access",
|
|
256
|
-
approvalsReviewer: "user",
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it("defaults native Codex approvals to guardian when requirements disallow user reviewer", () => {
|
|
261
|
-
const runtime = resolveRuntimeForTest({
|
|
262
|
-
pluginConfig: {},
|
|
263
|
-
requirementsToml: 'allowed_approvals_reviewers = ["auto_review"]\n',
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
expectRuntimePolicy(runtime, {
|
|
267
|
-
approvalPolicy: "on-request",
|
|
268
|
-
sandbox: "workspace-write",
|
|
269
|
-
approvalsReviewer: "auto_review",
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it("selects an allowed reviewer when sandbox requirements force guardian defaults", () => {
|
|
274
|
-
const runtime = resolveRuntimeForTest({
|
|
275
|
-
pluginConfig: {},
|
|
276
|
-
requirementsToml:
|
|
277
|
-
'allowed_sandbox_modes = ["read-only", "workspace-write"]\nallowed_approvals_reviewers = ["user"]\n',
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
expectRuntimePolicy(runtime, {
|
|
281
|
-
approvalPolicy: "on-request",
|
|
282
|
-
sandbox: "workspace-write",
|
|
283
|
-
approvalsReviewer: "user",
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it("ignores quoted sandbox modes inside requirements comments", () => {
|
|
288
|
-
const runtime = resolveRuntimeForTest({
|
|
289
|
-
pluginConfig: {},
|
|
290
|
-
requirementsToml: `allowed_sandbox_modes = [
|
|
291
|
-
"read-only",
|
|
292
|
-
# "danger-full-access",
|
|
293
|
-
"workspace-write",
|
|
294
|
-
]
|
|
295
|
-
`,
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
expectRuntimePolicy(runtime, {
|
|
299
|
-
approvalPolicy: "on-request",
|
|
300
|
-
sandbox: "workspace-write",
|
|
301
|
-
approvalsReviewer: "auto_review",
|
|
302
|
-
});
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it("applies the first matching remote sandbox requirements before resolving local stdio defaults", () => {
|
|
306
|
-
const runtime = resolveRuntimeForTest({
|
|
307
|
-
pluginConfig: {},
|
|
308
|
-
hostName: "BUILD-01.EXAMPLE.COM.",
|
|
309
|
-
requirementsToml: `[[remote_sandbox_config]]
|
|
310
|
-
hostname_patterns = ["build-*.example.com"]
|
|
311
|
-
allowed_sandbox_modes = ["read-only", "workspace-write"]
|
|
312
|
-
|
|
313
|
-
[[remote_sandbox_config]]
|
|
314
|
-
hostname_patterns = ["build-01.example.com"]
|
|
315
|
-
allowed_sandbox_modes = ["read-only", "danger-full-access"]
|
|
316
|
-
`,
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
expectRuntimePolicy(runtime, {
|
|
320
|
-
approvalPolicy: "on-request",
|
|
321
|
-
sandbox: "workspace-write",
|
|
322
|
-
approvalsReviewer: "auto_review",
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
it("ignores non-matching remote-only sandbox requirements when resolving local stdio defaults", () => {
|
|
327
|
-
const runtime = resolveRuntimeForTest({
|
|
328
|
-
pluginConfig: {},
|
|
329
|
-
hostName: "laptop.example.com",
|
|
330
|
-
requirementsToml: `[[remote_sandbox_config]]
|
|
331
|
-
hostname_patterns = ["build-*.example.com"]
|
|
332
|
-
allowed_sandbox_modes = ["read-only", "workspace-write"]
|
|
333
|
-
`,
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
expectRuntimePolicy(runtime, {
|
|
337
|
-
approvalPolicy: "never",
|
|
338
|
-
sandbox: "danger-full-access",
|
|
339
|
-
approvalsReviewer: "user",
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it("reads local requirements policy from the configured requirements path", () => {
|
|
344
|
-
const readPaths: string[] = [];
|
|
345
|
-
const runtime = resolveCodexAppServerRuntimeOptions({
|
|
346
|
-
pluginConfig: {},
|
|
347
|
-
env: {},
|
|
348
|
-
requirementsPath: "/custom/codex/requirements.toml",
|
|
349
|
-
readRequirementsFile: (path) => {
|
|
350
|
-
readPaths.push(path);
|
|
351
|
-
return 'allowed_sandbox_modes = ["read-only", "workspace-write"]\n';
|
|
352
|
-
},
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
expect(readPaths).toEqual(["/custom/codex/requirements.toml"]);
|
|
356
|
-
expectRuntimePolicy(runtime, {
|
|
357
|
-
approvalPolicy: "on-request",
|
|
358
|
-
sandbox: "workspace-write",
|
|
359
|
-
approvalsReviewer: "auto_review",
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it("reads local requirements policy from the Codex Windows requirements path", () => {
|
|
364
|
-
const readPaths: string[] = [];
|
|
365
|
-
const runtime = resolveCodexAppServerRuntimeOptions({
|
|
366
|
-
pluginConfig: {},
|
|
367
|
-
env: { ProgramData: "D:\\ManagedData" },
|
|
368
|
-
platform: "win32",
|
|
369
|
-
readRequirementsFile: (path) => {
|
|
370
|
-
readPaths.push(path);
|
|
371
|
-
return 'allowed_sandbox_modes = ["read-only", "workspace-write"]\n';
|
|
372
|
-
},
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
expect(readPaths).toEqual(["D:\\ManagedData\\OpenAI\\Codex\\requirements.toml"]);
|
|
376
|
-
expectRuntimePolicy(runtime, {
|
|
377
|
-
approvalPolicy: "on-request",
|
|
378
|
-
sandbox: "workspace-write",
|
|
379
|
-
approvalsReviewer: "auto_review",
|
|
380
|
-
});
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
it("keeps native Codex approvals unchained when requirements allow full access", () => {
|
|
384
|
-
const runtime = resolveRuntimeForTest({
|
|
385
|
-
pluginConfig: {},
|
|
386
|
-
requirementsToml:
|
|
387
|
-
'allowed_sandbox_modes = ["ReadOnly", "WorkspaceWrite", "DangerFullAccess"]\n',
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
expectRuntimePolicy(runtime, {
|
|
391
|
-
approvalPolicy: "never",
|
|
392
|
-
sandbox: "danger-full-access",
|
|
393
|
-
approvalsReviewer: "user",
|
|
394
|
-
});
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
it("keeps native Codex approvals unchained when requirements are malformed", () => {
|
|
398
|
-
const runtime = resolveRuntimeForTest({
|
|
399
|
-
pluginConfig: {},
|
|
400
|
-
requirementsToml: "allowed_sandbox_modes = [read-only]\n",
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
expectRuntimePolicy(runtime, {
|
|
404
|
-
approvalPolicy: "never",
|
|
405
|
-
sandbox: "danger-full-access",
|
|
406
|
-
approvalsReviewer: "user",
|
|
407
|
-
});
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it("does not apply local requirements policy to websocket app-server transports", () => {
|
|
411
|
-
const runtime = resolveRuntimeForTest({
|
|
412
|
-
pluginConfig: {
|
|
413
|
-
appServer: {
|
|
414
|
-
transport: "websocket",
|
|
415
|
-
url: "ws://127.0.0.1:39175",
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
requirementsToml: 'allowed_sandbox_modes = ["read-only", "workspace-write"]\n',
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
expectRuntimePolicy(runtime, {
|
|
422
|
-
approvalPolicy: "never",
|
|
423
|
-
sandbox: "danger-full-access",
|
|
424
|
-
approvalsReviewer: "user",
|
|
425
|
-
});
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it("keeps explicit yolo mode when requirements disallow full access", () => {
|
|
429
|
-
const requirementsToml = 'allowed_sandbox_modes = ["read-only", "workspace-write"]\n';
|
|
430
|
-
expectRuntimePolicy(
|
|
431
|
-
resolveRuntimeForTest({
|
|
432
|
-
pluginConfig: { appServer: { mode: "yolo" } },
|
|
433
|
-
requirementsToml,
|
|
434
|
-
}),
|
|
435
|
-
{
|
|
436
|
-
approvalPolicy: "never",
|
|
437
|
-
sandbox: "danger-full-access",
|
|
438
|
-
approvalsReviewer: "user",
|
|
439
|
-
},
|
|
440
|
-
);
|
|
441
|
-
expectRuntimePolicy(
|
|
442
|
-
resolveRuntimeForTest({
|
|
443
|
-
pluginConfig: {},
|
|
444
|
-
env: { KLAW_CODEX_APP_SERVER_MODE: "yolo" },
|
|
445
|
-
requirementsToml,
|
|
446
|
-
}),
|
|
447
|
-
{
|
|
448
|
-
approvalPolicy: "never",
|
|
449
|
-
sandbox: "danger-full-access",
|
|
450
|
-
approvalsReviewer: "user",
|
|
451
|
-
},
|
|
452
|
-
);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
it("parses dynamic tool controls", () => {
|
|
456
|
-
expect(
|
|
457
|
-
readCodexPluginConfig({
|
|
458
|
-
codexDynamicToolsLoading: "direct",
|
|
459
|
-
codexDynamicToolsExclude: ["custom_tool"],
|
|
460
|
-
}),
|
|
461
|
-
).toEqual({
|
|
462
|
-
codexDynamicToolsLoading: "direct",
|
|
463
|
-
codexDynamicToolsExclude: ["custom_tool"],
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
it("rejects the retired dynamic tool profile key", () => {
|
|
468
|
-
expect(
|
|
469
|
-
readCodexPluginConfig({
|
|
470
|
-
codexDynamicToolsProfile: "klaw-compat",
|
|
471
|
-
codexDynamicToolsLoading: "direct",
|
|
472
|
-
}),
|
|
473
|
-
).toEqual({});
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
it("parses native Codex plugin policy without treating wildcard as supported config", () => {
|
|
477
|
-
const config = readCodexPluginConfig({
|
|
478
|
-
appServer: { mode: "guardian" },
|
|
479
|
-
codexPlugins: {
|
|
480
|
-
enabled: true,
|
|
481
|
-
allow_destructive_actions: false,
|
|
482
|
-
plugins: {
|
|
483
|
-
"google-calendar": {
|
|
484
|
-
marketplaceName: "openai-curated",
|
|
485
|
-
pluginName: "google-calendar",
|
|
486
|
-
allow_destructive_actions: true,
|
|
487
|
-
},
|
|
488
|
-
slack: {
|
|
489
|
-
enabled: false,
|
|
490
|
-
marketplaceName: "openai-curated",
|
|
491
|
-
pluginName: "slack",
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
expect(config.appServer?.mode).toBe("guardian");
|
|
498
|
-
expect(config.codexPlugins?.enabled).toBe(true);
|
|
499
|
-
|
|
500
|
-
const policy = resolveCodexPluginsPolicy(config);
|
|
501
|
-
expect(policy).toEqual({
|
|
502
|
-
configured: true,
|
|
503
|
-
enabled: true,
|
|
504
|
-
allowDestructiveActions: false,
|
|
505
|
-
pluginPolicies: [
|
|
506
|
-
{
|
|
507
|
-
configKey: "google-calendar",
|
|
508
|
-
marketplaceName: "openai-curated",
|
|
509
|
-
pluginName: "google-calendar",
|
|
510
|
-
enabled: true,
|
|
511
|
-
allowDestructiveActions: true,
|
|
512
|
-
},
|
|
513
|
-
{
|
|
514
|
-
configKey: "slack",
|
|
515
|
-
marketplaceName: "openai-curated",
|
|
516
|
-
pluginName: "slack",
|
|
517
|
-
enabled: false,
|
|
518
|
-
allowDestructiveActions: false,
|
|
519
|
-
},
|
|
520
|
-
],
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
it("defaults native Codex plugin destructive policy to enabled", () => {
|
|
525
|
-
const policy = resolveCodexPluginsPolicy({
|
|
526
|
-
codexPlugins: {
|
|
527
|
-
enabled: true,
|
|
528
|
-
plugins: {
|
|
529
|
-
slack: {
|
|
530
|
-
marketplaceName: "openai-curated",
|
|
531
|
-
pluginName: "slack",
|
|
532
|
-
},
|
|
533
|
-
},
|
|
534
|
-
},
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
expect(policy).toEqual({
|
|
538
|
-
configured: true,
|
|
539
|
-
enabled: true,
|
|
540
|
-
allowDestructiveActions: true,
|
|
541
|
-
pluginPolicies: [
|
|
542
|
-
{
|
|
543
|
-
configKey: "slack",
|
|
544
|
-
marketplaceName: "openai-curated",
|
|
545
|
-
pluginName: "slack",
|
|
546
|
-
enabled: true,
|
|
547
|
-
allowDestructiveActions: true,
|
|
548
|
-
},
|
|
549
|
-
],
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
it("rejects non-curated native plugin identities", () => {
|
|
554
|
-
const config = readCodexPluginConfig({
|
|
555
|
-
codexPlugins: {
|
|
556
|
-
enabled: true,
|
|
557
|
-
plugins: {
|
|
558
|
-
gmail: {
|
|
559
|
-
marketplaceName: "custom-market",
|
|
560
|
-
pluginName: "gmail",
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
expect(config.codexPlugins).toBeUndefined();
|
|
567
|
-
expect(resolveCodexPluginsPolicy(config).pluginPolicies).toStrictEqual([]);
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
it("treats configured and environment commands as explicit overrides", () => {
|
|
571
|
-
expectFields(
|
|
572
|
-
resolveRuntimeForTest({
|
|
573
|
-
pluginConfig: { appServer: { command: "/opt/codex/bin/codex" } },
|
|
574
|
-
env: { KLAW_CODEX_APP_SERVER_BIN: "/usr/local/bin/codex" },
|
|
575
|
-
}).start,
|
|
576
|
-
"configured start",
|
|
577
|
-
{
|
|
578
|
-
command: "/opt/codex/bin/codex",
|
|
579
|
-
commandSource: "config",
|
|
580
|
-
},
|
|
581
|
-
);
|
|
582
|
-
|
|
583
|
-
expectFields(
|
|
584
|
-
resolveRuntimeForTest({
|
|
585
|
-
pluginConfig: {},
|
|
586
|
-
env: { KLAW_CODEX_APP_SERVER_BIN: "/usr/local/bin/codex" },
|
|
587
|
-
}).start,
|
|
588
|
-
"environment start",
|
|
589
|
-
{
|
|
590
|
-
command: "/usr/local/bin/codex",
|
|
591
|
-
commandSource: "env",
|
|
592
|
-
},
|
|
593
|
-
);
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
it("resolves Computer Use setup from plugin config and environment fallbacks", () => {
|
|
597
|
-
expect(
|
|
598
|
-
resolveCodexComputerUseConfig({
|
|
599
|
-
pluginConfig: {
|
|
600
|
-
computerUse: {
|
|
601
|
-
autoInstall: true,
|
|
602
|
-
marketplaceName: "desktop-tools",
|
|
603
|
-
},
|
|
604
|
-
},
|
|
605
|
-
env: {
|
|
606
|
-
KLAW_CODEX_COMPUTER_USE_PLUGIN_NAME: "env-fallback-plugin",
|
|
607
|
-
},
|
|
608
|
-
}),
|
|
609
|
-
).toEqual({
|
|
610
|
-
enabled: true,
|
|
611
|
-
autoInstall: true,
|
|
612
|
-
marketplaceDiscoveryTimeoutMs: 60_000,
|
|
613
|
-
pluginName: "env-fallback-plugin",
|
|
614
|
-
mcpServerName: "computer-use",
|
|
615
|
-
marketplaceName: "desktop-tools",
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
expectFields(
|
|
619
|
-
resolveCodexComputerUseConfig({
|
|
620
|
-
pluginConfig: {},
|
|
621
|
-
env: {
|
|
622
|
-
KLAW_CODEX_COMPUTER_USE: "1",
|
|
623
|
-
KLAW_CODEX_COMPUTER_USE_MARKETPLACE_SOURCE: "github:example/plugins",
|
|
624
|
-
KLAW_CODEX_COMPUTER_USE_AUTO_INSTALL: "true",
|
|
625
|
-
KLAW_CODEX_COMPUTER_USE_MARKETPLACE_DISCOVERY_TIMEOUT_MS: "30000",
|
|
626
|
-
},
|
|
627
|
-
}),
|
|
628
|
-
"computer use config",
|
|
629
|
-
{
|
|
630
|
-
enabled: true,
|
|
631
|
-
autoInstall: true,
|
|
632
|
-
marketplaceDiscoveryTimeoutMs: 30_000,
|
|
633
|
-
marketplaceSource: "github:example/plugins",
|
|
634
|
-
},
|
|
635
|
-
);
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
it("allows plugin config to opt in to guardian-reviewed local execution", () => {
|
|
639
|
-
const runtime = resolveRuntimeForTest({
|
|
640
|
-
pluginConfig: {
|
|
641
|
-
appServer: {
|
|
642
|
-
mode: "guardian",
|
|
643
|
-
},
|
|
644
|
-
},
|
|
645
|
-
env: {},
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
expectRuntimePolicy(runtime, {
|
|
649
|
-
approvalPolicy: "on-request",
|
|
650
|
-
sandbox: "workspace-write",
|
|
651
|
-
approvalsReviewer: "auto_review",
|
|
652
|
-
});
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
it("allows environment mode fallback to opt in to guardian-reviewed local execution", () => {
|
|
656
|
-
const runtime = resolveRuntimeForTest({
|
|
657
|
-
pluginConfig: {},
|
|
658
|
-
env: { KLAW_CODEX_APP_SERVER_MODE: "guardian" },
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
expectRuntimePolicy(runtime, {
|
|
662
|
-
approvalPolicy: "on-request",
|
|
663
|
-
sandbox: "workspace-write",
|
|
664
|
-
approvalsReviewer: "auto_review",
|
|
665
|
-
});
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
it("accepts the latest auto_review reviewer and legacy guardian_subagent alias", () => {
|
|
669
|
-
expect(
|
|
670
|
-
resolveRuntimeForTest({
|
|
671
|
-
pluginConfig: { appServer: { approvalsReviewer: "auto_review" } },
|
|
672
|
-
env: {},
|
|
673
|
-
}).approvalsReviewer,
|
|
674
|
-
).toBe("auto_review");
|
|
675
|
-
expect(
|
|
676
|
-
resolveRuntimeForTest({
|
|
677
|
-
pluginConfig: { appServer: { approvalsReviewer: "guardian_subagent" } },
|
|
678
|
-
env: {},
|
|
679
|
-
}).approvalsReviewer,
|
|
680
|
-
).toBe("guardian_subagent");
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
it("ignores removed KLAW_CODEX_APP_SERVER_GUARDIAN fallback", () => {
|
|
684
|
-
const runtime = resolveRuntimeForTest({
|
|
685
|
-
pluginConfig: {},
|
|
686
|
-
env: { KLAW_CODEX_APP_SERVER_GUARDIAN: "1" },
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
expectRuntimePolicy(runtime, {
|
|
690
|
-
approvalPolicy: "never",
|
|
691
|
-
sandbox: "danger-full-access",
|
|
692
|
-
approvalsReviewer: "user",
|
|
693
|
-
});
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
it("lets explicit policy fields override guardian mode", () => {
|
|
697
|
-
const runtime = resolveRuntimeForTest({
|
|
698
|
-
pluginConfig: {
|
|
699
|
-
appServer: {
|
|
700
|
-
mode: "guardian",
|
|
701
|
-
approvalPolicy: "on-failure",
|
|
702
|
-
sandbox: "danger-full-access",
|
|
703
|
-
approvalsReviewer: "user",
|
|
704
|
-
},
|
|
705
|
-
},
|
|
706
|
-
env: {},
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
expectRuntimePolicy(runtime, {
|
|
710
|
-
approvalPolicy: "on-failure",
|
|
711
|
-
sandbox: "danger-full-access",
|
|
712
|
-
approvalsReviewer: "user",
|
|
713
|
-
});
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
it("derives distinct shared-client keys for distinct auth tokens without exposing them", () => {
|
|
717
|
-
const first = codexAppServerStartOptionsKey({
|
|
718
|
-
transport: "websocket",
|
|
719
|
-
command: "codex",
|
|
720
|
-
args: [],
|
|
721
|
-
url: "ws://127.0.0.1:39175",
|
|
722
|
-
authToken: "tok_first",
|
|
723
|
-
headers: {},
|
|
724
|
-
});
|
|
725
|
-
const second = codexAppServerStartOptionsKey({
|
|
726
|
-
transport: "websocket",
|
|
727
|
-
command: "codex",
|
|
728
|
-
args: [],
|
|
729
|
-
url: "ws://127.0.0.1:39175",
|
|
730
|
-
authToken: "tok_second",
|
|
731
|
-
headers: {},
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
expect(first).not.toEqual(second);
|
|
735
|
-
expect(
|
|
736
|
-
codexAppServerStartOptionsKey({
|
|
737
|
-
transport: "websocket",
|
|
738
|
-
command: "codex",
|
|
739
|
-
args: [],
|
|
740
|
-
url: "ws://127.0.0.1:39175",
|
|
741
|
-
authToken: "tok_first",
|
|
742
|
-
headers: {},
|
|
743
|
-
}),
|
|
744
|
-
).toEqual(first);
|
|
745
|
-
expect(first).not.toContain("tok_first");
|
|
746
|
-
expect(second).not.toContain("tok_second");
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
it("derives distinct shared-client keys for distinct env values without exposing them", () => {
|
|
750
|
-
const first = codexAppServerStartOptionsKey({
|
|
751
|
-
transport: "stdio",
|
|
752
|
-
command: "codex",
|
|
753
|
-
args: ["app-server"],
|
|
754
|
-
headers: {},
|
|
755
|
-
env: { OPENAI_API_KEY: "sk-first" },
|
|
756
|
-
});
|
|
757
|
-
const second = codexAppServerStartOptionsKey({
|
|
758
|
-
transport: "stdio",
|
|
759
|
-
command: "codex",
|
|
760
|
-
args: ["app-server"],
|
|
761
|
-
headers: {},
|
|
762
|
-
env: { OPENAI_API_KEY: "sk-second" },
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
expect(first).not.toEqual(second);
|
|
766
|
-
expect(
|
|
767
|
-
codexAppServerStartOptionsKey({
|
|
768
|
-
transport: "stdio",
|
|
769
|
-
command: "codex",
|
|
770
|
-
args: ["app-server"],
|
|
771
|
-
headers: {},
|
|
772
|
-
env: { OPENAI_API_KEY: "sk-first" },
|
|
773
|
-
}),
|
|
774
|
-
).toEqual(first);
|
|
775
|
-
expect(first).not.toContain("sk-first");
|
|
776
|
-
expect(second).not.toContain("sk-second");
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
it("keeps secret-derived shared-client keys stable across module reloads", async () => {
|
|
780
|
-
const startOptions = {
|
|
781
|
-
transport: "websocket" as const,
|
|
782
|
-
command: "codex",
|
|
783
|
-
args: [],
|
|
784
|
-
url: "ws://127.0.0.1:39175",
|
|
785
|
-
authToken: "tok_reload",
|
|
786
|
-
headers: {},
|
|
787
|
-
env: { OPENAI_API_KEY: "sk-reload" },
|
|
788
|
-
};
|
|
789
|
-
const first = codexAppServerStartOptionsKey(startOptions);
|
|
790
|
-
|
|
791
|
-
vi.resetModules();
|
|
792
|
-
const reloaded = await import("./config.js");
|
|
793
|
-
|
|
794
|
-
expect(reloaded.codexAppServerStartOptionsKey(startOptions)).toEqual(first);
|
|
795
|
-
expect(first).not.toContain("tok_reload");
|
|
796
|
-
expect(first).not.toContain("sk-reload");
|
|
797
|
-
});
|
|
798
|
-
|
|
799
|
-
it("derives distinct shared-client keys for distinct agent dirs", () => {
|
|
800
|
-
const startOptions = {
|
|
801
|
-
transport: "stdio" as const,
|
|
802
|
-
command: "codex",
|
|
803
|
-
args: ["app-server"],
|
|
804
|
-
headers: {},
|
|
805
|
-
};
|
|
806
|
-
|
|
807
|
-
expect(codexAppServerStartOptionsKey(startOptions, { agentDir: "/tmp/agent-a" })).not.toEqual(
|
|
808
|
-
codexAppServerStartOptionsKey(startOptions, { agentDir: "/tmp/agent-b" }),
|
|
809
|
-
);
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
it("keeps runtime config keys aligned with manifest schema and UI hints", async () => {
|
|
813
|
-
const manifest = JSON.parse(
|
|
814
|
-
await fs.readFile(new URL("../../klaw.plugin.json", import.meta.url), "utf8"),
|
|
815
|
-
) as {
|
|
816
|
-
configSchema: {
|
|
817
|
-
properties: {
|
|
818
|
-
appServer: { properties: Record<string, unknown> };
|
|
819
|
-
computerUse: { properties: Record<string, unknown> };
|
|
820
|
-
codexPlugins: {
|
|
821
|
-
properties: Record<string, unknown>;
|
|
822
|
-
additionalProperties: boolean;
|
|
823
|
-
};
|
|
824
|
-
};
|
|
825
|
-
};
|
|
826
|
-
uiHints: Record<string, unknown>;
|
|
827
|
-
};
|
|
828
|
-
const manifestKeys = Object.keys(
|
|
829
|
-
manifest.configSchema.properties.appServer.properties,
|
|
830
|
-
).toSorted();
|
|
831
|
-
|
|
832
|
-
expect(manifestKeys).toEqual([...CODEX_APP_SERVER_CONFIG_KEYS].toSorted());
|
|
833
|
-
for (const key of CODEX_APP_SERVER_CONFIG_KEYS) {
|
|
834
|
-
expectUiHintLabel(manifest, `appServer.${key}`);
|
|
835
|
-
}
|
|
836
|
-
const computerUseManifestKeys = Object.keys(
|
|
837
|
-
manifest.configSchema.properties.computerUse.properties,
|
|
838
|
-
).toSorted();
|
|
839
|
-
expect(computerUseManifestKeys).toEqual([...CODEX_COMPUTER_USE_CONFIG_KEYS].toSorted());
|
|
840
|
-
for (const key of CODEX_COMPUTER_USE_CONFIG_KEYS) {
|
|
841
|
-
expectUiHintLabel(manifest, `computerUse.${key}`);
|
|
842
|
-
}
|
|
843
|
-
const codexPluginsProperties = manifest.configSchema.properties.codexPlugins;
|
|
844
|
-
const codexPluginsManifestKeys = Object.keys(codexPluginsProperties.properties).toSorted();
|
|
845
|
-
expect(codexPluginsManifestKeys).toEqual([...CODEX_PLUGINS_CONFIG_KEYS].toSorted());
|
|
846
|
-
expect(codexPluginsProperties.additionalProperties).toBe(false);
|
|
847
|
-
for (const key of CODEX_PLUGINS_CONFIG_KEYS) {
|
|
848
|
-
expectUiHintLabel(manifest, `codexPlugins.${key}`);
|
|
849
|
-
}
|
|
850
|
-
const pluginEntryProperties = (
|
|
851
|
-
codexPluginsProperties.properties.plugins as {
|
|
852
|
-
additionalProperties: { properties: Record<string, unknown> };
|
|
853
|
-
}
|
|
854
|
-
).additionalProperties.properties;
|
|
855
|
-
expect(Object.keys(pluginEntryProperties).toSorted()).toEqual(
|
|
856
|
-
[...CODEX_PLUGIN_ENTRY_CONFIG_KEYS].toSorted(),
|
|
857
|
-
);
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
it("does not schema-default mode-derived policy fields", async () => {
|
|
861
|
-
const manifest = JSON.parse(
|
|
862
|
-
await fs.readFile(new URL("../../klaw.plugin.json", import.meta.url), "utf8"),
|
|
863
|
-
) as {
|
|
864
|
-
configSchema: {
|
|
865
|
-
properties: {
|
|
866
|
-
appServer: {
|
|
867
|
-
properties: Record<string, { default?: unknown }>;
|
|
868
|
-
};
|
|
869
|
-
};
|
|
870
|
-
};
|
|
871
|
-
};
|
|
872
|
-
const appServerProperties = manifest.configSchema.properties.appServer.properties;
|
|
873
|
-
|
|
874
|
-
expect(appServerProperties.command?.default).toBeUndefined();
|
|
875
|
-
expect(appServerProperties.approvalPolicy?.default).toBeUndefined();
|
|
876
|
-
expect(appServerProperties.sandbox?.default).toBeUndefined();
|
|
877
|
-
expect(appServerProperties.approvalsReviewer?.default).toBeUndefined();
|
|
878
|
-
});
|
|
879
|
-
});
|