@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.
Files changed (138) hide show
  1. package/package.json +17 -2
  2. package/doctor-contract-api.test.ts +0 -44
  3. package/doctor-contract-api.ts +0 -68
  4. package/harness.ts +0 -72
  5. package/index.test.ts +0 -230
  6. package/index.ts +0 -66
  7. package/media-understanding-provider.test.ts +0 -486
  8. package/media-understanding-provider.ts +0 -521
  9. package/prompt-overlay-runtime-contract.test.ts +0 -48
  10. package/prompt-overlay.ts +0 -21
  11. package/provider-catalog.ts +0 -83
  12. package/provider-discovery.ts +0 -45
  13. package/provider.test.ts +0 -384
  14. package/provider.ts +0 -243
  15. package/src/app-server/app-inventory-cache.test.ts +0 -176
  16. package/src/app-server/app-inventory-cache.ts +0 -324
  17. package/src/app-server/approval-bridge.test.ts +0 -1471
  18. package/src/app-server/approval-bridge.ts +0 -1211
  19. package/src/app-server/auth-bridge.test.ts +0 -1449
  20. package/src/app-server/auth-bridge.ts +0 -614
  21. package/src/app-server/auth-profile-runtime-contract.test.ts +0 -239
  22. package/src/app-server/capabilities.ts +0 -27
  23. package/src/app-server/client-factory.ts +0 -24
  24. package/src/app-server/client.test.ts +0 -563
  25. package/src/app-server/client.ts +0 -715
  26. package/src/app-server/compact.test.ts +0 -710
  27. package/src/app-server/compact.ts +0 -500
  28. package/src/app-server/computer-use.test.ts +0 -788
  29. package/src/app-server/computer-use.ts +0 -683
  30. package/src/app-server/config.test.ts +0 -879
  31. package/src/app-server/config.ts +0 -1038
  32. package/src/app-server/context-engine-projection.test.ts +0 -252
  33. package/src/app-server/context-engine-projection.ts +0 -403
  34. package/src/app-server/delivery-no-reply-runtime-contract.test.ts +0 -80
  35. package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
  36. package/src/app-server/dynamic-tool-profile.ts +0 -69
  37. package/src/app-server/dynamic-tools.test.ts +0 -1302
  38. package/src/app-server/dynamic-tools.ts +0 -623
  39. package/src/app-server/elicitation-bridge.test.ts +0 -1056
  40. package/src/app-server/elicitation-bridge.ts +0 -783
  41. package/src/app-server/event-projector.test.ts +0 -2668
  42. package/src/app-server/event-projector.ts +0 -2057
  43. package/src/app-server/image-payload-sanitizer.test.ts +0 -49
  44. package/src/app-server/image-payload-sanitizer.ts +0 -167
  45. package/src/app-server/klaw-owned-tool-runtime-contract.test.ts +0 -456
  46. package/src/app-server/local-runtime-attribution.ts +0 -39
  47. package/src/app-server/managed-binary.test.ts +0 -139
  48. package/src/app-server/managed-binary.ts +0 -193
  49. package/src/app-server/models.test.ts +0 -246
  50. package/src/app-server/models.ts +0 -172
  51. package/src/app-server/native-hook-relay.test.ts +0 -271
  52. package/src/app-server/native-hook-relay.ts +0 -150
  53. package/src/app-server/native-subagent-task-mirror.test.ts +0 -573
  54. package/src/app-server/native-subagent-task-mirror.ts +0 -497
  55. package/src/app-server/outcome-fallback-runtime-contract.test.ts +0 -404
  56. package/src/app-server/plugin-activation.test.ts +0 -336
  57. package/src/app-server/plugin-activation.ts +0 -283
  58. package/src/app-server/plugin-app-cache-key.ts +0 -74
  59. package/src/app-server/plugin-approval-roundtrip.ts +0 -122
  60. package/src/app-server/plugin-inventory.test.ts +0 -355
  61. package/src/app-server/plugin-inventory.ts +0 -357
  62. package/src/app-server/plugin-thread-config.test.ts +0 -865
  63. package/src/app-server/plugin-thread-config.ts +0 -455
  64. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
  65. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
  66. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
  67. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
  68. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
  69. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
  70. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
  71. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
  72. package/src/app-server/protocol-validators.test.ts +0 -75
  73. package/src/app-server/protocol-validators.ts +0 -203
  74. package/src/app-server/protocol.ts +0 -520
  75. package/src/app-server/rate-limit-cache.ts +0 -48
  76. package/src/app-server/rate-limits.test.ts +0 -202
  77. package/src/app-server/rate-limits.ts +0 -583
  78. package/src/app-server/request.ts +0 -73
  79. package/src/app-server/run-attempt.context-engine.test.ts +0 -1004
  80. package/src/app-server/run-attempt.test.ts +0 -9477
  81. package/src/app-server/run-attempt.ts +0 -4683
  82. package/src/app-server/run-attempt.vision-tools.test.ts +0 -35
  83. package/src/app-server/schema-normalization-runtime-contract.test.ts +0 -206
  84. package/src/app-server/session-binding.test.ts +0 -303
  85. package/src/app-server/session-binding.ts +0 -398
  86. package/src/app-server/session-history.ts +0 -44
  87. package/src/app-server/shared-client.test.ts +0 -589
  88. package/src/app-server/shared-client.ts +0 -289
  89. package/src/app-server/side-question.test.ts +0 -1175
  90. package/src/app-server/side-question.ts +0 -1007
  91. package/src/app-server/test-support.ts +0 -48
  92. package/src/app-server/thread-lifecycle.test.ts +0 -447
  93. package/src/app-server/thread-lifecycle.ts +0 -939
  94. package/src/app-server/thread-lifecycle.user-mcp-servers.test.ts +0 -442
  95. package/src/app-server/timeout.ts +0 -9
  96. package/src/app-server/tool-progress-normalization.ts +0 -77
  97. package/src/app-server/trajectory.test.ts +0 -205
  98. package/src/app-server/trajectory.ts +0 -365
  99. package/src/app-server/transcript-mirror.test.ts +0 -524
  100. package/src/app-server/transcript-mirror.ts +0 -208
  101. package/src/app-server/transcript-repair-runtime-contract.test.ts +0 -44
  102. package/src/app-server/transport-stdio.test.ts +0 -171
  103. package/src/app-server/transport-stdio.ts +0 -107
  104. package/src/app-server/transport-websocket.test.ts +0 -69
  105. package/src/app-server/transport-websocket.ts +0 -90
  106. package/src/app-server/transport.ts +0 -117
  107. package/src/app-server/user-input-bridge.test.ts +0 -249
  108. package/src/app-server/user-input-bridge.ts +0 -316
  109. package/src/app-server/version.ts +0 -4
  110. package/src/app-server/vision-tools.ts +0 -12
  111. package/src/command-account.ts +0 -544
  112. package/src/command-formatters.ts +0 -425
  113. package/src/command-handlers.ts +0 -2004
  114. package/src/command-rpc.test.ts +0 -16
  115. package/src/command-rpc.ts +0 -142
  116. package/src/commands.test.ts +0 -3312
  117. package/src/commands.ts +0 -65
  118. package/src/conversation-binding-data.ts +0 -124
  119. package/src/conversation-binding.test.ts +0 -599
  120. package/src/conversation-binding.ts +0 -561
  121. package/src/conversation-control.test.ts +0 -126
  122. package/src/conversation-control.ts +0 -303
  123. package/src/conversation-turn-collector.test.ts +0 -191
  124. package/src/conversation-turn-collector.ts +0 -186
  125. package/src/conversation-turn-input.test.ts +0 -141
  126. package/src/conversation-turn-input.ts +0 -106
  127. package/src/manifest.test.ts +0 -20
  128. package/src/migration/apply.ts +0 -501
  129. package/src/migration/helpers.ts +0 -55
  130. package/src/migration/plan.ts +0 -461
  131. package/src/migration/provider.test.ts +0 -1741
  132. package/src/migration/provider.ts +0 -41
  133. package/src/migration/source.ts +0 -643
  134. package/src/migration/targets.ts +0 -25
  135. package/src/node-cli-sessions.test.ts +0 -180
  136. package/src/node-cli-sessions.ts +0 -711
  137. package/test-api.ts +0 -82
  138. package/tsconfig.json +0 -16
@@ -1,172 +0,0 @@
1
- import type { resolveCodexAppServerAuthProfileIdForAgent } from "./auth-bridge.js";
2
- import type { CodexAppServerClient } from "./client.js";
3
- import type { CodexAppServerStartOptions } from "./config.js";
4
- import { readCodexModelListResponse } from "./protocol-validators.js";
5
- import type { CodexModel, CodexReasoningEffortOption } from "./protocol.js";
6
-
7
- export type CodexAppServerModel = {
8
- id: string;
9
- model: string;
10
- displayName?: string;
11
- description?: string;
12
- hidden?: boolean;
13
- isDefault?: boolean;
14
- inputModalities: string[];
15
- supportedReasoningEfforts: string[];
16
- defaultReasoningEffort?: string;
17
- };
18
-
19
- export type CodexAppServerModelListResult = {
20
- models: CodexAppServerModel[];
21
- nextCursor?: string;
22
- truncated?: boolean;
23
- };
24
-
25
- export type CodexAppServerListModelsOptions = {
26
- limit?: number;
27
- cursor?: string;
28
- includeHidden?: boolean;
29
- timeoutMs?: number;
30
- startOptions?: CodexAppServerStartOptions;
31
- authProfileId?: string;
32
- agentDir?: string;
33
- config?: Parameters<typeof resolveCodexAppServerAuthProfileIdForAgent>[0]["config"];
34
- sharedClient?: boolean;
35
- };
36
-
37
- export async function listCodexAppServerModels(
38
- options: CodexAppServerListModelsOptions = {},
39
- ): Promise<CodexAppServerModelListResult> {
40
- return await withCodexAppServerModelClient(options, async ({ client, timeoutMs }) =>
41
- requestModelListPage(client, { ...options, timeoutMs }),
42
- );
43
- }
44
-
45
- export async function listAllCodexAppServerModels(
46
- options: CodexAppServerListModelsOptions & { maxPages?: number } = {},
47
- ): Promise<CodexAppServerModelListResult> {
48
- const maxPages = normalizeMaxPages(options.maxPages);
49
- return await withCodexAppServerModelClient(options, async ({ client, timeoutMs }) => {
50
- const models: CodexAppServerModel[] = [];
51
- let cursor = options.cursor;
52
- let nextCursor: string | undefined;
53
- for (let page = 0; page < maxPages; page += 1) {
54
- const result = await requestModelListPage(client, {
55
- ...options,
56
- timeoutMs,
57
- cursor,
58
- });
59
- models.push(...result.models);
60
- nextCursor = result.nextCursor;
61
- if (!nextCursor) {
62
- return { models };
63
- }
64
- cursor = nextCursor;
65
- }
66
- return { models, nextCursor, truncated: true };
67
- });
68
- }
69
-
70
- async function withCodexAppServerModelClient<T>(
71
- options: CodexAppServerListModelsOptions,
72
- run: (params: { client: CodexAppServerClient; timeoutMs: number }) => Promise<T>,
73
- ): Promise<T> {
74
- const timeoutMs = options.timeoutMs ?? 2500;
75
- const useSharedClient = options.sharedClient !== false;
76
- const { createIsolatedCodexAppServerClient, getSharedCodexAppServerClient } =
77
- await import("./shared-client.js");
78
- const client = useSharedClient
79
- ? await getSharedCodexAppServerClient({
80
- startOptions: options.startOptions,
81
- timeoutMs,
82
- authProfileId: options.authProfileId,
83
- agentDir: options.agentDir,
84
- config: options.config,
85
- })
86
- : await createIsolatedCodexAppServerClient({
87
- startOptions: options.startOptions,
88
- timeoutMs,
89
- authProfileId: options.authProfileId,
90
- agentDir: options.agentDir,
91
- config: options.config,
92
- });
93
- try {
94
- return await run({ client, timeoutMs });
95
- } finally {
96
- if (!useSharedClient) {
97
- client.close();
98
- }
99
- }
100
- }
101
-
102
- async function requestModelListPage(
103
- client: CodexAppServerClient,
104
- options: CodexAppServerListModelsOptions & { timeoutMs: number },
105
- ): Promise<CodexAppServerModelListResult> {
106
- const response = await client.request(
107
- "model/list",
108
- {
109
- limit: options.limit ?? null,
110
- cursor: options.cursor ?? null,
111
- includeHidden: options.includeHidden ?? null,
112
- },
113
- { timeoutMs: options.timeoutMs },
114
- );
115
- return readModelListResult(response);
116
- }
117
-
118
- export function readModelListResult(value: unknown): CodexAppServerModelListResult {
119
- const response = readCodexModelListResponse(value);
120
- if (!response) {
121
- return { models: [] };
122
- }
123
- const models = response.data
124
- .map((entry) => readCodexModel(entry))
125
- .filter((entry): entry is CodexAppServerModel => entry !== undefined);
126
- const nextCursor = response.nextCursor ?? undefined;
127
- return { models, ...(nextCursor ? { nextCursor } : {}) };
128
- }
129
-
130
- function readCodexModel(value: CodexModel): CodexAppServerModel | undefined {
131
- const id = readNonEmptyString(value.id);
132
- const model = readNonEmptyString(value.model) ?? id;
133
- if (!id || !model) {
134
- return undefined;
135
- }
136
- return {
137
- id,
138
- model,
139
- ...(readNonEmptyString(value.displayName)
140
- ? { displayName: readNonEmptyString(value.displayName) }
141
- : {}),
142
- ...(readNonEmptyString(value.description)
143
- ? { description: readNonEmptyString(value.description) }
144
- : {}),
145
- hidden: value.hidden,
146
- isDefault: value.isDefault,
147
- inputModalities: value.inputModalities,
148
- supportedReasoningEfforts: readReasoningEfforts(value.supportedReasoningEfforts),
149
- ...(readNonEmptyString(value.defaultReasoningEffort)
150
- ? { defaultReasoningEffort: readNonEmptyString(value.defaultReasoningEffort) }
151
- : {}),
152
- };
153
- }
154
-
155
- function readReasoningEfforts(value: CodexReasoningEffortOption[]): string[] {
156
- const efforts = value
157
- .map((entry) => readNonEmptyString(entry.reasoningEffort))
158
- .filter((entry): entry is string => entry !== undefined);
159
- return [...new Set(efforts)];
160
- }
161
-
162
- function readNonEmptyString(value: unknown): string | undefined {
163
- if (typeof value !== "string") {
164
- return undefined;
165
- }
166
- const trimmed = value.trim();
167
- return trimmed || undefined;
168
- }
169
-
170
- function normalizeMaxPages(value: unknown): number {
171
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 20;
172
- }
@@ -1,271 +0,0 @@
1
- import type { NativeHookRelayRegistrationHandle } from "klaw/plugin-sdk/agent-harness-runtime";
2
- import { describe, expect, it } from "vitest";
3
- import {
4
- buildCodexNativeHookRelayConfig,
5
- buildCodexNativeHookRelayDisabledConfig,
6
- } from "./native-hook-relay.js";
7
-
8
- describe("Codex native hook relay config", () => {
9
- it("builds deterministic Codex config overrides with command hooks", () => {
10
- const config = buildCodexNativeHookRelayConfig({
11
- relay: createRelay(),
12
- hookTimeoutSec: 7,
13
- });
14
-
15
- expect(config).toEqual({
16
- "features.hooks": true,
17
- "hooks.PreToolUse": [
18
- {
19
- hooks: [
20
- {
21
- type: "command",
22
- command: "klaw hooks relay --provider codex --relay-id relay-1 --event pre_tool_use",
23
- timeout: 7,
24
- async: false,
25
- statusMessage: "Klaw native hook relay",
26
- },
27
- ],
28
- },
29
- ],
30
- "hooks.PostToolUse": [
31
- {
32
- hooks: [
33
- {
34
- type: "command",
35
- command: "klaw hooks relay --provider codex --relay-id relay-1 --event post_tool_use",
36
- timeout: 7,
37
- async: false,
38
- statusMessage: "Klaw native hook relay",
39
- },
40
- ],
41
- },
42
- ],
43
- "hooks.PermissionRequest": [
44
- {
45
- hooks: [
46
- {
47
- type: "command",
48
- command:
49
- "klaw hooks relay --provider codex --relay-id relay-1 --event permission_request",
50
- timeout: 7,
51
- async: false,
52
- statusMessage: "Klaw native hook relay",
53
- },
54
- ],
55
- },
56
- ],
57
- "hooks.Stop": [
58
- {
59
- hooks: [
60
- {
61
- type: "command",
62
- command:
63
- "klaw hooks relay --provider codex --relay-id relay-1 --event before_agent_finalize",
64
- timeout: 7,
65
- async: false,
66
- statusMessage: "Klaw native hook relay",
67
- },
68
- ],
69
- },
70
- ],
71
- "hooks.state": {
72
- "/<session-flags>/config.toml:pre_tool_use:0:0": {
73
- enabled: true,
74
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
75
- },
76
- "<session-flags>/config.toml:pre_tool_use:0:0": {
77
- enabled: true,
78
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
79
- },
80
- "/<session-flags>/config.toml:post_tool_use:0:0": {
81
- enabled: true,
82
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
83
- },
84
- "<session-flags>/config.toml:post_tool_use:0:0": {
85
- enabled: true,
86
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
87
- },
88
- "/<session-flags>/config.toml:permission_request:0:0": {
89
- enabled: true,
90
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
91
- },
92
- "<session-flags>/config.toml:permission_request:0:0": {
93
- enabled: true,
94
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
95
- },
96
- "/<session-flags>/config.toml:stop:0:0": {
97
- enabled: true,
98
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
99
- },
100
- "<session-flags>/config.toml:stop:0:0": {
101
- enabled: true,
102
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
103
- },
104
- },
105
- });
106
- expect(JSON.stringify(config)).not.toContain("timeoutSec");
107
- expect(JSON.stringify(config)).not.toContain('"matcher":null');
108
- expect(config).not.toHaveProperty("hooks.SessionStart");
109
- expect(config).not.toHaveProperty("hooks.UserPromptSubmit");
110
- });
111
-
112
- it("includes only requested hook events", () => {
113
- expect(
114
- buildCodexNativeHookRelayConfig({
115
- relay: createRelay(),
116
- events: ["permission_request"],
117
- }),
118
- ).toEqual({
119
- "features.hooks": true,
120
- "hooks.PermissionRequest": [
121
- {
122
- hooks: [
123
- {
124
- type: "command",
125
- command:
126
- "klaw hooks relay --provider codex --relay-id relay-1 --event permission_request",
127
- timeout: 5,
128
- async: false,
129
- statusMessage: "Klaw native hook relay",
130
- },
131
- ],
132
- },
133
- ],
134
- "hooks.state": {
135
- "/<session-flags>/config.toml:permission_request:0:0": {
136
- enabled: true,
137
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
138
- },
139
- "<session-flags>/config.toml:permission_request:0:0": {
140
- enabled: true,
141
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
142
- },
143
- },
144
- });
145
- });
146
-
147
- it("clears requested hook events when the relay reports no local work", () => {
148
- expect(
149
- buildCodexNativeHookRelayConfig({
150
- relay: createRelay({ inactiveEvents: ["post_tool_use", "before_agent_finalize"] }),
151
- events: ["pre_tool_use", "post_tool_use", "before_agent_finalize"],
152
- }),
153
- ).toEqual({
154
- "features.hooks": true,
155
- "hooks.PreToolUse": [
156
- {
157
- hooks: [
158
- {
159
- type: "command",
160
- command: "klaw hooks relay --provider codex --relay-id relay-1 --event pre_tool_use",
161
- timeout: 5,
162
- async: false,
163
- statusMessage: "Klaw native hook relay",
164
- },
165
- ],
166
- },
167
- ],
168
- "hooks.PostToolUse": [],
169
- "hooks.Stop": [],
170
- "hooks.state": {
171
- "/<session-flags>/config.toml:pre_tool_use:0:0": {
172
- enabled: true,
173
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
174
- },
175
- "<session-flags>/config.toml:pre_tool_use:0:0": {
176
- enabled: true,
177
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
178
- },
179
- },
180
- });
181
- });
182
-
183
- it("clears omitted hook events when requested", () => {
184
- expect(
185
- buildCodexNativeHookRelayConfig({
186
- relay: createRelay(),
187
- events: ["permission_request"],
188
- clearOmittedEvents: true,
189
- }),
190
- ).toEqual({
191
- "features.hooks": true,
192
- "hooks.PreToolUse": [],
193
- "hooks.PostToolUse": [],
194
- "hooks.PermissionRequest": [
195
- {
196
- hooks: [
197
- {
198
- type: "command",
199
- command:
200
- "klaw hooks relay --provider codex --relay-id relay-1 --event permission_request",
201
- timeout: 5,
202
- async: false,
203
- statusMessage: "Klaw native hook relay",
204
- },
205
- ],
206
- },
207
- ],
208
- "hooks.Stop": [],
209
- "hooks.state": {
210
- "/<session-flags>/config.toml:pre_tool_use:0:0": { enabled: false },
211
- "<session-flags>/config.toml:pre_tool_use:0:0": { enabled: false },
212
- "/<session-flags>/config.toml:post_tool_use:0:0": { enabled: false },
213
- "<session-flags>/config.toml:post_tool_use:0:0": { enabled: false },
214
- "/<session-flags>/config.toml:permission_request:0:0": {
215
- enabled: true,
216
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
217
- },
218
- "<session-flags>/config.toml:permission_request:0:0": {
219
- enabled: true,
220
- trusted_hash: expect.stringMatching(/^sha256:[a-f0-9]{64}$/),
221
- },
222
- "/<session-flags>/config.toml:stop:0:0": { enabled: false },
223
- "<session-flags>/config.toml:stop:0:0": { enabled: false },
224
- },
225
- });
226
- });
227
-
228
- it("omits matchers so Codex MCP tool names reach the relay with a stable trust hash", () => {
229
- const config = buildCodexNativeHookRelayConfig({
230
- relay: createRelay(),
231
- events: ["pre_tool_use", "post_tool_use"],
232
- });
233
-
234
- expect((config["hooks.PreToolUse"] as Array<{ matcher?: unknown }>)[0]).not.toHaveProperty(
235
- "matcher",
236
- );
237
- expect((config["hooks.PostToolUse"] as Array<{ matcher?: unknown }>)[0]).not.toHaveProperty(
238
- "matcher",
239
- );
240
- });
241
-
242
- it("builds deterministic clearing config when the relay is disabled", () => {
243
- expect(buildCodexNativeHookRelayDisabledConfig()).toEqual({
244
- "features.hooks": false,
245
- "hooks.PreToolUse": [],
246
- "hooks.PostToolUse": [],
247
- "hooks.PermissionRequest": [],
248
- "hooks.Stop": [],
249
- });
250
- });
251
- });
252
-
253
- function createRelay(options?: {
254
- inactiveEvents?: readonly NativeHookRelayRegistrationHandle["allowedEvents"][number][];
255
- }): NativeHookRelayRegistrationHandle {
256
- const inactiveEvents = new Set(options?.inactiveEvents ?? []);
257
- return {
258
- relayId: "relay-1",
259
- provider: "codex",
260
- sessionId: "session-1",
261
- sessionKey: "agent:main:session-1",
262
- runId: "run-1",
263
- allowedEvents: ["pre_tool_use", "post_tool_use", "permission_request", "before_agent_finalize"],
264
- expiresAtMs: Date.now() + 1000,
265
- shouldRelayEvent: (event) => !inactiveEvents.has(event),
266
- commandForEvent: (event) =>
267
- `klaw hooks relay --provider codex --relay-id relay-1 --event ${event}`,
268
- renew: () => undefined,
269
- unregister: () => undefined,
270
- };
271
- }
@@ -1,150 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import type {
3
- NativeHookRelayEvent,
4
- NativeHookRelayRegistrationHandle,
5
- } from "klaw/plugin-sdk/agent-harness-runtime";
6
- import type { JsonObject, JsonValue } from "./protocol.js";
7
-
8
- export const CODEX_NATIVE_HOOK_RELAY_EVENTS: readonly NativeHookRelayEvent[] = [
9
- "pre_tool_use",
10
- "post_tool_use",
11
- "permission_request",
12
- "before_agent_finalize",
13
- ] as const;
14
-
15
- type CodexHookEventName = "PreToolUse" | "PostToolUse" | "PermissionRequest" | "Stop";
16
-
17
- const CODEX_HOOK_EVENT_BY_NATIVE_EVENT: Record<NativeHookRelayEvent, CodexHookEventName> = {
18
- pre_tool_use: "PreToolUse",
19
- post_tool_use: "PostToolUse",
20
- permission_request: "PermissionRequest",
21
- before_agent_finalize: "Stop",
22
- };
23
-
24
- const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT: Record<NativeHookRelayEvent, string> = {
25
- pre_tool_use: "pre_tool_use",
26
- post_tool_use: "post_tool_use",
27
- permission_request: "permission_request",
28
- before_agent_finalize: "stop",
29
- };
30
-
31
- const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = [
32
- "/<session-flags>/config.toml",
33
- "<session-flags>/config.toml",
34
- ] as const;
35
-
36
- export function buildCodexNativeHookRelayConfig(params: {
37
- relay: NativeHookRelayRegistrationHandle;
38
- events?: readonly NativeHookRelayEvent[];
39
- hookTimeoutSec?: number;
40
- clearOmittedEvents?: boolean;
41
- }): JsonObject {
42
- const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
43
- const selectedEvents = new Set<NativeHookRelayEvent>(events);
44
- const config: JsonObject = {
45
- "features.hooks": true,
46
- };
47
- const hookState: JsonObject = {};
48
- for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
49
- const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
50
- const selected = selectedEvents.has(event);
51
- if (!selected || !params.relay.shouldRelayEvent(event)) {
52
- if (selected || params.clearOmittedEvents) {
53
- config[`hooks.${codexEvent}`] = [] satisfies JsonValue;
54
- }
55
- if (params.clearOmittedEvents) {
56
- for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) {
57
- hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = {
58
- enabled: false,
59
- } satisfies JsonValue;
60
- }
61
- }
62
- continue;
63
- }
64
- const command = params.relay.commandForEvent(event);
65
- const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
66
- config[`hooks.${codexEvent}`] = [
67
- {
68
- hooks: [
69
- {
70
- type: "command",
71
- command,
72
- timeout,
73
- async: false,
74
- statusMessage: "Klaw native hook relay",
75
- },
76
- ],
77
- },
78
- ] satisfies JsonValue;
79
- const state = {
80
- enabled: true,
81
- trusted_hash: codexCommandHookTrustedHash({
82
- event,
83
- command,
84
- timeout,
85
- statusMessage: "Klaw native hook relay",
86
- }),
87
- };
88
- for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) {
89
- hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] =
90
- state satisfies JsonValue;
91
- }
92
- }
93
- config["hooks.state"] = hookState;
94
- return config;
95
- }
96
-
97
- export function buildCodexNativeHookRelayDisabledConfig(): JsonObject {
98
- return {
99
- "features.hooks": false,
100
- "hooks.PreToolUse": [],
101
- "hooks.PostToolUse": [],
102
- "hooks.PermissionRequest": [],
103
- "hooks.Stop": [],
104
- };
105
- }
106
-
107
- function normalizeHookTimeoutSec(value: number | undefined): number {
108
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : 5;
109
- }
110
-
111
- function codexCommandHookTrustedHash(params: {
112
- event: NativeHookRelayEvent;
113
- command: string;
114
- timeout: number;
115
- statusMessage: string;
116
- }): string {
117
- // Keep the match-all matcher omitted rather than null. Codex app-server
118
- // converts JSON null to an empty TOML string before hashing, which changes the
119
- // trust identity even though both forms match all tools.
120
- const identity = {
121
- event_name: CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[params.event],
122
- hooks: [
123
- {
124
- async: false,
125
- command: params.command,
126
- statusMessage: params.statusMessage,
127
- timeout: params.timeout,
128
- type: "command",
129
- },
130
- ],
131
- };
132
- const hash = createHash("sha256")
133
- .update(JSON.stringify(sortJsonValue(identity)))
134
- .digest("hex");
135
- return `sha256:${hash}`;
136
- }
137
-
138
- function sortJsonValue(value: JsonValue): JsonValue {
139
- if (!value || typeof value !== "object") {
140
- return value;
141
- }
142
- if (Array.isArray(value)) {
143
- return value.map(sortJsonValue);
144
- }
145
- const sorted: JsonObject = {};
146
- for (const key of Object.keys(value).toSorted()) {
147
- sorted[key] = sortJsonValue(value[key]);
148
- }
149
- return sorted;
150
- }