@ccpocket/bridge 1.66.1 → 1.67.0
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 +384 -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/session.d.ts +2 -0
- package/dist/session.js +35 -1
- package/dist/session.js.map +1 -1
- package/dist/websocket.js +29 -3
- 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
|
+
});
|
|
1424
1551
|
break;
|
|
1552
|
+
}
|
|
1553
|
+
default: {
|
|
1554
|
+
console.warn(`[codex-process] unsupported server request: ${method}`);
|
|
1555
|
+
this.respondToServerRequestError(id, -32601, `Unsupported server request: ${method}`);
|
|
1556
|
+
break;
|
|
1557
|
+
}
|
|
1425
1558
|
}
|
|
1426
1559
|
}
|
|
1427
1560
|
handleNotification(method, params) {
|
|
@@ -1555,6 +1688,55 @@ export class CodexProcess extends EventEmitter {
|
|
|
1555
1688
|
this.handleServerRequestResolved(params);
|
|
1556
1689
|
break;
|
|
1557
1690
|
}
|
|
1691
|
+
case "warning": {
|
|
1692
|
+
const message = stringValue(params.message);
|
|
1693
|
+
if (message) {
|
|
1694
|
+
this.emitMessage({
|
|
1695
|
+
type: "error",
|
|
1696
|
+
errorCode: "codex_warning",
|
|
1697
|
+
message,
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
break;
|
|
1701
|
+
}
|
|
1702
|
+
case "guardianWarning": {
|
|
1703
|
+
const message = stringValue(params.message);
|
|
1704
|
+
if (!message)
|
|
1705
|
+
break;
|
|
1706
|
+
if (isInformationalGuardianApproval(message)) {
|
|
1707
|
+
console.debug("[codex-process] suppressed informational guardian approval notification");
|
|
1708
|
+
break;
|
|
1709
|
+
}
|
|
1710
|
+
this.emitMessage({
|
|
1711
|
+
type: "error",
|
|
1712
|
+
errorCode: "codex_warning",
|
|
1713
|
+
message,
|
|
1714
|
+
});
|
|
1715
|
+
break;
|
|
1716
|
+
}
|
|
1717
|
+
case "configWarning":
|
|
1718
|
+
case "deprecationNotice": {
|
|
1719
|
+
const summary = stringValue(params.summary);
|
|
1720
|
+
const details = stringValue(params.details);
|
|
1721
|
+
if (summary || details) {
|
|
1722
|
+
this.emitMessage({
|
|
1723
|
+
type: "error",
|
|
1724
|
+
errorCode: "codex_warning",
|
|
1725
|
+
message: [summary, details].filter(Boolean).join("\n"),
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
break;
|
|
1729
|
+
}
|
|
1730
|
+
case "error": {
|
|
1731
|
+
const error = asRecord(params.error);
|
|
1732
|
+
const message = stringValue(error?.message) ?? "Codex runtime error";
|
|
1733
|
+
this.emitMessage({
|
|
1734
|
+
type: "error",
|
|
1735
|
+
errorCode: params.willRetry ? "codex_warning" : "codex_runtime_error",
|
|
1736
|
+
message: params.willRetry ? `${message}\nCodex will retry.` : message,
|
|
1737
|
+
});
|
|
1738
|
+
break;
|
|
1739
|
+
}
|
|
1558
1740
|
default:
|
|
1559
1741
|
break;
|
|
1560
1742
|
}
|
|
@@ -1954,6 +2136,22 @@ export class CodexProcess extends EventEmitter {
|
|
|
1954
2136
|
this.lastPlanItemText = planText;
|
|
1955
2137
|
break;
|
|
1956
2138
|
}
|
|
2139
|
+
case "exitedreviewmode": {
|
|
2140
|
+
const text = typeof item.review === "string" ? item.review : "";
|
|
2141
|
+
if (!text)
|
|
2142
|
+
break;
|
|
2143
|
+
this.lastResultText = text;
|
|
2144
|
+
this.emitMessage({
|
|
2145
|
+
type: "assistant",
|
|
2146
|
+
message: {
|
|
2147
|
+
id: itemId,
|
|
2148
|
+
role: "assistant",
|
|
2149
|
+
content: [{ type: "text", text }],
|
|
2150
|
+
model: this.getMessageModel(),
|
|
2151
|
+
},
|
|
2152
|
+
});
|
|
2153
|
+
break;
|
|
2154
|
+
}
|
|
1957
2155
|
case "error": {
|
|
1958
2156
|
const message = typeof item.message === "string" ? item.message : "Codex item error";
|
|
1959
2157
|
this.emitMessage({ type: "error", message });
|
|
@@ -2039,6 +2237,16 @@ export class CodexProcess extends EventEmitter {
|
|
|
2039
2237
|
}
|
|
2040
2238
|
}
|
|
2041
2239
|
}
|
|
2240
|
+
respondToServerRequestError(id, code, message) {
|
|
2241
|
+
try {
|
|
2242
|
+
this.writeEnvelope({ id, error: { code, message } });
|
|
2243
|
+
}
|
|
2244
|
+
catch (err) {
|
|
2245
|
+
if (!this.stopped) {
|
|
2246
|
+
console.warn(`[codex-process] failed to reject server request: ${err instanceof Error ? err.message : String(err)}`);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2042
2250
|
writeEnvelope(envelope) {
|
|
2043
2251
|
if (!this.transport || !this.transport.isRunning) {
|
|
2044
2252
|
throw new Error("codex app-server is not running");
|
|
@@ -2751,12 +2959,15 @@ function parseResultObject(rawResult) {
|
|
|
2751
2959
|
return { byId: {}, byQuestion: {} };
|
|
2752
2960
|
}
|
|
2753
2961
|
}
|
|
2962
|
+
function isInformationalGuardianApproval(message) {
|
|
2963
|
+
const normalized = message.trim().replace(/\s+/g, " ").toLowerCase();
|
|
2964
|
+
return (normalized.startsWith("automatic approval review approved") &&
|
|
2965
|
+
/: auto-review returned a low[- ]risk allow decision\.?$/.test(normalized));
|
|
2966
|
+
}
|
|
2754
2967
|
function normalizeAnswerValues(value) {
|
|
2755
2968
|
if (typeof value === "string") {
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
.map((part) => part.trim())
|
|
2759
|
-
.filter((part) => part.length > 0);
|
|
2969
|
+
const normalized = value.trim();
|
|
2970
|
+
return normalized ? [normalized] : [];
|
|
2760
2971
|
}
|
|
2761
2972
|
if (Array.isArray(value)) {
|
|
2762
2973
|
return value
|
|
@@ -2777,6 +2988,13 @@ function normalizeAnswerValues(value) {
|
|
|
2777
2988
|
return normalized ? [normalized] : [];
|
|
2778
2989
|
}
|
|
2779
2990
|
function buildElicitationResponse(pending, rawResult) {
|
|
2991
|
+
if (pending.kind === "tool_suggestion") {
|
|
2992
|
+
return {
|
|
2993
|
+
action: parseElicitationAction(rawResult),
|
|
2994
|
+
content: null,
|
|
2995
|
+
_meta: null,
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2780
2998
|
if (pending.kind === "elicitation_url") {
|
|
2781
2999
|
const action = parseElicitationAction(rawResult);
|
|
2782
3000
|
return {
|
|
@@ -2790,23 +3008,20 @@ function buildElicitationResponse(pending, rawResult) {
|
|
|
2790
3008
|
}
|
|
2791
3009
|
const parsed = parseResultObject(rawResult);
|
|
2792
3010
|
const content = {};
|
|
3011
|
+
const schema = asRecord(pending.input.requestedSchema);
|
|
3012
|
+
const properties = asRecord(schema?.properties) ?? {};
|
|
2793
3013
|
for (const question of pending.questions) {
|
|
2794
3014
|
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;
|
|
3015
|
+
const value = coerceElicitationValue(candidate, asRecord(properties[question.id]));
|
|
3016
|
+
if (value !== undefined) {
|
|
3017
|
+
content[question.id] = value;
|
|
2801
3018
|
}
|
|
2802
3019
|
}
|
|
2803
3020
|
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;
|
|
3021
|
+
const questionId = pending.questions[0].id;
|
|
3022
|
+
const value = coerceElicitationValue(rawResult, asRecord(properties[questionId]));
|
|
3023
|
+
if (value !== undefined) {
|
|
3024
|
+
content[questionId] = value;
|
|
2810
3025
|
}
|
|
2811
3026
|
}
|
|
2812
3027
|
return {
|
|
@@ -2815,6 +3030,48 @@ function buildElicitationResponse(pending, rawResult) {
|
|
|
2815
3030
|
_meta: null,
|
|
2816
3031
|
};
|
|
2817
3032
|
}
|
|
3033
|
+
function coerceElicitationValue(value, field) {
|
|
3034
|
+
if (value == null)
|
|
3035
|
+
return undefined;
|
|
3036
|
+
const type = stringValue(field?.type) ?? "string";
|
|
3037
|
+
if (type === "array") {
|
|
3038
|
+
if (Array.isArray(value)) {
|
|
3039
|
+
const entries = value.map((entry) => String(entry));
|
|
3040
|
+
return entries.length > 0 ? entries : undefined;
|
|
3041
|
+
}
|
|
3042
|
+
if (typeof value === "string") {
|
|
3043
|
+
return value
|
|
3044
|
+
.split(",")
|
|
3045
|
+
.map((entry) => entry.trim())
|
|
3046
|
+
.filter(Boolean);
|
|
3047
|
+
}
|
|
3048
|
+
return [String(value)];
|
|
3049
|
+
}
|
|
3050
|
+
const scalar = Array.isArray(value) ? value[0] : value;
|
|
3051
|
+
if (scalar == null)
|
|
3052
|
+
return undefined;
|
|
3053
|
+
if (typeof scalar === "string" && scalar.trim().length === 0) {
|
|
3054
|
+
return undefined;
|
|
3055
|
+
}
|
|
3056
|
+
if (type === "boolean") {
|
|
3057
|
+
if (typeof scalar === "boolean")
|
|
3058
|
+
return scalar;
|
|
3059
|
+
if (String(scalar).toLowerCase() === "true")
|
|
3060
|
+
return true;
|
|
3061
|
+
if (String(scalar).toLowerCase() === "false")
|
|
3062
|
+
return false;
|
|
3063
|
+
return undefined;
|
|
3064
|
+
}
|
|
3065
|
+
if (type === "number" || type === "integer") {
|
|
3066
|
+
const number = typeof scalar === "number" ? scalar : Number(scalar);
|
|
3067
|
+
if (!Number.isFinite(number))
|
|
3068
|
+
return undefined;
|
|
3069
|
+
if (type === "integer" && !Number.isInteger(number))
|
|
3070
|
+
return undefined;
|
|
3071
|
+
return number;
|
|
3072
|
+
}
|
|
3073
|
+
return String(scalar);
|
|
3074
|
+
}
|
|
2818
3075
|
function buildApprovalElicitationResponse(pending, rawResult) {
|
|
2819
3076
|
const selection = resolveApprovalElicitationSelection(pending, rawResult);
|
|
2820
3077
|
const normalized = selection.trim().toLowerCase();
|
|
@@ -2908,7 +3165,32 @@ function createElicitationInput(params) {
|
|
|
2908
3165
|
}
|
|
2909
3166
|
const schema = asRecord(params.requestedSchema);
|
|
2910
3167
|
const elicitationMeta = asRecord(params._meta);
|
|
2911
|
-
if (
|
|
3168
|
+
if (isToolSuggestionElicitation(serverName, elicitationMeta)) {
|
|
3169
|
+
const toolName = stringValue(elicitationMeta?.tool_name) ?? "Tool";
|
|
3170
|
+
return {
|
|
3171
|
+
kind: "tool_suggestion",
|
|
3172
|
+
questions: [],
|
|
3173
|
+
input: {
|
|
3174
|
+
mode: "form",
|
|
3175
|
+
serverName,
|
|
3176
|
+
message,
|
|
3177
|
+
_meta: elicitationMeta ?? null,
|
|
3178
|
+
toolType: stringValue(elicitationMeta?.tool_type),
|
|
3179
|
+
suggestType: stringValue(elicitationMeta?.suggest_type),
|
|
3180
|
+
suggestReason: stringValue(elicitationMeta?.suggest_reason) ?? message,
|
|
3181
|
+
toolId: stringValue(elicitationMeta?.tool_id),
|
|
3182
|
+
toolName,
|
|
3183
|
+
installUrl: stringValue(elicitationMeta?.install_url),
|
|
3184
|
+
remotePluginId: stringValue(elicitationMeta?.remote_plugin_id),
|
|
3185
|
+
appConnectorIds: Array.isArray(elicitationMeta?.app_connector_ids)
|
|
3186
|
+
? elicitationMeta.app_connector_ids.filter((entry) => typeof entry === "string")
|
|
3187
|
+
: [],
|
|
3188
|
+
installState: "idle",
|
|
3189
|
+
appsNeedingAuth: [],
|
|
3190
|
+
},
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
if (isApprovalActionElicitation(schema, serverName, elicitationMeta)) {
|
|
2912
3194
|
const questionId = "approval";
|
|
2913
3195
|
const isToolApproval = isToolApprovalElicitation(elicitationMeta);
|
|
2914
3196
|
return {
|
|
@@ -2944,26 +3226,15 @@ function createElicitationInput(params) {
|
|
|
2944
3226
|
const field = value;
|
|
2945
3227
|
const title = typeof field.title === "string" ? field.title : key;
|
|
2946
3228
|
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
3229
|
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
|
-
: [];
|
|
3230
|
+
const options = buildElicitationFieldOptions(field, description);
|
|
2962
3231
|
return {
|
|
2963
3232
|
id: key,
|
|
2964
3233
|
question: requiredFields.has(key) ? `${title} (required)` : title,
|
|
2965
3234
|
header: serverName,
|
|
2966
3235
|
options,
|
|
3236
|
+
required: requiredFields.has(key),
|
|
3237
|
+
multiSelect: type === "array",
|
|
2967
3238
|
isOther: options.length === 0,
|
|
2968
3239
|
isSecret: false,
|
|
2969
3240
|
};
|
|
@@ -2976,6 +3247,8 @@ function createElicitationInput(params) {
|
|
|
2976
3247
|
question: message,
|
|
2977
3248
|
header: serverName,
|
|
2978
3249
|
options: [],
|
|
3250
|
+
required: true,
|
|
3251
|
+
multiSelect: false,
|
|
2979
3252
|
isOther: true,
|
|
2980
3253
|
isSecret: false,
|
|
2981
3254
|
},
|
|
@@ -2985,6 +3258,7 @@ function createElicitationInput(params) {
|
|
|
2985
3258
|
questions: normalizedQuestions.map((question) => ({
|
|
2986
3259
|
id: question.id,
|
|
2987
3260
|
question: question.question,
|
|
3261
|
+
required: question.required,
|
|
2988
3262
|
})),
|
|
2989
3263
|
input: {
|
|
2990
3264
|
mode: "form",
|
|
@@ -2997,15 +3271,58 @@ function createElicitationInput(params) {
|
|
|
2997
3271
|
header: question.header,
|
|
2998
3272
|
question: question.question,
|
|
2999
3273
|
options: question.options,
|
|
3000
|
-
|
|
3274
|
+
required: question.required,
|
|
3275
|
+
multiSelect: question.multiSelect,
|
|
3001
3276
|
isOther: question.isOther,
|
|
3002
3277
|
isSecret: question.isSecret,
|
|
3003
3278
|
})),
|
|
3004
3279
|
},
|
|
3005
3280
|
};
|
|
3006
3281
|
}
|
|
3007
|
-
function
|
|
3008
|
-
|
|
3282
|
+
function buildElicitationFieldOptions(field, description) {
|
|
3283
|
+
const type = stringValue(field.type);
|
|
3284
|
+
const source = type === "array" ? asRecord(field.items) ?? {} : field;
|
|
3285
|
+
const rawOptions = Array.isArray(source.oneOf)
|
|
3286
|
+
? source.oneOf
|
|
3287
|
+
: Array.isArray(source.anyOf)
|
|
3288
|
+
? source.anyOf
|
|
3289
|
+
: null;
|
|
3290
|
+
if (rawOptions) {
|
|
3291
|
+
return rawOptions.flatMap((entry, index) => {
|
|
3292
|
+
const option = asRecord(entry);
|
|
3293
|
+
const value = stringValue(option?.const);
|
|
3294
|
+
if (!value)
|
|
3295
|
+
return [];
|
|
3296
|
+
return [
|
|
3297
|
+
{
|
|
3298
|
+
label: stringValue(option?.title) ?? value,
|
|
3299
|
+
value,
|
|
3300
|
+
description: index === 0 ? description : "",
|
|
3301
|
+
},
|
|
3302
|
+
];
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
if (Array.isArray(source.enum)) {
|
|
3306
|
+
return source.enum.map((entry, index) => {
|
|
3307
|
+
const value = String(entry);
|
|
3308
|
+
return {
|
|
3309
|
+
label: value,
|
|
3310
|
+
value,
|
|
3311
|
+
description: index === 0 ? description : "",
|
|
3312
|
+
};
|
|
3313
|
+
});
|
|
3314
|
+
}
|
|
3315
|
+
if (type === "boolean") {
|
|
3316
|
+
return [
|
|
3317
|
+
{ label: "true", value: "true", description },
|
|
3318
|
+
{ label: "false", value: "false", description: "" },
|
|
3319
|
+
];
|
|
3320
|
+
}
|
|
3321
|
+
return [];
|
|
3322
|
+
}
|
|
3323
|
+
function isApprovalActionElicitation(schema, serverName, meta) {
|
|
3324
|
+
return (isEmptyObjectSchema(schema) &&
|
|
3325
|
+
!isToolSuggestionElicitation(serverName, meta));
|
|
3009
3326
|
}
|
|
3010
3327
|
function isEmptyObjectSchema(schema) {
|
|
3011
3328
|
if (!schema)
|
|
@@ -3018,8 +3335,9 @@ function isEmptyObjectSchema(schema) {
|
|
|
3018
3335
|
function isToolApprovalElicitation(meta) {
|
|
3019
3336
|
return meta?.codex_approval_kind === "mcp_tool_call";
|
|
3020
3337
|
}
|
|
3021
|
-
function isToolSuggestionElicitation(meta) {
|
|
3022
|
-
return
|
|
3338
|
+
function isToolSuggestionElicitation(serverName, meta) {
|
|
3339
|
+
return (serverName === "codex_apps" &&
|
|
3340
|
+
meta?.codex_approval_kind === "tool_suggestion");
|
|
3023
3341
|
}
|
|
3024
3342
|
function buildApprovalActionElicitationOptions(meta, isToolApproval) {
|
|
3025
3343
|
const persistModes = extractPersistModes(meta);
|
|
@@ -3108,6 +3426,32 @@ function asRecord(value) {
|
|
|
3108
3426
|
? value
|
|
3109
3427
|
: undefined;
|
|
3110
3428
|
}
|
|
3429
|
+
function stringValue(value) {
|
|
3430
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
3431
|
+
}
|
|
3432
|
+
function normalizeToolSuggestionApps(value) {
|
|
3433
|
+
if (!Array.isArray(value))
|
|
3434
|
+
return [];
|
|
3435
|
+
return value.flatMap((entry) => {
|
|
3436
|
+
const app = asRecord(entry);
|
|
3437
|
+
const id = stringValue(app?.id);
|
|
3438
|
+
const name = stringValue(app?.name);
|
|
3439
|
+
if (!id || !name)
|
|
3440
|
+
return [];
|
|
3441
|
+
const description = stringValue(app?.description);
|
|
3442
|
+
const installUrl = stringValue(app?.installUrl);
|
|
3443
|
+
const category = stringValue(app?.category);
|
|
3444
|
+
return [
|
|
3445
|
+
{
|
|
3446
|
+
id,
|
|
3447
|
+
name,
|
|
3448
|
+
...(description ? { description } : {}),
|
|
3449
|
+
...(installUrl ? { installUrl } : {}),
|
|
3450
|
+
...(category ? { category } : {}),
|
|
3451
|
+
},
|
|
3452
|
+
];
|
|
3453
|
+
});
|
|
3454
|
+
}
|
|
3111
3455
|
function buildPlanUpdateToolUseInput(params) {
|
|
3112
3456
|
const stepsRaw = params.plan;
|
|
3113
3457
|
if (!Array.isArray(stepsRaw) || stepsRaw.length === 0)
|