@ccpocket/bridge 1.66.0 → 1.66.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/dist/codex-process.d.ts +9 -0
- package/dist/codex-process.js +365 -40
- package/dist/codex-process.js.map +1 -1
- package/dist/parser.d.ts +4 -0
- package/dist/parser.js +4 -0
- package/dist/parser.js.map +1 -1
- package/dist/usage.d.ts +15 -0
- package/dist/usage.js +27 -13
- package/dist/usage.js.map +1 -1
- package/dist/websocket.js +26 -0
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/codex-process.d.ts
CHANGED
|
@@ -253,6 +253,12 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
|
|
|
253
253
|
approveAlways(toolUseId?: string): void;
|
|
254
254
|
reject(toolUseId?: string, _message?: string): void;
|
|
255
255
|
answer(toolUseId: string, result: string): void;
|
|
256
|
+
/**
|
|
257
|
+
* Install a plugin or begin connector authentication proposed by Codex.
|
|
258
|
+
* The elicitation remains pending while external app authentication is
|
|
259
|
+
* required, and is accepted only after installation is complete.
|
|
260
|
+
*/
|
|
261
|
+
installToolSuggestion(toolUseId: string): Promise<void>;
|
|
256
262
|
getPendingPermission(toolUseId?: string): {
|
|
257
263
|
toolUseId: string;
|
|
258
264
|
toolName: string;
|
|
@@ -269,6 +275,8 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
|
|
|
269
275
|
* the permission (approve/reject) path.
|
|
270
276
|
*/
|
|
271
277
|
private approveUserInput;
|
|
278
|
+
private updateToolSuggestion;
|
|
279
|
+
private resolveToolSuggestion;
|
|
272
280
|
/**
|
|
273
281
|
* Reject a pending user-input request (McpElicitation fallback).
|
|
274
282
|
*/
|
|
@@ -303,6 +311,7 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
|
|
|
303
311
|
private request;
|
|
304
312
|
private notify;
|
|
305
313
|
private respondToServerRequest;
|
|
314
|
+
private respondToServerRequestError;
|
|
306
315
|
private writeEnvelope;
|
|
307
316
|
private rejectAllPending;
|
|
308
317
|
private setStatus;
|
package/dist/codex-process.js
CHANGED
|
@@ -611,6 +611,94 @@ export class CodexProcess extends EventEmitter {
|
|
|
611
611
|
this.setStatus("running");
|
|
612
612
|
}
|
|
613
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Install a plugin or begin connector authentication proposed by Codex.
|
|
616
|
+
* The elicitation remains pending while external app authentication is
|
|
617
|
+
* required, and is accepted only after installation is complete.
|
|
618
|
+
*/
|
|
619
|
+
async installToolSuggestion(toolUseId) {
|
|
620
|
+
const pending = this.resolvePendingUserInput(toolUseId);
|
|
621
|
+
if (!pending || pending.kind !== "tool_suggestion") {
|
|
622
|
+
throw new Error("No pending tool suggestion found");
|
|
623
|
+
}
|
|
624
|
+
const currentState = pending.input.installState;
|
|
625
|
+
if (currentState === "installing")
|
|
626
|
+
return;
|
|
627
|
+
if (currentState === "needs_auth")
|
|
628
|
+
return;
|
|
629
|
+
const meta = asRecord(pending.input._meta) ?? {};
|
|
630
|
+
const toolType = stringValue(meta.tool_type) ?? "";
|
|
631
|
+
const suggestType = stringValue(meta.suggest_type) ?? "";
|
|
632
|
+
if (suggestType !== "install") {
|
|
633
|
+
this.updateToolSuggestion(pending, {
|
|
634
|
+
installState: "failed",
|
|
635
|
+
installError: `Unsupported suggestion action: ${suggestType || "unknown"}`,
|
|
636
|
+
});
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
if (toolType === "connector") {
|
|
640
|
+
const installUrl = stringValue(meta.install_url);
|
|
641
|
+
if (!installUrl) {
|
|
642
|
+
this.updateToolSuggestion(pending, {
|
|
643
|
+
installState: "failed",
|
|
644
|
+
installError: "This connector did not provide an installation URL.",
|
|
645
|
+
});
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
this.updateToolSuggestion(pending, { installState: "needs_auth" });
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
if (toolType !== "plugin") {
|
|
652
|
+
this.updateToolSuggestion(pending, {
|
|
653
|
+
installState: "failed",
|
|
654
|
+
installError: `Unsupported tool type: ${toolType || "unknown"}`,
|
|
655
|
+
});
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
const toolId = stringValue(meta.tool_id) ?? "";
|
|
659
|
+
const remotePluginId = stringValue(meta.remote_plugin_id);
|
|
660
|
+
const separator = toolId.lastIndexOf("@");
|
|
661
|
+
const fallbackPluginName = separator > 0 ? toolId.slice(0, separator) : toolId;
|
|
662
|
+
const remoteMarketplaceName = separator > 0 ? toolId.slice(separator + 1) : "openai-curated-remote";
|
|
663
|
+
const pluginName = remotePluginId ?? fallbackPluginName;
|
|
664
|
+
if (!pluginName) {
|
|
665
|
+
this.updateToolSuggestion(pending, {
|
|
666
|
+
installState: "failed",
|
|
667
|
+
installError: "This plugin did not provide an installation identifier.",
|
|
668
|
+
});
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
this.updateToolSuggestion(pending, {
|
|
672
|
+
installState: "installing",
|
|
673
|
+
installError: null,
|
|
674
|
+
});
|
|
675
|
+
try {
|
|
676
|
+
const result = (await this.request("plugin/install", {
|
|
677
|
+
remoteMarketplaceName,
|
|
678
|
+
pluginName,
|
|
679
|
+
}));
|
|
680
|
+
// The user may reject the suggestion while installation is in flight.
|
|
681
|
+
if (this.pendingUserInputs.get(toolUseId) !== pending)
|
|
682
|
+
return;
|
|
683
|
+
const appsNeedingAuth = normalizeToolSuggestionApps(result.appsNeedingAuth);
|
|
684
|
+
if (appsNeedingAuth.length > 0) {
|
|
685
|
+
this.updateToolSuggestion(pending, {
|
|
686
|
+
installState: "needs_auth",
|
|
687
|
+
appsNeedingAuth,
|
|
688
|
+
});
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
this.resolveToolSuggestion(pending, "Installed");
|
|
692
|
+
}
|
|
693
|
+
catch (err) {
|
|
694
|
+
if (this.pendingUserInputs.get(toolUseId) !== pending)
|
|
695
|
+
return;
|
|
696
|
+
this.updateToolSuggestion(pending, {
|
|
697
|
+
installState: "failed",
|
|
698
|
+
installError: err instanceof Error ? err.message : String(err),
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
614
702
|
getPendingPermission(toolUseId) {
|
|
615
703
|
// Check plan completion first
|
|
616
704
|
if (this.pendingPlanCompletion) {
|
|
@@ -669,6 +757,15 @@ export class CodexProcess extends EventEmitter {
|
|
|
669
757
|
const pending = this.resolvePendingUserInput(toolUseId);
|
|
670
758
|
if (!pending)
|
|
671
759
|
return false;
|
|
760
|
+
if (pending.kind === "tool_suggestion") {
|
|
761
|
+
if (pending.input.installState === "needs_auth") {
|
|
762
|
+
this.resolveToolSuggestion(pending, "Installed");
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
void this.installToolSuggestion(pending.toolUseId);
|
|
766
|
+
}
|
|
767
|
+
return true;
|
|
768
|
+
}
|
|
672
769
|
this.pendingUserInputs.delete(pending.toolUseId);
|
|
673
770
|
this.respondToServerRequest(pending.requestId, buildUserInputResponse(pending, result));
|
|
674
771
|
this.emitToolResult(pending.toolUseId, "Approved");
|
|
@@ -677,6 +774,31 @@ export class CodexProcess extends EventEmitter {
|
|
|
677
774
|
}
|
|
678
775
|
return true;
|
|
679
776
|
}
|
|
777
|
+
updateToolSuggestion(pending, changes) {
|
|
778
|
+
pending.input = { ...pending.input, ...changes };
|
|
779
|
+
this.emitMessage({
|
|
780
|
+
type: "permission_request",
|
|
781
|
+
toolUseId: pending.toolUseId,
|
|
782
|
+
toolName: "ToolSuggestion",
|
|
783
|
+
input: { ...pending.input },
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
resolveToolSuggestion(pending, toolResult) {
|
|
787
|
+
this.pendingUserInputs.delete(pending.toolUseId);
|
|
788
|
+
this.respondToServerRequest(pending.requestId, {
|
|
789
|
+
action: "accept",
|
|
790
|
+
content: null,
|
|
791
|
+
_meta: null,
|
|
792
|
+
});
|
|
793
|
+
this.emitMessage({
|
|
794
|
+
type: "permission_resolved",
|
|
795
|
+
toolUseId: pending.toolUseId,
|
|
796
|
+
});
|
|
797
|
+
this.emitToolResult(pending.toolUseId, toolResult);
|
|
798
|
+
if (this.pendingApprovals.size === 0 && this.pendingUserInputs.size === 0) {
|
|
799
|
+
this.setStatus("running");
|
|
800
|
+
}
|
|
801
|
+
}
|
|
680
802
|
/**
|
|
681
803
|
* Reject a pending user-input request (McpElicitation fallback).
|
|
682
804
|
*/
|
|
@@ -1402,10 +1524,13 @@ export class CodexProcess extends EventEmitter {
|
|
|
1402
1524
|
case "mcpServer/elicitation/request": {
|
|
1403
1525
|
const toolUseId = this.extractToolUseId(params, id);
|
|
1404
1526
|
const elicitation = createElicitationInput(params);
|
|
1527
|
+
const toolName = elicitation.kind === "tool_suggestion"
|
|
1528
|
+
? "ToolSuggestion"
|
|
1529
|
+
: "McpElicitation";
|
|
1405
1530
|
this.pendingUserInputs.set(toolUseId, {
|
|
1406
1531
|
requestId: id,
|
|
1407
1532
|
toolUseId,
|
|
1408
|
-
toolName
|
|
1533
|
+
toolName,
|
|
1409
1534
|
questions: elicitation.questions,
|
|
1410
1535
|
input: elicitation.input,
|
|
1411
1536
|
kind: elicitation.kind,
|
|
@@ -1413,15 +1538,23 @@ export class CodexProcess extends EventEmitter {
|
|
|
1413
1538
|
this.emitMessage({
|
|
1414
1539
|
type: "permission_request",
|
|
1415
1540
|
toolUseId,
|
|
1416
|
-
toolName
|
|
1541
|
+
toolName,
|
|
1417
1542
|
input: elicitation.input,
|
|
1418
1543
|
});
|
|
1419
1544
|
this.setStatus("waiting_approval");
|
|
1420
1545
|
break;
|
|
1421
1546
|
}
|
|
1422
|
-
|
|
1423
|
-
this.respondToServerRequest(id, {
|
|
1547
|
+
case "currentTime/read": {
|
|
1548
|
+
this.respondToServerRequest(id, {
|
|
1549
|
+
currentTimeAt: Math.floor(Date.now() / 1000),
|
|
1550
|
+
});
|
|
1551
|
+
break;
|
|
1552
|
+
}
|
|
1553
|
+
default: {
|
|
1554
|
+
console.warn(`[codex-process] unsupported server request: ${method}`);
|
|
1555
|
+
this.respondToServerRequestError(id, -32601, `Unsupported server request: ${method}`);
|
|
1424
1556
|
break;
|
|
1557
|
+
}
|
|
1425
1558
|
}
|
|
1426
1559
|
}
|
|
1427
1560
|
handleNotification(method, params) {
|
|
@@ -1555,6 +1688,41 @@ export class CodexProcess extends EventEmitter {
|
|
|
1555
1688
|
this.handleServerRequestResolved(params);
|
|
1556
1689
|
break;
|
|
1557
1690
|
}
|
|
1691
|
+
case "warning":
|
|
1692
|
+
case "guardianWarning": {
|
|
1693
|
+
const message = stringValue(params.message);
|
|
1694
|
+
if (message) {
|
|
1695
|
+
this.emitMessage({
|
|
1696
|
+
type: "error",
|
|
1697
|
+
errorCode: "codex_warning",
|
|
1698
|
+
message,
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
case "configWarning":
|
|
1704
|
+
case "deprecationNotice": {
|
|
1705
|
+
const summary = stringValue(params.summary);
|
|
1706
|
+
const details = stringValue(params.details);
|
|
1707
|
+
if (summary || details) {
|
|
1708
|
+
this.emitMessage({
|
|
1709
|
+
type: "error",
|
|
1710
|
+
errorCode: "codex_warning",
|
|
1711
|
+
message: [summary, details].filter(Boolean).join("\n"),
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
break;
|
|
1715
|
+
}
|
|
1716
|
+
case "error": {
|
|
1717
|
+
const error = asRecord(params.error);
|
|
1718
|
+
const message = stringValue(error?.message) ?? "Codex runtime error";
|
|
1719
|
+
this.emitMessage({
|
|
1720
|
+
type: "error",
|
|
1721
|
+
errorCode: params.willRetry ? "codex_warning" : "codex_runtime_error",
|
|
1722
|
+
message: params.willRetry ? `${message}\nCodex will retry.` : message,
|
|
1723
|
+
});
|
|
1724
|
+
break;
|
|
1725
|
+
}
|
|
1558
1726
|
default:
|
|
1559
1727
|
break;
|
|
1560
1728
|
}
|
|
@@ -1954,6 +2122,22 @@ export class CodexProcess extends EventEmitter {
|
|
|
1954
2122
|
this.lastPlanItemText = planText;
|
|
1955
2123
|
break;
|
|
1956
2124
|
}
|
|
2125
|
+
case "exitedreviewmode": {
|
|
2126
|
+
const text = typeof item.review === "string" ? item.review : "";
|
|
2127
|
+
if (!text)
|
|
2128
|
+
break;
|
|
2129
|
+
this.lastResultText = text;
|
|
2130
|
+
this.emitMessage({
|
|
2131
|
+
type: "assistant",
|
|
2132
|
+
message: {
|
|
2133
|
+
id: itemId,
|
|
2134
|
+
role: "assistant",
|
|
2135
|
+
content: [{ type: "text", text }],
|
|
2136
|
+
model: this.getMessageModel(),
|
|
2137
|
+
},
|
|
2138
|
+
});
|
|
2139
|
+
break;
|
|
2140
|
+
}
|
|
1957
2141
|
case "error": {
|
|
1958
2142
|
const message = typeof item.message === "string" ? item.message : "Codex item error";
|
|
1959
2143
|
this.emitMessage({ type: "error", message });
|
|
@@ -2039,6 +2223,16 @@ export class CodexProcess extends EventEmitter {
|
|
|
2039
2223
|
}
|
|
2040
2224
|
}
|
|
2041
2225
|
}
|
|
2226
|
+
respondToServerRequestError(id, code, message) {
|
|
2227
|
+
try {
|
|
2228
|
+
this.writeEnvelope({ id, error: { code, message } });
|
|
2229
|
+
}
|
|
2230
|
+
catch (err) {
|
|
2231
|
+
if (!this.stopped) {
|
|
2232
|
+
console.warn(`[codex-process] failed to reject server request: ${err instanceof Error ? err.message : String(err)}`);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2042
2236
|
writeEnvelope(envelope) {
|
|
2043
2237
|
if (!this.transport || !this.transport.isRunning) {
|
|
2044
2238
|
throw new Error("codex app-server is not running");
|
|
@@ -2753,10 +2947,8 @@ function parseResultObject(rawResult) {
|
|
|
2753
2947
|
}
|
|
2754
2948
|
function normalizeAnswerValues(value) {
|
|
2755
2949
|
if (typeof value === "string") {
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
.map((part) => part.trim())
|
|
2759
|
-
.filter((part) => part.length > 0);
|
|
2950
|
+
const normalized = value.trim();
|
|
2951
|
+
return normalized ? [normalized] : [];
|
|
2760
2952
|
}
|
|
2761
2953
|
if (Array.isArray(value)) {
|
|
2762
2954
|
return value
|
|
@@ -2777,6 +2969,13 @@ function normalizeAnswerValues(value) {
|
|
|
2777
2969
|
return normalized ? [normalized] : [];
|
|
2778
2970
|
}
|
|
2779
2971
|
function buildElicitationResponse(pending, rawResult) {
|
|
2972
|
+
if (pending.kind === "tool_suggestion") {
|
|
2973
|
+
return {
|
|
2974
|
+
action: parseElicitationAction(rawResult),
|
|
2975
|
+
content: null,
|
|
2976
|
+
_meta: null,
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2780
2979
|
if (pending.kind === "elicitation_url") {
|
|
2781
2980
|
const action = parseElicitationAction(rawResult);
|
|
2782
2981
|
return {
|
|
@@ -2790,23 +2989,20 @@ function buildElicitationResponse(pending, rawResult) {
|
|
|
2790
2989
|
}
|
|
2791
2990
|
const parsed = parseResultObject(rawResult);
|
|
2792
2991
|
const content = {};
|
|
2992
|
+
const schema = asRecord(pending.input.requestedSchema);
|
|
2993
|
+
const properties = asRecord(schema?.properties) ?? {};
|
|
2793
2994
|
for (const question of pending.questions) {
|
|
2794
2995
|
const candidate = parsed.byId[question.id] ?? parsed.byQuestion[question.question];
|
|
2795
|
-
const
|
|
2796
|
-
if (
|
|
2797
|
-
content[question.id] =
|
|
2798
|
-
}
|
|
2799
|
-
else if (answers.length > 1) {
|
|
2800
|
-
content[question.id] = answers;
|
|
2996
|
+
const value = coerceElicitationValue(candidate, asRecord(properties[question.id]));
|
|
2997
|
+
if (value !== undefined) {
|
|
2998
|
+
content[question.id] = value;
|
|
2801
2999
|
}
|
|
2802
3000
|
}
|
|
2803
3001
|
if (Object.keys(content).length === 0 && pending.questions.length === 1) {
|
|
2804
|
-
const
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
else if (answers.length > 1) {
|
|
2809
|
-
content[pending.questions[0].id] = answers;
|
|
3002
|
+
const questionId = pending.questions[0].id;
|
|
3003
|
+
const value = coerceElicitationValue(rawResult, asRecord(properties[questionId]));
|
|
3004
|
+
if (value !== undefined) {
|
|
3005
|
+
content[questionId] = value;
|
|
2810
3006
|
}
|
|
2811
3007
|
}
|
|
2812
3008
|
return {
|
|
@@ -2815,6 +3011,48 @@ function buildElicitationResponse(pending, rawResult) {
|
|
|
2815
3011
|
_meta: null,
|
|
2816
3012
|
};
|
|
2817
3013
|
}
|
|
3014
|
+
function coerceElicitationValue(value, field) {
|
|
3015
|
+
if (value == null)
|
|
3016
|
+
return undefined;
|
|
3017
|
+
const type = stringValue(field?.type) ?? "string";
|
|
3018
|
+
if (type === "array") {
|
|
3019
|
+
if (Array.isArray(value)) {
|
|
3020
|
+
const entries = value.map((entry) => String(entry));
|
|
3021
|
+
return entries.length > 0 ? entries : undefined;
|
|
3022
|
+
}
|
|
3023
|
+
if (typeof value === "string") {
|
|
3024
|
+
return value
|
|
3025
|
+
.split(",")
|
|
3026
|
+
.map((entry) => entry.trim())
|
|
3027
|
+
.filter(Boolean);
|
|
3028
|
+
}
|
|
3029
|
+
return [String(value)];
|
|
3030
|
+
}
|
|
3031
|
+
const scalar = Array.isArray(value) ? value[0] : value;
|
|
3032
|
+
if (scalar == null)
|
|
3033
|
+
return undefined;
|
|
3034
|
+
if (typeof scalar === "string" && scalar.trim().length === 0) {
|
|
3035
|
+
return undefined;
|
|
3036
|
+
}
|
|
3037
|
+
if (type === "boolean") {
|
|
3038
|
+
if (typeof scalar === "boolean")
|
|
3039
|
+
return scalar;
|
|
3040
|
+
if (String(scalar).toLowerCase() === "true")
|
|
3041
|
+
return true;
|
|
3042
|
+
if (String(scalar).toLowerCase() === "false")
|
|
3043
|
+
return false;
|
|
3044
|
+
return undefined;
|
|
3045
|
+
}
|
|
3046
|
+
if (type === "number" || type === "integer") {
|
|
3047
|
+
const number = typeof scalar === "number" ? scalar : Number(scalar);
|
|
3048
|
+
if (!Number.isFinite(number))
|
|
3049
|
+
return undefined;
|
|
3050
|
+
if (type === "integer" && !Number.isInteger(number))
|
|
3051
|
+
return undefined;
|
|
3052
|
+
return number;
|
|
3053
|
+
}
|
|
3054
|
+
return String(scalar);
|
|
3055
|
+
}
|
|
2818
3056
|
function buildApprovalElicitationResponse(pending, rawResult) {
|
|
2819
3057
|
const selection = resolveApprovalElicitationSelection(pending, rawResult);
|
|
2820
3058
|
const normalized = selection.trim().toLowerCase();
|
|
@@ -2908,7 +3146,32 @@ function createElicitationInput(params) {
|
|
|
2908
3146
|
}
|
|
2909
3147
|
const schema = asRecord(params.requestedSchema);
|
|
2910
3148
|
const elicitationMeta = asRecord(params._meta);
|
|
2911
|
-
if (
|
|
3149
|
+
if (isToolSuggestionElicitation(serverName, elicitationMeta)) {
|
|
3150
|
+
const toolName = stringValue(elicitationMeta?.tool_name) ?? "Tool";
|
|
3151
|
+
return {
|
|
3152
|
+
kind: "tool_suggestion",
|
|
3153
|
+
questions: [],
|
|
3154
|
+
input: {
|
|
3155
|
+
mode: "form",
|
|
3156
|
+
serverName,
|
|
3157
|
+
message,
|
|
3158
|
+
_meta: elicitationMeta ?? null,
|
|
3159
|
+
toolType: stringValue(elicitationMeta?.tool_type),
|
|
3160
|
+
suggestType: stringValue(elicitationMeta?.suggest_type),
|
|
3161
|
+
suggestReason: stringValue(elicitationMeta?.suggest_reason) ?? message,
|
|
3162
|
+
toolId: stringValue(elicitationMeta?.tool_id),
|
|
3163
|
+
toolName,
|
|
3164
|
+
installUrl: stringValue(elicitationMeta?.install_url),
|
|
3165
|
+
remotePluginId: stringValue(elicitationMeta?.remote_plugin_id),
|
|
3166
|
+
appConnectorIds: Array.isArray(elicitationMeta?.app_connector_ids)
|
|
3167
|
+
? elicitationMeta.app_connector_ids.filter((entry) => typeof entry === "string")
|
|
3168
|
+
: [],
|
|
3169
|
+
installState: "idle",
|
|
3170
|
+
appsNeedingAuth: [],
|
|
3171
|
+
},
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
if (isApprovalActionElicitation(schema, serverName, elicitationMeta)) {
|
|
2912
3175
|
const questionId = "approval";
|
|
2913
3176
|
const isToolApproval = isToolApprovalElicitation(elicitationMeta);
|
|
2914
3177
|
return {
|
|
@@ -2944,26 +3207,15 @@ function createElicitationInput(params) {
|
|
|
2944
3207
|
const field = value;
|
|
2945
3208
|
const title = typeof field.title === "string" ? field.title : key;
|
|
2946
3209
|
const description = typeof field.description === "string" ? field.description : message;
|
|
2947
|
-
const enumValues = Array.isArray(field.enum)
|
|
2948
|
-
? field.enum.map((entry) => String(entry))
|
|
2949
|
-
: [];
|
|
2950
3210
|
const type = typeof field.type === "string" ? field.type : "";
|
|
2951
|
-
const options =
|
|
2952
|
-
? enumValues.map((entry, index) => ({
|
|
2953
|
-
label: entry,
|
|
2954
|
-
description: index === 0 ? description : "",
|
|
2955
|
-
}))
|
|
2956
|
-
: type === "boolean"
|
|
2957
|
-
? [
|
|
2958
|
-
{ label: "true", description: description },
|
|
2959
|
-
{ label: "false", description: "" },
|
|
2960
|
-
]
|
|
2961
|
-
: [];
|
|
3211
|
+
const options = buildElicitationFieldOptions(field, description);
|
|
2962
3212
|
return {
|
|
2963
3213
|
id: key,
|
|
2964
3214
|
question: requiredFields.has(key) ? `${title} (required)` : title,
|
|
2965
3215
|
header: serverName,
|
|
2966
3216
|
options,
|
|
3217
|
+
required: requiredFields.has(key),
|
|
3218
|
+
multiSelect: type === "array",
|
|
2967
3219
|
isOther: options.length === 0,
|
|
2968
3220
|
isSecret: false,
|
|
2969
3221
|
};
|
|
@@ -2976,6 +3228,8 @@ function createElicitationInput(params) {
|
|
|
2976
3228
|
question: message,
|
|
2977
3229
|
header: serverName,
|
|
2978
3230
|
options: [],
|
|
3231
|
+
required: true,
|
|
3232
|
+
multiSelect: false,
|
|
2979
3233
|
isOther: true,
|
|
2980
3234
|
isSecret: false,
|
|
2981
3235
|
},
|
|
@@ -2985,6 +3239,7 @@ function createElicitationInput(params) {
|
|
|
2985
3239
|
questions: normalizedQuestions.map((question) => ({
|
|
2986
3240
|
id: question.id,
|
|
2987
3241
|
question: question.question,
|
|
3242
|
+
required: question.required,
|
|
2988
3243
|
})),
|
|
2989
3244
|
input: {
|
|
2990
3245
|
mode: "form",
|
|
@@ -2997,15 +3252,58 @@ function createElicitationInput(params) {
|
|
|
2997
3252
|
header: question.header,
|
|
2998
3253
|
question: question.question,
|
|
2999
3254
|
options: question.options,
|
|
3000
|
-
|
|
3255
|
+
required: question.required,
|
|
3256
|
+
multiSelect: question.multiSelect,
|
|
3001
3257
|
isOther: question.isOther,
|
|
3002
3258
|
isSecret: question.isSecret,
|
|
3003
3259
|
})),
|
|
3004
3260
|
},
|
|
3005
3261
|
};
|
|
3006
3262
|
}
|
|
3007
|
-
function
|
|
3008
|
-
|
|
3263
|
+
function buildElicitationFieldOptions(field, description) {
|
|
3264
|
+
const type = stringValue(field.type);
|
|
3265
|
+
const source = type === "array" ? asRecord(field.items) ?? {} : field;
|
|
3266
|
+
const rawOptions = Array.isArray(source.oneOf)
|
|
3267
|
+
? source.oneOf
|
|
3268
|
+
: Array.isArray(source.anyOf)
|
|
3269
|
+
? source.anyOf
|
|
3270
|
+
: null;
|
|
3271
|
+
if (rawOptions) {
|
|
3272
|
+
return rawOptions.flatMap((entry, index) => {
|
|
3273
|
+
const option = asRecord(entry);
|
|
3274
|
+
const value = stringValue(option?.const);
|
|
3275
|
+
if (!value)
|
|
3276
|
+
return [];
|
|
3277
|
+
return [
|
|
3278
|
+
{
|
|
3279
|
+
label: stringValue(option?.title) ?? value,
|
|
3280
|
+
value,
|
|
3281
|
+
description: index === 0 ? description : "",
|
|
3282
|
+
},
|
|
3283
|
+
];
|
|
3284
|
+
});
|
|
3285
|
+
}
|
|
3286
|
+
if (Array.isArray(source.enum)) {
|
|
3287
|
+
return source.enum.map((entry, index) => {
|
|
3288
|
+
const value = String(entry);
|
|
3289
|
+
return {
|
|
3290
|
+
label: value,
|
|
3291
|
+
value,
|
|
3292
|
+
description: index === 0 ? description : "",
|
|
3293
|
+
};
|
|
3294
|
+
});
|
|
3295
|
+
}
|
|
3296
|
+
if (type === "boolean") {
|
|
3297
|
+
return [
|
|
3298
|
+
{ label: "true", value: "true", description },
|
|
3299
|
+
{ label: "false", value: "false", description: "" },
|
|
3300
|
+
];
|
|
3301
|
+
}
|
|
3302
|
+
return [];
|
|
3303
|
+
}
|
|
3304
|
+
function isApprovalActionElicitation(schema, serverName, meta) {
|
|
3305
|
+
return (isEmptyObjectSchema(schema) &&
|
|
3306
|
+
!isToolSuggestionElicitation(serverName, meta));
|
|
3009
3307
|
}
|
|
3010
3308
|
function isEmptyObjectSchema(schema) {
|
|
3011
3309
|
if (!schema)
|
|
@@ -3018,8 +3316,9 @@ function isEmptyObjectSchema(schema) {
|
|
|
3018
3316
|
function isToolApprovalElicitation(meta) {
|
|
3019
3317
|
return meta?.codex_approval_kind === "mcp_tool_call";
|
|
3020
3318
|
}
|
|
3021
|
-
function isToolSuggestionElicitation(meta) {
|
|
3022
|
-
return
|
|
3319
|
+
function isToolSuggestionElicitation(serverName, meta) {
|
|
3320
|
+
return (serverName === "codex_apps" &&
|
|
3321
|
+
meta?.codex_approval_kind === "tool_suggestion");
|
|
3023
3322
|
}
|
|
3024
3323
|
function buildApprovalActionElicitationOptions(meta, isToolApproval) {
|
|
3025
3324
|
const persistModes = extractPersistModes(meta);
|
|
@@ -3108,6 +3407,32 @@ function asRecord(value) {
|
|
|
3108
3407
|
? value
|
|
3109
3408
|
: undefined;
|
|
3110
3409
|
}
|
|
3410
|
+
function stringValue(value) {
|
|
3411
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
3412
|
+
}
|
|
3413
|
+
function normalizeToolSuggestionApps(value) {
|
|
3414
|
+
if (!Array.isArray(value))
|
|
3415
|
+
return [];
|
|
3416
|
+
return value.flatMap((entry) => {
|
|
3417
|
+
const app = asRecord(entry);
|
|
3418
|
+
const id = stringValue(app?.id);
|
|
3419
|
+
const name = stringValue(app?.name);
|
|
3420
|
+
if (!id || !name)
|
|
3421
|
+
return [];
|
|
3422
|
+
const description = stringValue(app?.description);
|
|
3423
|
+
const installUrl = stringValue(app?.installUrl);
|
|
3424
|
+
const category = stringValue(app?.category);
|
|
3425
|
+
return [
|
|
3426
|
+
{
|
|
3427
|
+
id,
|
|
3428
|
+
name,
|
|
3429
|
+
...(description ? { description } : {}),
|
|
3430
|
+
...(installUrl ? { installUrl } : {}),
|
|
3431
|
+
...(category ? { category } : {}),
|
|
3432
|
+
},
|
|
3433
|
+
];
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3111
3436
|
function buildPlanUpdateToolUseInput(params) {
|
|
3112
3437
|
const stepsRaw = params.plan;
|
|
3113
3438
|
if (!Array.isArray(stepsRaw) || stepsRaw.length === 0)
|