@gakr-gakr/codex 0.1.0 → 0.1.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.
Files changed (115) hide show
  1. package/dist/client-DNN2uyJW.js +642 -0
  2. package/dist/client-factory-Bu9OClHJ.js +9 -0
  3. package/dist/command-formatters-BpPOTePl.js +520 -0
  4. package/dist/command-handlers-BBs7Vws9.js +1533 -0
  5. package/dist/compact-CDboBy7o.js +329 -0
  6. package/dist/computer-use-DCZB46Sw.js +367 -0
  7. package/dist/config-CLMSw0p2.js +510 -0
  8. package/dist/doctor-contract-api.js +53 -0
  9. package/dist/harness.js +51 -0
  10. package/dist/index.js +1171 -0
  11. package/dist/media-understanding-provider.js +335 -0
  12. package/dist/models-jLA2SIvd.js +110 -0
  13. package/dist/node-cli-sessions-BLRDs_US.js +1216 -0
  14. package/dist/plugin-activation-CEy_oYpx.js +452 -0
  15. package/dist/prompt-overlay.js +12 -0
  16. package/dist/protocol-C9UWI98H.js +9 -0
  17. package/dist/protocol-validators-BGBspNmF.js +5988 -0
  18. package/dist/provider-catalog.js +84 -0
  19. package/dist/provider-discovery.js +33 -0
  20. package/dist/provider.js +150 -0
  21. package/dist/rate-limit-cache-9LxQdE0K.js +24 -0
  22. package/dist/request-DbSPeTcV.js +89 -0
  23. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  24. package/dist/run-attempt-BoEwzQCv.js +5463 -0
  25. package/dist/session-binding-e2GFp9VH.js +222 -0
  26. package/dist/shared-client-D7Vy0glq.js +631 -0
  27. package/dist/side-question-BDLuEzFP.js +668 -0
  28. package/dist/test-api.js +49 -0
  29. package/dist/thread-lifecycle-Clo0EHMk.js +1565 -0
  30. package/dist/vision-tools-Cofrv35p.js +1379 -0
  31. package/package.json +16 -1
  32. package/doctor-contract-api.ts +0 -68
  33. package/harness.ts +0 -72
  34. package/index.ts +0 -124
  35. package/media-understanding-provider.ts +0 -521
  36. package/prompt-overlay.ts +0 -21
  37. package/provider-catalog.ts +0 -83
  38. package/provider-discovery.ts +0 -45
  39. package/provider.ts +0 -243
  40. package/src/app-server/app-inventory-cache.ts +0 -324
  41. package/src/app-server/approval-bridge.ts +0 -1211
  42. package/src/app-server/auth-bridge.ts +0 -614
  43. package/src/app-server/capabilities.ts +0 -27
  44. package/src/app-server/client-factory.ts +0 -24
  45. package/src/app-server/client.ts +0 -715
  46. package/src/app-server/compact.ts +0 -512
  47. package/src/app-server/computer-use.ts +0 -683
  48. package/src/app-server/config.ts +0 -1038
  49. package/src/app-server/context-engine-projection.ts +0 -403
  50. package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
  51. package/src/app-server/dynamic-tool-profile.ts +0 -70
  52. package/src/app-server/dynamic-tools.ts +0 -623
  53. package/src/app-server/elicitation-bridge.ts +0 -783
  54. package/src/app-server/event-projector.ts +0 -2065
  55. package/src/app-server/image-payload-sanitizer.ts +0 -167
  56. package/src/app-server/local-runtime-attribution.ts +0 -39
  57. package/src/app-server/managed-binary.ts +0 -193
  58. package/src/app-server/models.ts +0 -172
  59. package/src/app-server/native-hook-relay.ts +0 -150
  60. package/src/app-server/native-subagent-task-mirror.ts +0 -497
  61. package/src/app-server/plugin-activation.ts +0 -283
  62. package/src/app-server/plugin-app-cache-key.ts +0 -74
  63. package/src/app-server/plugin-approval-roundtrip.ts +0 -122
  64. package/src/app-server/plugin-inventory.ts +0 -357
  65. package/src/app-server/plugin-thread-config.ts +0 -455
  66. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
  67. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
  68. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
  69. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
  70. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
  71. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
  72. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
  73. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
  74. package/src/app-server/protocol-validators.ts +0 -203
  75. package/src/app-server/protocol.ts +0 -520
  76. package/src/app-server/rate-limit-cache.ts +0 -48
  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.ts +0 -4862
  80. package/src/app-server/session-binding.ts +0 -398
  81. package/src/app-server/session-history.ts +0 -44
  82. package/src/app-server/shared-client.ts +0 -289
  83. package/src/app-server/side-question.ts +0 -1009
  84. package/src/app-server/test-support.ts +0 -48
  85. package/src/app-server/thread-lifecycle.ts +0 -959
  86. package/src/app-server/timeout.ts +0 -9
  87. package/src/app-server/tool-progress-normalization.ts +0 -77
  88. package/src/app-server/trajectory.ts +0 -368
  89. package/src/app-server/transcript-mirror.ts +0 -208
  90. package/src/app-server/transport-stdio.ts +0 -107
  91. package/src/app-server/transport-websocket.ts +0 -90
  92. package/src/app-server/transport.ts +0 -117
  93. package/src/app-server/user-input-bridge.ts +0 -316
  94. package/src/app-server/version.ts +0 -4
  95. package/src/app-server/vision-tools.ts +0 -12
  96. package/src/command-account.ts +0 -544
  97. package/src/command-formatters.ts +0 -426
  98. package/src/command-handlers.ts +0 -2021
  99. package/src/command-plugins-management.ts +0 -137
  100. package/src/command-rpc.ts +0 -142
  101. package/src/commands.ts +0 -65
  102. package/src/conversation-binding-data.ts +0 -124
  103. package/src/conversation-binding.ts +0 -561
  104. package/src/conversation-control.ts +0 -303
  105. package/src/conversation-turn-collector.ts +0 -186
  106. package/src/conversation-turn-input.ts +0 -106
  107. package/src/migration/apply.ts +0 -501
  108. package/src/migration/helpers.ts +0 -55
  109. package/src/migration/plan.ts +0 -461
  110. package/src/migration/provider.ts +0 -41
  111. package/src/migration/source.ts +0 -643
  112. package/src/migration/targets.ts +0 -25
  113. package/src/node-cli-sessions.ts +0 -711
  114. package/test-api.ts +0 -95
  115. package/tsconfig.json +0 -16
@@ -1,783 +0,0 @@
1
- import {
2
- embeddedAgentLog,
3
- type EmbeddedRunAttemptParams,
4
- } from "autobot/plugin-sdk/agent-harness-runtime";
5
- import { formatCodexDisplayText } from "../command-formatters.js";
6
- import {
7
- approvalRequestExplicitlyUnavailable,
8
- mapExecDecisionToOutcome,
9
- requestPluginApproval,
10
- type AppServerApprovalOutcome,
11
- waitForPluginApprovalDecision,
12
- } from "./plugin-approval-roundtrip.js";
13
- import type {
14
- PluginAppPolicyContext,
15
- PluginAppPolicyContextEntry,
16
- } from "./plugin-thread-config.js";
17
- import { isJsonObject, type JsonObject, type JsonValue } from "./protocol.js";
18
-
19
- type ApprovalPropertyContext = {
20
- name: string;
21
- schema: JsonObject;
22
- required: boolean;
23
- };
24
-
25
- type BridgeableApprovalElicitation = {
26
- title: string;
27
- description: string;
28
- requestedSchema: JsonObject;
29
- meta: JsonObject;
30
- };
31
-
32
- type PluginElicitationResolution =
33
- | { kind: "not_plugin" }
34
- | { kind: "matched"; entry: PluginAppPolicyContextEntry }
35
- | { kind: "decline"; reason: string };
36
-
37
- const MCP_TOOL_APPROVAL_KIND = "mcp_tool_call";
38
- const MCP_TOOL_APPROVAL_KIND_KEY = "codex_approval_kind";
39
- const MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY = "connector_name";
40
- const MCP_TOOL_APPROVAL_TOOL_TITLE_KEY = "tool_title";
41
- const MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY = "tool_description";
42
- const MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY = "tool_params_display";
43
- const MCP_TOOL_APPROVAL_SOURCE_KEY = "source";
44
- const MCP_TOOL_APPROVAL_CONNECTOR_SOURCE = "connector";
45
- const CODEX_APPS_SERVER_NAME = "codex_apps";
46
- const PLUGIN_APP_ID_META_KEYS = ["app_id", "appId", "codex_app_id", "codexAppId"];
47
- const PLUGIN_CONNECTOR_ID_META_KEYS = ["connector_id", "connectorId"];
48
- const PLUGIN_NAME_META_KEYS = ["plugin_name", "pluginName", "codex_plugin_name", "codexPluginName"];
49
- const PLUGIN_CONFIG_KEY_META_KEYS = ["config_key", "configKey", "codex_config_key"];
50
- const PLUGIN_MARKETPLACE_NAME_META_KEYS = [
51
- "marketplace_name",
52
- "marketplaceName",
53
- "codex_marketplace_name",
54
- "codexMarketplaceName",
55
- ];
56
- const MAX_DISPLAY_PARAM_ENTRIES = 8;
57
- const MAX_DISPLAY_PARAM_VALUE_LENGTH = 120;
58
- const MAX_DISPLAY_VALUE_ARRAY_ITEMS = 8;
59
- const MAX_DISPLAY_VALUE_OBJECT_KEYS = 8;
60
- const MAX_DISPLAY_VALUE_DEPTH = 3;
61
- const DISPLAY_TEXT_SCAN_MAX_LENGTH = 4096;
62
- const ANSI_OSC_SEQUENCE_RE = new RegExp(
63
- String.raw`(?:\u001b]|\u009d)[^\u001b\u009c\u0007]*(?:\u0007|\u001b\\|\u009c)`,
64
- "g",
65
- );
66
- const ANSI_CONTROL_SEQUENCE_RE = new RegExp(
67
- String.raw`(?:\u001b\[[0-?]*[ -/]*[@-~]|\u009b[0-?]*[ -/]*[@-~]|\u001b[@-Z\\-_])`,
68
- "g",
69
- );
70
- const CONTROL_CHARACTER_RE = new RegExp(String.raw`[\u0000-\u001f\u007f-\u009f]+`, "g");
71
- const INVISIBLE_FORMATTING_CONTROL_RE = new RegExp(
72
- String.raw`[\u00ad\u034f\u061c\u200b-\u200f\u202a-\u202e\u2060-\u206f\ufeff\ufe00-\ufe0f\u{e0100}-\u{e01ef}]`,
73
- "gu",
74
- );
75
- const DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE = new RegExp(
76
- String.raw`(?:\u001b\][^\u001b\u009c\u0007]*|\u009d[^\u001b\u009c\u0007]*|\u001b\[[0-?]*[ -/]*|\u009b[0-?]*[ -/]*|\u001b)$`,
77
- );
78
-
79
- export async function handleCodexAppServerElicitationRequest(params: {
80
- requestParams: JsonValue | undefined;
81
- paramsForRun: EmbeddedRunAttemptParams;
82
- threadId: string;
83
- turnId: string;
84
- pluginAppPolicyContext?: PluginAppPolicyContext;
85
- signal?: AbortSignal;
86
- }): Promise<JsonValue | undefined> {
87
- const requestParams = isJsonObject(params.requestParams) ? params.requestParams : undefined;
88
- if (!requestParams) {
89
- return undefined;
90
- }
91
- if (!matchesCurrentThread(requestParams, params.threadId)) {
92
- return undefined;
93
- }
94
- if (turnIdMismatches(requestParams, params.turnId)) {
95
- return undefined;
96
- }
97
- const pluginResolution = resolvePluginElicitation({
98
- requestParams,
99
- pluginAppPolicyContext: params.pluginAppPolicyContext,
100
- });
101
- if (pluginResolution.kind !== "not_plugin") {
102
- if (pluginResolution.kind === "decline") {
103
- logPluginElicitationDecline(pluginResolution.reason, requestParams);
104
- return declineElicitationResponse();
105
- }
106
- if (!hasExactTurnId(requestParams, params.turnId)) {
107
- logPluginElicitationDecline("missing_active_turn", requestParams);
108
- return declineElicitationResponse();
109
- }
110
- return buildPluginPolicyElicitationResponse(pluginResolution.entry, requestParams);
111
- }
112
-
113
- const approvalPrompt = readBridgeableApprovalElicitation(requestParams);
114
- if (!approvalPrompt) {
115
- return undefined;
116
- }
117
-
118
- const outcome = await requestPluginApprovalOutcome({
119
- paramsForRun: params.paramsForRun,
120
- title: approvalPrompt.title,
121
- description: approvalPrompt.description,
122
- signal: params.signal,
123
- });
124
- return buildElicitationResponse(approvalPrompt.requestedSchema, approvalPrompt.meta, outcome);
125
- }
126
-
127
- function matchesCurrentThread(requestParams: JsonObject | undefined, threadId: string): boolean {
128
- if (!requestParams) {
129
- return false;
130
- }
131
- const requestThreadId = readString(requestParams, "threadId");
132
- return requestThreadId === threadId;
133
- }
134
-
135
- function turnIdMismatches(requestParams: JsonObject | undefined, turnId: string): boolean {
136
- const rawTurnId = requestParams?.turnId;
137
- return rawTurnId !== null && rawTurnId !== undefined && rawTurnId !== turnId;
138
- }
139
-
140
- function hasExactTurnId(requestParams: JsonObject | undefined, turnId: string): boolean {
141
- return requestParams?.turnId === turnId;
142
- }
143
-
144
- function resolvePluginElicitation(params: {
145
- requestParams: JsonObject | undefined;
146
- pluginAppPolicyContext?: PluginAppPolicyContext;
147
- }): PluginElicitationResolution {
148
- const requestParams = params.requestParams;
149
- if (!requestParams) {
150
- return { kind: "not_plugin" };
151
- }
152
- const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
153
- const context = params.pluginAppPolicyContext;
154
- const entries = context ? Object.values(context.apps) : [];
155
-
156
- const appId =
157
- readFirstString(meta, PLUGIN_APP_ID_META_KEYS) ??
158
- readFirstString(requestParams, PLUGIN_APP_ID_META_KEYS);
159
- const connectorId = readFirstString(meta, PLUGIN_CONNECTOR_ID_META_KEYS);
160
- const isCodexConnectorApproval = isCodexConnectorApprovalElicitation(requestParams, meta);
161
- if (isCodexConnectorApproval && appId && connectorId && appId !== connectorId) {
162
- return { kind: "decline", reason: "app_id_connector_id_mismatch" };
163
- }
164
- if (appId) {
165
- if (!context) {
166
- return { kind: "decline", reason: "missing_policy_context" };
167
- }
168
- const entry = context.apps[appId];
169
- return uniquePluginMatch(entry ? [entry] : [], "app_id");
170
- }
171
- if (isCodexConnectorApproval && connectorId) {
172
- if (!context) {
173
- return { kind: "decline", reason: "missing_policy_context" };
174
- }
175
- const entry = context.apps[connectorId];
176
- return uniquePluginMatch(entry ? [entry] : [], "connector_id");
177
- }
178
-
179
- const serverName = readString(requestParams, "serverName");
180
- if (serverName && context) {
181
- const matches = entries.filter((entry) => entry.mcpServerNames.includes(serverName));
182
- if (matches.length > 0) {
183
- return uniquePluginMatch(matches, "server_name");
184
- }
185
- }
186
-
187
- const metadataResolution = resolvePluginStableMetadataMatch({
188
- meta,
189
- requestParams,
190
- entries,
191
- context,
192
- });
193
- if (metadataResolution.kind !== "not_plugin") {
194
- return metadataResolution;
195
- }
196
-
197
- if (context && hasDisplayNameOnlyPluginMatch(meta, entries)) {
198
- return { kind: "decline", reason: "display_name_only" };
199
- }
200
-
201
- return { kind: "not_plugin" };
202
- }
203
-
204
- function isCodexConnectorApprovalElicitation(requestParams: JsonObject, meta: JsonObject): boolean {
205
- return (
206
- readString(requestParams, "serverName") === CODEX_APPS_SERVER_NAME &&
207
- readString(meta, MCP_TOOL_APPROVAL_KIND_KEY) === MCP_TOOL_APPROVAL_KIND &&
208
- readString(meta, MCP_TOOL_APPROVAL_SOURCE_KEY) === MCP_TOOL_APPROVAL_CONNECTOR_SOURCE
209
- );
210
- }
211
-
212
- function resolvePluginStableMetadataMatch(params: {
213
- meta: JsonObject;
214
- requestParams: JsonObject;
215
- entries: PluginAppPolicyContextEntry[];
216
- context?: PluginAppPolicyContext;
217
- }): PluginElicitationResolution {
218
- const pluginName =
219
- readFirstString(params.meta, PLUGIN_NAME_META_KEYS) ??
220
- readFirstString(params.requestParams, PLUGIN_NAME_META_KEYS);
221
- const configKey =
222
- readFirstString(params.meta, PLUGIN_CONFIG_KEY_META_KEYS) ??
223
- readFirstString(params.requestParams, PLUGIN_CONFIG_KEY_META_KEYS);
224
- const marketplaceName =
225
- readFirstString(params.meta, PLUGIN_MARKETPLACE_NAME_META_KEYS) ??
226
- readFirstString(params.requestParams, PLUGIN_MARKETPLACE_NAME_META_KEYS);
227
- if (!pluginName && !configKey) {
228
- return { kind: "not_plugin" };
229
- }
230
- if (!params.context) {
231
- return { kind: "decline", reason: "missing_policy_context" };
232
- }
233
- const matches = params.entries.filter((entry) => {
234
- if (marketplaceName && entry.marketplaceName !== marketplaceName) {
235
- return false;
236
- }
237
- if (pluginName && entry.pluginName !== pluginName) {
238
- return false;
239
- }
240
- if (configKey && entry.configKey !== configKey) {
241
- return false;
242
- }
243
- return true;
244
- });
245
- return uniquePluginMatch(matches, "metadata");
246
- }
247
-
248
- function uniquePluginMatch(
249
- matches: PluginAppPolicyContextEntry[],
250
- source: string,
251
- ): PluginElicitationResolution {
252
- if (matches.length === 1 && matches[0]) {
253
- return { kind: "matched", entry: matches[0] };
254
- }
255
- return {
256
- kind: "decline",
257
- reason: matches.length === 0 ? `${source}_not_enabled` : `${source}_ambiguous`,
258
- };
259
- }
260
-
261
- function hasDisplayNameOnlyPluginMatch(
262
- meta: JsonObject,
263
- entries: PluginAppPolicyContextEntry[],
264
- ): boolean {
265
- const connectorName = readString(meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY);
266
- if (!connectorName) {
267
- return false;
268
- }
269
- const normalized = normalizePluginIdentityText(connectorName);
270
- return entries.some(
271
- (entry) =>
272
- normalizePluginIdentityText(entry.pluginName) === normalized ||
273
- normalizePluginIdentityText(entry.configKey) === normalized,
274
- );
275
- }
276
-
277
- function normalizePluginIdentityText(value: string): string {
278
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
279
- }
280
-
281
- function buildPluginPolicyElicitationResponse(
282
- entry: PluginAppPolicyContextEntry,
283
- requestParams: JsonObject,
284
- ): JsonValue {
285
- if (!entry.allowDestructiveActions) {
286
- logPluginElicitationDecline("destructive_actions_disabled", requestParams);
287
- return declineElicitationResponse();
288
- }
289
- if (
290
- readString(requestParams, "mode") !== "form" ||
291
- !isJsonObject(requestParams.requestedSchema)
292
- ) {
293
- logPluginElicitationDecline("unsupported_schema", requestParams);
294
- return declineElicitationResponse();
295
- }
296
- const meta = isJsonObject(requestParams["_meta"]) ? requestParams["_meta"] : {};
297
- const response = buildElicitationResponse(requestParams.requestedSchema, meta, "approved-once");
298
- if (isJsonObject(response) && response.action === "accept") {
299
- return response;
300
- }
301
- logPluginElicitationDecline("unmappable_schema", requestParams);
302
- return declineElicitationResponse();
303
- }
304
-
305
- function declineElicitationResponse(): JsonValue {
306
- return { action: "decline", content: null, _meta: null };
307
- }
308
-
309
- function logPluginElicitationDecline(reason: string, requestParams: JsonObject | undefined): void {
310
- embeddedAgentLog.debug("codex plugin elicitation declined", {
311
- reason,
312
- serverName: readString(requestParams, "serverName"),
313
- mode: readString(requestParams, "mode"),
314
- });
315
- }
316
-
317
- function readBridgeableApprovalElicitation(
318
- requestParams: JsonObject | undefined,
319
- ): BridgeableApprovalElicitation | undefined {
320
- if (
321
- !requestParams ||
322
- readString(requestParams, "mode") !== "form" ||
323
- !isJsonObject(requestParams["_meta"]) ||
324
- requestParams["_meta"][MCP_TOOL_APPROVAL_KIND_KEY] !== MCP_TOOL_APPROVAL_KIND ||
325
- !isJsonObject(requestParams.requestedSchema)
326
- ) {
327
- return undefined;
328
- }
329
-
330
- const requestedSchema = requestParams.requestedSchema;
331
- if (
332
- readString(requestedSchema, "type") !== "object" ||
333
- !isJsonObject(requestedSchema.properties)
334
- ) {
335
- return undefined;
336
- }
337
-
338
- const title =
339
- sanitizeDisplayText(readString(requestParams, "message") ?? "") || "Codex MCP tool approval";
340
- return {
341
- title,
342
- description: buildApprovalDescription({
343
- title,
344
- meta: requestParams["_meta"],
345
- requestedSchema,
346
- serverName: sanitizeOptionalDisplayText(readString(requestParams, "serverName")),
347
- }),
348
- requestedSchema,
349
- meta: requestParams["_meta"],
350
- };
351
- }
352
-
353
- function buildApprovalDescription(params: {
354
- title: string;
355
- meta: JsonObject;
356
- requestedSchema: JsonObject;
357
- serverName: string | undefined;
358
- }): string {
359
- const connectorName = sanitizeOptionalDisplayText(
360
- readString(params.meta, MCP_TOOL_APPROVAL_CONNECTOR_NAME_KEY),
361
- );
362
- const toolTitle = sanitizeOptionalDisplayText(
363
- readString(params.meta, MCP_TOOL_APPROVAL_TOOL_TITLE_KEY),
364
- );
365
- const toolDescription = sanitizeOptionalDisplayText(
366
- readString(params.meta, MCP_TOOL_APPROVAL_TOOL_DESCRIPTION_KEY),
367
- );
368
- const summaryLines = [
369
- connectorName && `App: ${connectorName}`,
370
- toolTitle && `Tool: ${toolTitle}`,
371
- params.serverName && `MCP server: ${params.serverName}`,
372
- toolDescription,
373
- ].filter((line): line is string => Boolean(line));
374
- const paramLines = readDisplayParamLines(params.meta);
375
- const propertyLines = readPropertyDescriptionLines(params.requestedSchema);
376
- return [
377
- params.title,
378
- summaryLines.join("\n"),
379
- paramLines.length > 0 ? ["Parameters:", ...paramLines].join("\n") : "",
380
- propertyLines.length > 0 ? ["Fields:", ...propertyLines].join("\n") : "",
381
- ]
382
- .filter(Boolean)
383
- .join("\n\n");
384
- }
385
-
386
- function readPropertyDescriptionLines(requestedSchema: JsonObject): string[] {
387
- const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
388
- return Object.entries(properties)
389
- .map(([name, value]) => {
390
- const schema = isJsonObject(value) ? value : undefined;
391
- if (!schema) {
392
- return undefined;
393
- }
394
- const propTitle =
395
- sanitizeDisplayText(readString(schema, "title") ?? "") ||
396
- sanitizeDisplayText(name) ||
397
- "field";
398
- const description = sanitizeOptionalDisplayText(readString(schema, "description"));
399
- return description ? `- ${propTitle}: ${description}` : `- ${propTitle}`;
400
- })
401
- .filter((line): line is string => Boolean(line));
402
- }
403
-
404
- function readDisplayParamLines(meta: JsonObject): string[] {
405
- const displayParams = meta[MCP_TOOL_APPROVAL_TOOL_PARAMS_DISPLAY_KEY];
406
- if (!Array.isArray(displayParams)) {
407
- return [];
408
- }
409
- const lines = displayParams
410
- .slice(0, MAX_DISPLAY_PARAM_ENTRIES)
411
- .map((entry) => {
412
- const param = isJsonObject(entry) ? entry : undefined;
413
- if (!param) {
414
- return undefined;
415
- }
416
- const name =
417
- sanitizeOptionalDisplayText(readString(param, "display_name")) ??
418
- sanitizeOptionalDisplayText(readString(param, "name"));
419
- if (!name) {
420
- return undefined;
421
- }
422
- return `- ${name}: ${formatDisplayParamValue(param.value)}`;
423
- })
424
- .filter((line): line is string => Boolean(line));
425
- const remaining = displayParams.length - MAX_DISPLAY_PARAM_ENTRIES;
426
- return remaining > 0 ? [...lines, `- Additional parameters: ${remaining} more`] : lines;
427
- }
428
-
429
- function formatDisplayParamValue(value: JsonValue | undefined): string {
430
- const formatted = typeof value === "string" ? value : formatDisplayJsonValue(value ?? null);
431
- return truncateDisplayText(sanitizeDisplayText(formatted), MAX_DISPLAY_PARAM_VALUE_LENGTH);
432
- }
433
-
434
- function formatDisplayJsonValue(value: JsonValue, depth = MAX_DISPLAY_VALUE_DEPTH): string {
435
- if (value === null) {
436
- return "null";
437
- }
438
- if (typeof value === "string") {
439
- return JSON.stringify(truncateDisplayText(sanitizeDisplayText(value), 80));
440
- }
441
- if (typeof value === "number" || typeof value === "boolean") {
442
- return String(value);
443
- }
444
- if (Array.isArray(value)) {
445
- if (depth <= 0) {
446
- return "[truncated]";
447
- }
448
- const parts: string[] = [];
449
- const limit = Math.min(value.length, MAX_DISPLAY_VALUE_ARRAY_ITEMS);
450
- for (let i = 0; i < limit; i += 1) {
451
- parts.push(formatDisplayJsonValue(value[i] ?? null, depth - 1));
452
- }
453
- if (value.length > MAX_DISPLAY_VALUE_ARRAY_ITEMS) {
454
- parts.push("...");
455
- }
456
- return `[${parts.join(",")}]`;
457
- }
458
- if (typeof value === "object") {
459
- if (depth <= 0) {
460
- return "{truncated}";
461
- }
462
- const parts: string[] = [];
463
- let count = 0;
464
- let truncated = false;
465
- for (const key in value) {
466
- if (!Object.prototype.hasOwnProperty.call(value, key)) {
467
- continue;
468
- }
469
- if (count >= MAX_DISPLAY_VALUE_OBJECT_KEYS) {
470
- truncated = true;
471
- break;
472
- }
473
- const safeKey = truncateDisplayText(sanitizeDisplayText(key), 80);
474
- parts.push(
475
- `${JSON.stringify(safeKey)}:${formatDisplayJsonValue(value[key] ?? null, depth - 1)}`,
476
- );
477
- count += 1;
478
- }
479
- if (truncated) {
480
- parts.push("...");
481
- }
482
- return `{${parts.join(",")}}`;
483
- }
484
- return "null";
485
- }
486
-
487
- function sanitizeOptionalDisplayText(value: string | undefined): string | undefined {
488
- const sanitized = value === undefined ? "" : sanitizeDisplayText(value);
489
- return sanitized || undefined;
490
- }
491
-
492
- function sanitizeDisplayText(value: string): string {
493
- const scanned = value.slice(0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
494
- const clipped = value.length > DISPLAY_TEXT_SCAN_MAX_LENGTH;
495
- const sanitized = scanned
496
- .replace(ANSI_OSC_SEQUENCE_RE, "")
497
- .replace(ANSI_CONTROL_SEQUENCE_RE, "")
498
- .replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE, "")
499
- .replace(INVISIBLE_FORMATTING_CONTROL_RE, " ")
500
- .replace(CONTROL_CHARACTER_RE, " ")
501
- .replace(/\s+/g, " ")
502
- .trim();
503
- const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
504
- return clipped && escaped ? `${escaped}...` : escaped;
505
- }
506
-
507
- function truncateDisplayText(value: string, maxLength: number): string {
508
- return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
509
- }
510
-
511
- async function requestPluginApprovalOutcome(params: {
512
- paramsForRun: EmbeddedRunAttemptParams;
513
- title: string;
514
- description: string;
515
- signal?: AbortSignal;
516
- }): Promise<AppServerApprovalOutcome> {
517
- try {
518
- const requestResult = await requestPluginApproval({
519
- paramsForRun: params.paramsForRun,
520
- title: params.title,
521
- description: params.description,
522
- severity: "warning",
523
- toolName: "codex_mcp_tool_approval",
524
- });
525
-
526
- const approvalId = requestResult?.id;
527
- if (!approvalId) {
528
- return "unavailable";
529
- }
530
-
531
- const decision = approvalRequestExplicitlyUnavailable(requestResult)
532
- ? null
533
- : await waitForPluginApprovalDecision({ approvalId, signal: params.signal });
534
- return mapExecDecisionToOutcome(decision);
535
- } catch {
536
- return params.signal?.aborted ? "cancelled" : "denied";
537
- }
538
- }
539
-
540
- function buildElicitationResponse(
541
- requestedSchema: JsonObject,
542
- meta: JsonObject,
543
- outcome: AppServerApprovalOutcome,
544
- ): JsonValue {
545
- if (outcome === "cancelled") {
546
- return { action: "cancel", content: null, _meta: null };
547
- }
548
- if (outcome === "denied" || outcome === "unavailable") {
549
- return { action: "decline", content: null, _meta: null };
550
- }
551
-
552
- const content = buildAcceptedContent(requestedSchema, meta, outcome);
553
- if (!content) {
554
- if (hasNoSchemaProperties(requestedSchema)) {
555
- return {
556
- action: "accept",
557
- content: null,
558
- _meta: buildAcceptedMeta(meta, outcome),
559
- };
560
- }
561
- embeddedAgentLog.warn("codex MCP approval elicitation approved without a mappable response", {
562
- approvalKind: meta[MCP_TOOL_APPROVAL_KIND_KEY],
563
- fields: Object.keys(requestedSchema.properties ?? {}),
564
- outcome,
565
- });
566
- return { action: "decline", content: null, _meta: null };
567
- }
568
- return { action: "accept", content, _meta: buildAcceptedMeta(meta, outcome) };
569
- }
570
-
571
- function buildAcceptedContent(
572
- requestedSchema: JsonObject,
573
- meta: JsonObject,
574
- outcome: AppServerApprovalOutcome,
575
- ): JsonObject | undefined {
576
- const properties = isJsonObject(requestedSchema.properties)
577
- ? requestedSchema.properties
578
- : undefined;
579
- if (!properties) {
580
- return undefined;
581
- }
582
- const required = Array.isArray(requestedSchema.required)
583
- ? new Set(
584
- requestedSchema.required.filter((entry): entry is string => typeof entry === "string"),
585
- )
586
- : new Set<string>();
587
- const content: JsonObject = {};
588
- let sawApprovalField = false;
589
-
590
- for (const [name, value] of Object.entries(properties)) {
591
- const schema = isJsonObject(value) ? value : undefined;
592
- if (!schema) {
593
- continue;
594
- }
595
- const property = { name, schema, required: required.has(name) };
596
- const next =
597
- readApprovalFieldValue(property, outcome) ??
598
- readPersistFieldValue(property, meta, outcome) ??
599
- readFallbackFieldValue(property, outcome);
600
-
601
- if (next === undefined) {
602
- if (isApprovalField(property)) {
603
- sawApprovalField = true;
604
- }
605
- if (property.required) {
606
- return undefined;
607
- }
608
- continue;
609
- }
610
-
611
- if (isApprovalField(property)) {
612
- sawApprovalField = true;
613
- }
614
- content[name] = next;
615
- }
616
-
617
- return sawApprovalField ? content : undefined;
618
- }
619
-
620
- function readApprovalFieldValue(
621
- property: ApprovalPropertyContext,
622
- outcome: AppServerApprovalOutcome,
623
- ): JsonValue | undefined {
624
- if (!isApprovalField(property)) {
625
- return undefined;
626
- }
627
- const type = readString(property.schema, "type");
628
- if (type === "boolean") {
629
- return true;
630
- }
631
- const options = readEnumOptions(property.schema);
632
- if (options.length === 0) {
633
- return undefined;
634
- }
635
-
636
- const sessionChoice = options.find((option) => isSessionApprovalOption(option));
637
- const acceptChoice = options.find((option) => isPositiveApprovalOption(option));
638
- if (outcome === "approved-session") {
639
- return sessionChoice?.value ?? acceptChoice?.value;
640
- }
641
- return acceptChoice?.value ?? sessionChoice?.value;
642
- }
643
-
644
- function readPersistFieldValue(
645
- property: ApprovalPropertyContext,
646
- meta: JsonObject,
647
- outcome: AppServerApprovalOutcome,
648
- ): JsonValue | undefined {
649
- if (!isPersistField(property) || outcome !== "approved-session") {
650
- return undefined;
651
- }
652
- const persistHints = readPersistHints(meta);
653
- const options = readEnumOptions(property.schema);
654
- if (options.length === 0) {
655
- return undefined;
656
- }
657
- const preferred = choosePersistHint(persistHints);
658
- if (preferred) {
659
- const match = options.find(
660
- (option) => option.value === preferred || option.label === preferred,
661
- );
662
- return match?.value;
663
- }
664
- return undefined;
665
- }
666
-
667
- function readDefaultValue(schema: JsonObject): JsonValue | undefined {
668
- return schema.default as JsonValue | undefined;
669
- }
670
-
671
- function readFallbackFieldValue(
672
- property: ApprovalPropertyContext,
673
- outcome: AppServerApprovalOutcome,
674
- ): JsonValue | undefined {
675
- if (outcome === "approved-once" && isPersistField(property)) {
676
- return undefined;
677
- }
678
- return readDefaultValue(property.schema);
679
- }
680
-
681
- function isApprovalField(property: ApprovalPropertyContext): boolean {
682
- const haystack = propertyText(property).toLowerCase();
683
- return /\b(approve|approval|allow|accept|decision)\b/.test(haystack);
684
- }
685
-
686
- function isPersistField(property: ApprovalPropertyContext): boolean {
687
- const haystack = propertyText(property).toLowerCase();
688
- return /\b(persist|session|always|scope)\b/.test(haystack);
689
- }
690
-
691
- function propertyText(property: ApprovalPropertyContext): string {
692
- return [
693
- property.name,
694
- readString(property.schema, "title"),
695
- readString(property.schema, "description"),
696
- ]
697
- .filter(Boolean)
698
- .join(" ");
699
- }
700
-
701
- function readPersistHints(meta: JsonObject): string[] {
702
- const raw = meta.persist;
703
- if (typeof raw === "string") {
704
- return [raw];
705
- }
706
- if (Array.isArray(raw)) {
707
- return raw.filter((entry): entry is string => typeof entry === "string");
708
- }
709
- return ["session", "always"];
710
- }
711
-
712
- function buildAcceptedMeta(meta: JsonObject, outcome: AppServerApprovalOutcome): JsonObject | null {
713
- if (outcome !== "approved-session") {
714
- return null;
715
- }
716
- const persist = choosePersistHint(readPersistHints(meta));
717
- return persist ? { persist } : null;
718
- }
719
-
720
- function choosePersistHint(persistHints: string[]): "always" | "session" | undefined {
721
- if (persistHints.includes("always")) {
722
- return "always";
723
- }
724
- if (persistHints.includes("session")) {
725
- return "session";
726
- }
727
- return undefined;
728
- }
729
-
730
- function hasNoSchemaProperties(requestedSchema: JsonObject): boolean {
731
- const properties = isJsonObject(requestedSchema.properties) ? requestedSchema.properties : {};
732
- return Object.keys(properties).length === 0;
733
- }
734
-
735
- function readEnumOptions(schema: JsonObject): Array<{ value: string; label: string }> {
736
- if (Array.isArray(schema.enum)) {
737
- const values = schema.enum.filter((entry): entry is string => typeof entry === "string");
738
- const labels = Array.isArray(schema.enumNames)
739
- ? schema.enumNames.filter((entry): entry is string => typeof entry === "string")
740
- : [];
741
- return values.map((value, index) => ({ value, label: labels[index] ?? value }));
742
- }
743
- if (Array.isArray(schema.oneOf)) {
744
- return schema.oneOf
745
- .map((entry) => {
746
- const option = isJsonObject(entry) ? entry : undefined;
747
- const value = readString(option, "const");
748
- if (!value) {
749
- return undefined;
750
- }
751
- return { value, label: readString(option, "title") ?? value };
752
- })
753
- .filter((entry): entry is { value: string; label: string } => Boolean(entry));
754
- }
755
- return [];
756
- }
757
-
758
- function isPositiveApprovalOption(option: { value: string; label: string }): boolean {
759
- const haystack = `${option.value} ${option.label}`.toLowerCase();
760
- return /\b(allow|approve|accept|yes|continue|proceed|true)\b/.test(haystack);
761
- }
762
-
763
- function isSessionApprovalOption(option: { value: string; label: string }): boolean {
764
- const haystack = `${option.value} ${option.label}`.toLowerCase();
765
- return (
766
- /\b(session|always|persistent)\b/.test(haystack) && /\b(allow|approve|accept)\b/.test(haystack)
767
- );
768
- }
769
-
770
- function readString(record: JsonObject | undefined, key: string): string | undefined {
771
- const value = record?.[key];
772
- return typeof value === "string" && value.trim() ? value : undefined;
773
- }
774
-
775
- function readFirstString(record: JsonObject | undefined, keys: string[]): string | undefined {
776
- for (const key of keys) {
777
- const value = readString(record, key);
778
- if (value) {
779
- return value;
780
- }
781
- }
782
- return undefined;
783
- }