@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.
- package/dist/client-DNN2uyJW.js +642 -0
- package/dist/client-factory-Bu9OClHJ.js +9 -0
- package/dist/command-formatters-BpPOTePl.js +520 -0
- package/dist/command-handlers-BBs7Vws9.js +1533 -0
- package/dist/compact-CDboBy7o.js +329 -0
- package/dist/computer-use-DCZB46Sw.js +367 -0
- package/dist/config-CLMSw0p2.js +510 -0
- package/dist/doctor-contract-api.js +53 -0
- package/dist/harness.js +51 -0
- package/dist/index.js +1171 -0
- package/dist/media-understanding-provider.js +335 -0
- package/dist/models-jLA2SIvd.js +110 -0
- package/dist/node-cli-sessions-BLRDs_US.js +1216 -0
- package/dist/plugin-activation-CEy_oYpx.js +452 -0
- package/dist/prompt-overlay.js +12 -0
- package/dist/protocol-C9UWI98H.js +9 -0
- package/dist/protocol-validators-BGBspNmF.js +5988 -0
- package/dist/provider-catalog.js +84 -0
- package/dist/provider-discovery.js +33 -0
- package/dist/provider.js +150 -0
- package/dist/rate-limit-cache-9LxQdE0K.js +24 -0
- package/dist/request-DbSPeTcV.js +89 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/run-attempt-BoEwzQCv.js +5463 -0
- package/dist/session-binding-e2GFp9VH.js +222 -0
- package/dist/shared-client-D7Vy0glq.js +631 -0
- package/dist/side-question-BDLuEzFP.js +668 -0
- package/dist/test-api.js +49 -0
- package/dist/thread-lifecycle-Clo0EHMk.js +1565 -0
- package/dist/vision-tools-Cofrv35p.js +1379 -0
- package/package.json +16 -1
- package/doctor-contract-api.ts +0 -68
- package/harness.ts +0 -72
- package/index.ts +0 -124
- package/media-understanding-provider.ts +0 -521
- package/prompt-overlay.ts +0 -21
- package/provider-catalog.ts +0 -83
- package/provider-discovery.ts +0 -45
- package/provider.ts +0 -243
- package/src/app-server/app-inventory-cache.ts +0 -324
- package/src/app-server/approval-bridge.ts +0 -1211
- package/src/app-server/auth-bridge.ts +0 -614
- package/src/app-server/capabilities.ts +0 -27
- package/src/app-server/client-factory.ts +0 -24
- package/src/app-server/client.ts +0 -715
- package/src/app-server/compact.ts +0 -512
- package/src/app-server/computer-use.ts +0 -683
- package/src/app-server/config.ts +0 -1038
- package/src/app-server/context-engine-projection.ts +0 -403
- package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
- package/src/app-server/dynamic-tool-profile.ts +0 -70
- package/src/app-server/dynamic-tools.ts +0 -623
- package/src/app-server/elicitation-bridge.ts +0 -783
- package/src/app-server/event-projector.ts +0 -2065
- package/src/app-server/image-payload-sanitizer.ts +0 -167
- package/src/app-server/local-runtime-attribution.ts +0 -39
- package/src/app-server/managed-binary.ts +0 -193
- package/src/app-server/models.ts +0 -172
- package/src/app-server/native-hook-relay.ts +0 -150
- package/src/app-server/native-subagent-task-mirror.ts +0 -497
- package/src/app-server/plugin-activation.ts +0 -283
- package/src/app-server/plugin-app-cache-key.ts +0 -74
- package/src/app-server/plugin-approval-roundtrip.ts +0 -122
- package/src/app-server/plugin-inventory.ts +0 -357
- package/src/app-server/plugin-thread-config.ts +0 -455
- package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
- package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
- package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
- package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
- package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
- package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
- package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
- package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
- package/src/app-server/protocol-validators.ts +0 -203
- package/src/app-server/protocol.ts +0 -520
- package/src/app-server/rate-limit-cache.ts +0 -48
- package/src/app-server/rate-limits.ts +0 -583
- package/src/app-server/request.ts +0 -73
- package/src/app-server/run-attempt.ts +0 -4862
- package/src/app-server/session-binding.ts +0 -398
- package/src/app-server/session-history.ts +0 -44
- package/src/app-server/shared-client.ts +0 -289
- package/src/app-server/side-question.ts +0 -1009
- package/src/app-server/test-support.ts +0 -48
- package/src/app-server/thread-lifecycle.ts +0 -959
- package/src/app-server/timeout.ts +0 -9
- package/src/app-server/tool-progress-normalization.ts +0 -77
- package/src/app-server/trajectory.ts +0 -368
- package/src/app-server/transcript-mirror.ts +0 -208
- package/src/app-server/transport-stdio.ts +0 -107
- package/src/app-server/transport-websocket.ts +0 -90
- package/src/app-server/transport.ts +0 -117
- package/src/app-server/user-input-bridge.ts +0 -316
- package/src/app-server/version.ts +0 -4
- package/src/app-server/vision-tools.ts +0 -12
- package/src/command-account.ts +0 -544
- package/src/command-formatters.ts +0 -426
- package/src/command-handlers.ts +0 -2021
- package/src/command-plugins-management.ts +0 -137
- package/src/command-rpc.ts +0 -142
- package/src/commands.ts +0 -65
- package/src/conversation-binding-data.ts +0 -124
- package/src/conversation-binding.ts +0 -561
- package/src/conversation-control.ts +0 -303
- package/src/conversation-turn-collector.ts +0 -186
- package/src/conversation-turn-input.ts +0 -106
- package/src/migration/apply.ts +0 -501
- package/src/migration/helpers.ts +0 -55
- package/src/migration/plan.ts +0 -461
- package/src/migration/provider.ts +0 -41
- package/src/migration/source.ts +0 -643
- package/src/migration/targets.ts +0 -25
- package/src/node-cli-sessions.ts +0 -711
- package/test-api.ts +0 -95
- package/tsconfig.json +0 -16
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import {
|
|
3
|
-
materializeWindowsSpawnProgram,
|
|
4
|
-
resolveWindowsSpawnProgram,
|
|
5
|
-
} from "autobot/plugin-sdk/windows-spawn";
|
|
6
|
-
import type { CodexAppServerStartOptions } from "./config.js";
|
|
7
|
-
import type { CodexAppServerTransport } from "./transport.js";
|
|
8
|
-
|
|
9
|
-
const UNSAFE_ENVIRONMENT_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
10
|
-
|
|
11
|
-
type CodexAppServerSpawnRuntime = {
|
|
12
|
-
platform: NodeJS.Platform;
|
|
13
|
-
env: NodeJS.ProcessEnv;
|
|
14
|
-
execPath: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const DEFAULT_SPAWN_RUNTIME: CodexAppServerSpawnRuntime = {
|
|
18
|
-
platform: process.platform,
|
|
19
|
-
env: process.env,
|
|
20
|
-
execPath: process.execPath,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export function resolveCodexAppServerSpawnInvocation(
|
|
24
|
-
options: CodexAppServerStartOptions,
|
|
25
|
-
runtime: CodexAppServerSpawnRuntime = DEFAULT_SPAWN_RUNTIME,
|
|
26
|
-
): { command: string; args: string[]; shell?: boolean; windowsHide?: boolean } {
|
|
27
|
-
if (options.commandSource === "managed") {
|
|
28
|
-
throw new Error("Managed Codex app-server start options must be resolved before spawn.");
|
|
29
|
-
}
|
|
30
|
-
const program = resolveWindowsSpawnProgram({
|
|
31
|
-
command: options.command,
|
|
32
|
-
platform: runtime.platform,
|
|
33
|
-
env: runtime.env,
|
|
34
|
-
execPath: runtime.execPath,
|
|
35
|
-
packageName: "@openai/codex",
|
|
36
|
-
});
|
|
37
|
-
const resolved = materializeWindowsSpawnProgram(program, options.args);
|
|
38
|
-
return {
|
|
39
|
-
command: resolved.command,
|
|
40
|
-
args: resolved.argv,
|
|
41
|
-
shell: resolved.shell,
|
|
42
|
-
windowsHide: resolved.windowsHide,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function resolveCodexAppServerSpawnEnv(
|
|
47
|
-
options: Pick<CodexAppServerStartOptions, "env" | "clearEnv">,
|
|
48
|
-
baseEnv: NodeJS.ProcessEnv = process.env,
|
|
49
|
-
platform: NodeJS.Platform = process.platform,
|
|
50
|
-
): NodeJS.ProcessEnv {
|
|
51
|
-
const env = Object.create(null) as NodeJS.ProcessEnv;
|
|
52
|
-
copySafeEnvironmentEntries(env, baseEnv);
|
|
53
|
-
copySafeEnvironmentEntries(env, options.env ?? {});
|
|
54
|
-
const keysToClear = normalizedEnvironmentKeys(options.clearEnv ?? []);
|
|
55
|
-
if (platform === "win32") {
|
|
56
|
-
const lowerCaseKeysToClear = new Set(keysToClear.map((key) => key.toLowerCase()));
|
|
57
|
-
for (const candidate of Object.keys(env)) {
|
|
58
|
-
if (lowerCaseKeysToClear.has(candidate.toLowerCase())) {
|
|
59
|
-
delete env[candidate];
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
} else {
|
|
63
|
-
for (const key of keysToClear) {
|
|
64
|
-
delete env[key];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return env;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function normalizedEnvironmentKeys(rawKeys: readonly string[]): string[] {
|
|
71
|
-
const keys: string[] = [];
|
|
72
|
-
for (const rawKey of rawKeys) {
|
|
73
|
-
const key = rawKey.trim();
|
|
74
|
-
if (key.length > 0) {
|
|
75
|
-
keys.push(key);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return keys;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function copySafeEnvironmentEntries(
|
|
82
|
-
target: NodeJS.ProcessEnv,
|
|
83
|
-
source: NodeJS.ProcessEnv | Record<string, string | undefined>,
|
|
84
|
-
): void {
|
|
85
|
-
for (const [key, value] of Object.entries(source)) {
|
|
86
|
-
if (UNSAFE_ENVIRONMENT_KEYS.has(key)) {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
target[key] = value;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function createStdioTransport(options: CodexAppServerStartOptions): CodexAppServerTransport {
|
|
94
|
-
const env = resolveCodexAppServerSpawnEnv(options);
|
|
95
|
-
const invocation = resolveCodexAppServerSpawnInvocation(options, {
|
|
96
|
-
platform: process.platform,
|
|
97
|
-
env,
|
|
98
|
-
execPath: process.execPath,
|
|
99
|
-
});
|
|
100
|
-
return spawn(invocation.command, invocation.args, {
|
|
101
|
-
env,
|
|
102
|
-
detached: process.platform !== "win32",
|
|
103
|
-
shell: invocation.shell,
|
|
104
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
105
|
-
windowsHide: invocation.windowsHide,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
import { PassThrough, Writable } from "node:stream";
|
|
3
|
-
import WebSocket, { type RawData } from "ws";
|
|
4
|
-
import type { CodexAppServerStartOptions } from "./config.js";
|
|
5
|
-
import type { CodexAppServerTransport } from "./transport.js";
|
|
6
|
-
|
|
7
|
-
export function createWebSocketTransport(
|
|
8
|
-
options: CodexAppServerStartOptions,
|
|
9
|
-
): CodexAppServerTransport {
|
|
10
|
-
if (!options.url) {
|
|
11
|
-
throw new Error(
|
|
12
|
-
"codex app-server websocket transport requires plugins.entries.codex.config.appServer.url",
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
const events = new EventEmitter();
|
|
16
|
-
const stdout = new PassThrough();
|
|
17
|
-
const stderr = new PassThrough();
|
|
18
|
-
const headers = {
|
|
19
|
-
...options.headers,
|
|
20
|
-
...(options.authToken ? { Authorization: `Bearer ${options.authToken}` } : {}),
|
|
21
|
-
};
|
|
22
|
-
const socket = new WebSocket(options.url, { headers });
|
|
23
|
-
const pendingFrames: string[] = [];
|
|
24
|
-
let killed = false;
|
|
25
|
-
|
|
26
|
-
const sendFrame = (frame: string) => {
|
|
27
|
-
const trimmed = frame.trim();
|
|
28
|
-
if (!trimmed) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
32
|
-
socket.send(trimmed);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
pendingFrames.push(trimmed);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// `initialize` can be written before the WebSocket open event fires. Buffer
|
|
39
|
-
// whole JSON-RPC frames so stdio and websocket transports share call timing.
|
|
40
|
-
socket.once("open", () => {
|
|
41
|
-
for (const frame of pendingFrames.splice(0)) {
|
|
42
|
-
socket.send(frame);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
socket.once("error", (error) => events.emit("error", error));
|
|
46
|
-
socket.once("close", (code, reason) => {
|
|
47
|
-
killed = true;
|
|
48
|
-
events.emit("exit", code, reason.toString("utf8"));
|
|
49
|
-
});
|
|
50
|
-
socket.on("message", (data) => {
|
|
51
|
-
const text = websocketFrameToText(data);
|
|
52
|
-
stdout.write(text.endsWith("\n") ? text : `${text}\n`);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const stdin = new Writable({
|
|
56
|
-
write(chunk, _encoding, callback) {
|
|
57
|
-
for (const frame of chunk.toString("utf8").split("\n")) {
|
|
58
|
-
sendFrame(frame);
|
|
59
|
-
}
|
|
60
|
-
callback();
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
stdin,
|
|
66
|
-
stdout,
|
|
67
|
-
stderr,
|
|
68
|
-
get killed() {
|
|
69
|
-
return killed;
|
|
70
|
-
},
|
|
71
|
-
kill: () => {
|
|
72
|
-
killed = true;
|
|
73
|
-
socket.close();
|
|
74
|
-
},
|
|
75
|
-
once: (event, listener) => events.once(event, listener),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function websocketFrameToText(data: RawData): string {
|
|
80
|
-
if (typeof data === "string") {
|
|
81
|
-
return data;
|
|
82
|
-
}
|
|
83
|
-
if (Buffer.isBuffer(data)) {
|
|
84
|
-
return data.toString("utf8");
|
|
85
|
-
}
|
|
86
|
-
if (Array.isArray(data)) {
|
|
87
|
-
return Buffer.concat(data).toString("utf8");
|
|
88
|
-
}
|
|
89
|
-
return Buffer.from(data).toString("utf8");
|
|
90
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
export type CodexAppServerTransport = {
|
|
2
|
-
stdin: {
|
|
3
|
-
write: (data: string, callback?: (error?: Error | null) => void) => unknown;
|
|
4
|
-
end?: () => unknown;
|
|
5
|
-
destroy?: () => unknown;
|
|
6
|
-
unref?: () => unknown;
|
|
7
|
-
on?: (event: "error", listener: (error: Error) => void) => unknown;
|
|
8
|
-
};
|
|
9
|
-
stdout: NodeJS.ReadableStream & {
|
|
10
|
-
destroy?: () => unknown;
|
|
11
|
-
unref?: () => unknown;
|
|
12
|
-
};
|
|
13
|
-
stderr: NodeJS.ReadableStream & {
|
|
14
|
-
destroy?: () => unknown;
|
|
15
|
-
unref?: () => unknown;
|
|
16
|
-
};
|
|
17
|
-
pid?: number;
|
|
18
|
-
exitCode?: number | null;
|
|
19
|
-
signalCode?: string | null;
|
|
20
|
-
killed?: boolean;
|
|
21
|
-
kill?: (signal?: NodeJS.Signals) => unknown;
|
|
22
|
-
unref?: () => unknown;
|
|
23
|
-
once: (event: string, listener: (...args: unknown[]) => void) => unknown;
|
|
24
|
-
off?: (event: string, listener: (...args: unknown[]) => void) => unknown;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export function closeCodexAppServerTransport(
|
|
28
|
-
child: CodexAppServerTransport,
|
|
29
|
-
options: { forceKillDelayMs?: number } = {},
|
|
30
|
-
): void {
|
|
31
|
-
child.stdin.end?.();
|
|
32
|
-
child.stdin.destroy?.();
|
|
33
|
-
const forceKillDelayMs = options.forceKillDelayMs ?? 1_000;
|
|
34
|
-
const forceKill = setTimeout(
|
|
35
|
-
() => {
|
|
36
|
-
if (hasCodexAppServerTransportExited(child)) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
signalCodexAppServerTransport(child, "SIGKILL");
|
|
40
|
-
},
|
|
41
|
-
Math.max(1, forceKillDelayMs),
|
|
42
|
-
);
|
|
43
|
-
forceKill.unref?.();
|
|
44
|
-
child.once("exit", () => {
|
|
45
|
-
clearTimeout(forceKill);
|
|
46
|
-
child.stdout.destroy?.();
|
|
47
|
-
child.stderr.destroy?.();
|
|
48
|
-
});
|
|
49
|
-
child.unref?.();
|
|
50
|
-
child.stdout.unref?.();
|
|
51
|
-
child.stderr.unref?.();
|
|
52
|
-
child.stdin.unref?.();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function closeCodexAppServerTransportAndWait(
|
|
56
|
-
child: CodexAppServerTransport,
|
|
57
|
-
options: { exitTimeoutMs?: number; forceKillDelayMs?: number } = {},
|
|
58
|
-
): Promise<boolean> {
|
|
59
|
-
if (!hasCodexAppServerTransportExited(child)) {
|
|
60
|
-
closeCodexAppServerTransport(child, options);
|
|
61
|
-
}
|
|
62
|
-
return await waitForCodexAppServerTransportExit(child, options.exitTimeoutMs ?? 2_000);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function hasCodexAppServerTransportExited(child: CodexAppServerTransport): boolean {
|
|
66
|
-
return child.exitCode !== null && child.exitCode !== undefined
|
|
67
|
-
? true
|
|
68
|
-
: child.signalCode !== null && child.signalCode !== undefined;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function waitForCodexAppServerTransportExit(
|
|
72
|
-
child: CodexAppServerTransport,
|
|
73
|
-
timeoutMs: number,
|
|
74
|
-
): Promise<boolean> {
|
|
75
|
-
if (hasCodexAppServerTransportExited(child)) {
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
return await new Promise<boolean>((resolve) => {
|
|
79
|
-
let settled = false;
|
|
80
|
-
const onExit = () => {
|
|
81
|
-
if (settled) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
settled = true;
|
|
85
|
-
clearTimeout(timeout);
|
|
86
|
-
resolve(true);
|
|
87
|
-
};
|
|
88
|
-
const timeout = setTimeout(
|
|
89
|
-
() => {
|
|
90
|
-
if (settled) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
settled = true;
|
|
94
|
-
child.off?.("exit", onExit);
|
|
95
|
-
resolve(false);
|
|
96
|
-
},
|
|
97
|
-
Math.max(1, timeoutMs),
|
|
98
|
-
);
|
|
99
|
-
child.once("exit", onExit);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function signalCodexAppServerTransport(
|
|
104
|
-
child: CodexAppServerTransport,
|
|
105
|
-
signal: NodeJS.Signals,
|
|
106
|
-
): void {
|
|
107
|
-
if (child.pid && process.platform !== "win32") {
|
|
108
|
-
try {
|
|
109
|
-
process.kill(-child.pid, signal);
|
|
110
|
-
return;
|
|
111
|
-
} catch {
|
|
112
|
-
// Fall back to the child handle. The process may already be gone or not
|
|
113
|
-
// be a process-group leader on older call sites.
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
child.kill?.(signal);
|
|
117
|
-
}
|
|
@@ -1,316 +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
|
-
isJsonObject,
|
|
8
|
-
type CodexServerNotification,
|
|
9
|
-
type JsonObject,
|
|
10
|
-
type JsonValue,
|
|
11
|
-
} from "./protocol.js";
|
|
12
|
-
|
|
13
|
-
type PendingUserInput = {
|
|
14
|
-
requestId: number | string;
|
|
15
|
-
threadId: string;
|
|
16
|
-
turnId: string;
|
|
17
|
-
itemId: string;
|
|
18
|
-
questions: UserInputQuestion[];
|
|
19
|
-
resolve: (value: JsonValue) => void;
|
|
20
|
-
cleanup: () => void;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
type UserInputQuestion = {
|
|
24
|
-
id: string;
|
|
25
|
-
header: string;
|
|
26
|
-
question: string;
|
|
27
|
-
isOther: boolean;
|
|
28
|
-
isSecret: boolean;
|
|
29
|
-
options: UserInputOption[] | null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type UserInputOption = {
|
|
33
|
-
label: string;
|
|
34
|
-
description: string;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
type CodexUserInputBridge = {
|
|
38
|
-
handleRequest: (request: {
|
|
39
|
-
id: number | string;
|
|
40
|
-
params?: JsonValue;
|
|
41
|
-
}) => Promise<JsonValue | undefined>;
|
|
42
|
-
handleQueuedMessage: (text: string) => boolean;
|
|
43
|
-
handleNotification: (notification: CodexServerNotification) => void;
|
|
44
|
-
cancelPending: () => void;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export function createCodexUserInputBridge(params: {
|
|
48
|
-
paramsForRun: EmbeddedRunAttemptParams;
|
|
49
|
-
threadId: string;
|
|
50
|
-
turnId: string;
|
|
51
|
-
signal?: AbortSignal;
|
|
52
|
-
}): CodexUserInputBridge {
|
|
53
|
-
let pending: PendingUserInput | undefined;
|
|
54
|
-
|
|
55
|
-
const resolvePending = (value: JsonValue) => {
|
|
56
|
-
const current = pending;
|
|
57
|
-
if (!current) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
pending = undefined;
|
|
61
|
-
current.cleanup();
|
|
62
|
-
current.resolve(value);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
async handleRequest(request) {
|
|
67
|
-
const requestParams = readUserInputParams(request.params);
|
|
68
|
-
if (!requestParams) {
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
|
-
if (requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) {
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
if (requestParams.questions.length === 0) {
|
|
75
|
-
return emptyUserInputResponse();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
resolvePending(emptyUserInputResponse());
|
|
79
|
-
|
|
80
|
-
return new Promise<JsonValue>((resolve) => {
|
|
81
|
-
const abortListener = () => resolvePending(emptyUserInputResponse());
|
|
82
|
-
const cleanup = () => params.signal?.removeEventListener("abort", abortListener);
|
|
83
|
-
pending = {
|
|
84
|
-
requestId: request.id,
|
|
85
|
-
threadId: requestParams.threadId,
|
|
86
|
-
turnId: requestParams.turnId,
|
|
87
|
-
itemId: requestParams.itemId,
|
|
88
|
-
questions: requestParams.questions,
|
|
89
|
-
resolve,
|
|
90
|
-
cleanup,
|
|
91
|
-
};
|
|
92
|
-
params.signal?.addEventListener("abort", abortListener, { once: true });
|
|
93
|
-
if (params.signal?.aborted) {
|
|
94
|
-
resolvePending(emptyUserInputResponse());
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
void deliverUserInputPrompt(params.paramsForRun, requestParams.questions).catch((error) => {
|
|
98
|
-
embeddedAgentLog.warn("failed to deliver codex user input prompt", { error });
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
},
|
|
102
|
-
handleQueuedMessage(text) {
|
|
103
|
-
const current = pending;
|
|
104
|
-
if (!current) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
resolvePending(buildUserInputResponse(current.questions, text));
|
|
108
|
-
return true;
|
|
109
|
-
},
|
|
110
|
-
handleNotification(notification) {
|
|
111
|
-
if (notification.method !== "serverRequest/resolved" || !pending) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const notificationParams = isJsonObject(notification.params)
|
|
115
|
-
? notification.params
|
|
116
|
-
: undefined;
|
|
117
|
-
const requestId = notificationParams ? readRequestId(notificationParams) : undefined;
|
|
118
|
-
if (
|
|
119
|
-
notificationParams &&
|
|
120
|
-
readString(notificationParams, "threadId") === pending.threadId &&
|
|
121
|
-
requestId !== undefined &&
|
|
122
|
-
String(requestId) === String(pending.requestId)
|
|
123
|
-
) {
|
|
124
|
-
resolvePending(emptyUserInputResponse());
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
cancelPending() {
|
|
128
|
-
resolvePending(emptyUserInputResponse());
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function readUserInputParams(value: JsonValue | undefined):
|
|
134
|
-
| {
|
|
135
|
-
threadId: string;
|
|
136
|
-
turnId: string;
|
|
137
|
-
itemId: string;
|
|
138
|
-
questions: UserInputQuestion[];
|
|
139
|
-
}
|
|
140
|
-
| undefined {
|
|
141
|
-
if (!isJsonObject(value)) {
|
|
142
|
-
return undefined;
|
|
143
|
-
}
|
|
144
|
-
const threadId = readString(value, "threadId");
|
|
145
|
-
const turnId = readString(value, "turnId");
|
|
146
|
-
const itemId = readString(value, "itemId");
|
|
147
|
-
const questionsRaw = value.questions;
|
|
148
|
-
if (!threadId || !turnId || !itemId || !Array.isArray(questionsRaw)) {
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
const questions = questionsRaw
|
|
152
|
-
.map(readQuestion)
|
|
153
|
-
.filter((question): question is UserInputQuestion => Boolean(question));
|
|
154
|
-
return { threadId, turnId, itemId, questions };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function readQuestion(value: JsonValue): UserInputQuestion | undefined {
|
|
158
|
-
if (!isJsonObject(value)) {
|
|
159
|
-
return undefined;
|
|
160
|
-
}
|
|
161
|
-
const id = readString(value, "id");
|
|
162
|
-
const header = readString(value, "header");
|
|
163
|
-
const question = readString(value, "question");
|
|
164
|
-
if (!id || !header || !question) {
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
return {
|
|
168
|
-
id,
|
|
169
|
-
header,
|
|
170
|
-
question,
|
|
171
|
-
isOther: value.isOther === true,
|
|
172
|
-
isSecret: value.isSecret === true,
|
|
173
|
-
options: readOptions(value.options),
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function readOptions(value: JsonValue | undefined): UserInputOption[] | null {
|
|
178
|
-
if (!Array.isArray(value)) {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
const options = value
|
|
182
|
-
.map(readOption)
|
|
183
|
-
.filter((option): option is UserInputOption => Boolean(option));
|
|
184
|
-
return options.length > 0 ? options : null;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function readOption(value: JsonValue): UserInputOption | undefined {
|
|
188
|
-
if (!isJsonObject(value)) {
|
|
189
|
-
return undefined;
|
|
190
|
-
}
|
|
191
|
-
const label = readString(value, "label");
|
|
192
|
-
const description = readString(value, "description") ?? "";
|
|
193
|
-
return label ? { label, description } : undefined;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async function deliverUserInputPrompt(
|
|
197
|
-
params: EmbeddedRunAttemptParams,
|
|
198
|
-
questions: UserInputQuestion[],
|
|
199
|
-
): Promise<void> {
|
|
200
|
-
const text = formatUserInputPrompt(questions);
|
|
201
|
-
if (params.onBlockReply) {
|
|
202
|
-
await params.onBlockReply({ text });
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
await params.onPartialReply?.({ text });
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function formatUserInputPrompt(questions: UserInputQuestion[]): string {
|
|
209
|
-
const lines = ["Codex needs input:"];
|
|
210
|
-
questions.forEach((question, index) => {
|
|
211
|
-
if (questions.length > 1) {
|
|
212
|
-
lines.push(
|
|
213
|
-
"",
|
|
214
|
-
`${index + 1}. ${formatCodexDisplayText(question.header)}`,
|
|
215
|
-
formatCodexDisplayText(question.question),
|
|
216
|
-
);
|
|
217
|
-
} else {
|
|
218
|
-
lines.push(
|
|
219
|
-
"",
|
|
220
|
-
formatCodexDisplayText(question.header),
|
|
221
|
-
formatCodexDisplayText(question.question),
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
if (question.isSecret) {
|
|
225
|
-
lines.push("This channel may show your reply to other participants.");
|
|
226
|
-
}
|
|
227
|
-
question.options?.forEach((option, optionIndex) => {
|
|
228
|
-
lines.push(
|
|
229
|
-
`${optionIndex + 1}. ${formatCodexDisplayText(option.label)}${
|
|
230
|
-
option.description ? ` - ${formatCodexDisplayText(option.description)}` : ""
|
|
231
|
-
}`,
|
|
232
|
-
);
|
|
233
|
-
});
|
|
234
|
-
if (question.isOther) {
|
|
235
|
-
lines.push("Other: reply with your own answer.");
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
return lines.join("\n");
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function buildUserInputResponse(questions: UserInputQuestion[], inputText: string): JsonObject {
|
|
242
|
-
const answers: JsonObject = {};
|
|
243
|
-
if (questions.length === 1) {
|
|
244
|
-
const question = questions[0];
|
|
245
|
-
if (question) {
|
|
246
|
-
const answer = normalizeAnswer(inputText, question);
|
|
247
|
-
answers[question.id] = { answers: answer ? [answer] : [] };
|
|
248
|
-
}
|
|
249
|
-
return { answers };
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const keyed = parseKeyedAnswers(inputText);
|
|
253
|
-
const fallbackLines = inputText
|
|
254
|
-
.split(/\r?\n/)
|
|
255
|
-
.map((line) => line.trim())
|
|
256
|
-
.filter(Boolean);
|
|
257
|
-
questions.forEach((question, index) => {
|
|
258
|
-
const key =
|
|
259
|
-
keyed.get(question.id.toLowerCase()) ??
|
|
260
|
-
keyed.get(question.header.toLowerCase()) ??
|
|
261
|
-
keyed.get(question.question.toLowerCase()) ??
|
|
262
|
-
keyed.get(String(index + 1));
|
|
263
|
-
const answer = key ?? fallbackLines[index] ?? "";
|
|
264
|
-
const normalized = answer ? normalizeAnswer(answer, question) : undefined;
|
|
265
|
-
answers[question.id] = { answers: normalized ? [normalized] : [] };
|
|
266
|
-
});
|
|
267
|
-
return { answers };
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function normalizeAnswer(answer: string, question: UserInputQuestion): string | undefined {
|
|
271
|
-
const trimmed = answer.trim();
|
|
272
|
-
const options = question.options ?? [];
|
|
273
|
-
const optionIndex = /^\d+$/.test(trimmed) ? Number(trimmed) - 1 : -1;
|
|
274
|
-
const indexed = optionIndex >= 0 ? options[optionIndex] : undefined;
|
|
275
|
-
if (indexed) {
|
|
276
|
-
return indexed.label;
|
|
277
|
-
}
|
|
278
|
-
const exact = options.find((option) => option.label.toLowerCase() === trimmed.toLowerCase());
|
|
279
|
-
if (exact) {
|
|
280
|
-
return exact.label;
|
|
281
|
-
}
|
|
282
|
-
if (options.length > 0 && !question.isOther) {
|
|
283
|
-
return undefined;
|
|
284
|
-
}
|
|
285
|
-
return trimmed || undefined;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function parseKeyedAnswers(inputText: string): Map<string, string> {
|
|
289
|
-
const answers = new Map<string, string>();
|
|
290
|
-
for (const line of inputText.split(/\r?\n/)) {
|
|
291
|
-
const match = line.match(/^\s*([^:=-]+?)\s*[:=-]\s*(.+?)\s*$/);
|
|
292
|
-
if (!match) {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
const key = match[1]?.trim().toLowerCase();
|
|
296
|
-
const value = match[2]?.trim();
|
|
297
|
-
if (key && value) {
|
|
298
|
-
answers.set(key, value);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return answers;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function emptyUserInputResponse(): JsonObject {
|
|
305
|
-
return { answers: {} };
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function readString(record: JsonObject, key: string): string | undefined {
|
|
309
|
-
const value = record[key];
|
|
310
|
-
return typeof value === "string" ? value : undefined;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function readRequestId(record: JsonObject): string | number | undefined {
|
|
314
|
-
const value = record.requestId;
|
|
315
|
-
return typeof value === "string" || typeof value === "number" ? value : undefined;
|
|
316
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export function filterToolsForVisionInputs<T extends { name?: string }>(
|
|
2
|
-
tools: T[],
|
|
3
|
-
params: {
|
|
4
|
-
modelHasVision: boolean;
|
|
5
|
-
hasInboundImages: boolean;
|
|
6
|
-
},
|
|
7
|
-
): T[] {
|
|
8
|
-
if (!params.modelHasVision || !params.hasInboundImages) {
|
|
9
|
-
return tools;
|
|
10
|
-
}
|
|
11
|
-
return tools.filter((tool) => tool.name !== "image");
|
|
12
|
-
}
|