@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk 0.1.14 → 0.1.16
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-input-builder.d.ts +5 -0
- package/dist/codex-input-builder.js +43 -0
- package/dist/codex-model-provider.d.ts +2 -1
- package/dist/codex-model-provider.js +8 -0
- package/dist/codex-openai-responses-bridge-request.d.ts +11 -0
- package/dist/codex-openai-responses-bridge-request.js +334 -0
- package/dist/codex-openai-responses-bridge-shared.d.ts +47 -0
- package/dist/codex-openai-responses-bridge-shared.js +50 -0
- package/dist/codex-openai-responses-bridge-stream.d.ts +22 -0
- package/dist/codex-openai-responses-bridge-stream.js +312 -0
- package/dist/codex-openai-responses-bridge.d.ts +5 -0
- package/dist/codex-openai-responses-bridge.js +140 -0
- package/dist/codex-responses-capability.d.ts +12 -0
- package/dist/codex-responses-capability.js +109 -0
- package/dist/codex-session-type.d.ts +20 -0
- package/dist/codex-session-type.js +57 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +129 -100
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,20 +1,44 @@
|
|
|
1
1
|
import {
|
|
2
2
|
findProviderByModel,
|
|
3
3
|
findProviderByName,
|
|
4
|
-
buildRequestedSkillsUserPrompt,
|
|
5
4
|
resolveProviderRuntime,
|
|
6
|
-
getWorkspacePath
|
|
7
|
-
SkillsLoader
|
|
5
|
+
getWorkspacePath
|
|
8
6
|
} from "@nextclaw/core";
|
|
9
7
|
import {
|
|
10
8
|
CodexSdkNcpAgentRuntime
|
|
11
9
|
} from "@nextclaw/nextclaw-ncp-runtime-codex-sdk";
|
|
12
10
|
import {
|
|
13
11
|
buildUserFacingModelRoute,
|
|
12
|
+
buildCodexBridgeModelProviderId,
|
|
14
13
|
resolveExternalModelProvider
|
|
15
14
|
} from "./codex-model-provider.js";
|
|
15
|
+
import { buildCodexInputBuilder } from "./codex-input-builder.js";
|
|
16
|
+
import { ensureCodexOpenAiResponsesBridge } from "./codex-openai-responses-bridge.js";
|
|
17
|
+
import { resolveCodexResponsesApiSupport } from "./codex-responses-capability.js";
|
|
18
|
+
import {
|
|
19
|
+
createDescribeCodexSessionType
|
|
20
|
+
} from "./codex-session-type.js";
|
|
16
21
|
const PLUGIN_ID = "nextclaw-ncp-runtime-plugin-codex-sdk";
|
|
17
22
|
const CODEX_RUNTIME_KIND = "codex";
|
|
23
|
+
class DeferredCodexSdkNcpAgentRuntime {
|
|
24
|
+
constructor(createRuntime) {
|
|
25
|
+
this.createRuntime = createRuntime;
|
|
26
|
+
}
|
|
27
|
+
runtimePromise = null;
|
|
28
|
+
run(input, options) {
|
|
29
|
+
const resolveRuntime = async () => {
|
|
30
|
+
if (!this.runtimePromise) {
|
|
31
|
+
this.runtimePromise = this.createRuntime();
|
|
32
|
+
}
|
|
33
|
+
return await this.runtimePromise;
|
|
34
|
+
};
|
|
35
|
+
const stream = async function* () {
|
|
36
|
+
const runtime = await resolveRuntime();
|
|
37
|
+
yield* runtime.run(input, options);
|
|
38
|
+
}.bind(this);
|
|
39
|
+
return stream();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
18
42
|
function readString(value) {
|
|
19
43
|
if (typeof value !== "string") {
|
|
20
44
|
return void 0;
|
|
@@ -82,7 +106,7 @@ function resolveCodexExecutionOptions(params) {
|
|
|
82
106
|
}
|
|
83
107
|
function resolveCodexCliConfig(params) {
|
|
84
108
|
const explicitConfig = readRecord(params.pluginConfig.config);
|
|
85
|
-
const modelProvider = resolveExternalModelProvider({
|
|
109
|
+
const modelProvider = readString(params.modelProviderOverride) ?? resolveExternalModelProvider({
|
|
86
110
|
explicitModelProvider: params.pluginConfig.modelProvider,
|
|
87
111
|
providerName: params.providerName,
|
|
88
112
|
providerDisplayName: params.providerDisplayName,
|
|
@@ -109,35 +133,6 @@ function resolveCodexCliConfig(params) {
|
|
|
109
133
|
...explicitConfig ?? {}
|
|
110
134
|
};
|
|
111
135
|
}
|
|
112
|
-
function readRequestedSkills(metadata) {
|
|
113
|
-
const raw = metadata.requested_skills ?? metadata.requestedSkills;
|
|
114
|
-
if (!Array.isArray(raw)) {
|
|
115
|
-
return [];
|
|
116
|
-
}
|
|
117
|
-
return raw.map((entry) => readString(entry)).filter((entry) => Boolean(entry)).slice(0, 8);
|
|
118
|
-
}
|
|
119
|
-
function readUserText(input) {
|
|
120
|
-
for (let index = input.messages.length - 1; index >= 0; index -= 1) {
|
|
121
|
-
const message = input.messages[index];
|
|
122
|
-
if (message?.role !== "user") {
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
const text = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("").trim();
|
|
126
|
-
if (text) {
|
|
127
|
-
return text;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return "";
|
|
131
|
-
}
|
|
132
|
-
function buildCodexInputBuilder(workspace) {
|
|
133
|
-
const skillsLoader = new SkillsLoader(workspace);
|
|
134
|
-
return async (input) => {
|
|
135
|
-
const userText = readUserText(input);
|
|
136
|
-
const metadata = input.metadata && typeof input.metadata === "object" && !Array.isArray(input.metadata) ? input.metadata : {};
|
|
137
|
-
const requestedSkills = readRequestedSkills(metadata);
|
|
138
|
-
return buildRequestedSkillsUserPrompt(skillsLoader, requestedSkills, userText);
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
136
|
function resolveCodexModel(params) {
|
|
142
137
|
return readString(params.sessionMetadata.preferred_model) ?? readString(params.sessionMetadata.model) ?? readString(params.pluginConfig.model) ?? params.config.agents.defaults.model;
|
|
143
138
|
}
|
|
@@ -152,81 +147,115 @@ const plugin = {
|
|
|
152
147
|
},
|
|
153
148
|
register(api) {
|
|
154
149
|
const pluginConfig = readRecord(api.pluginConfig) ?? {};
|
|
150
|
+
const describeCodexSessionType = createDescribeCodexSessionType({
|
|
151
|
+
config: api.config,
|
|
152
|
+
pluginConfig
|
|
153
|
+
});
|
|
155
154
|
api.registerNcpAgentRuntime({
|
|
156
155
|
kind: CODEX_RUNTIME_KIND,
|
|
157
156
|
label: "Codex",
|
|
157
|
+
describeSessionType: describeCodexSessionType,
|
|
158
158
|
createRuntime: (runtimeParams) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
sessionMetadata: runtimeParams.sessionMetadata
|
|
164
|
-
});
|
|
165
|
-
const resolvedProviderRuntime = resolveProviderRuntime(nextConfig, model);
|
|
166
|
-
const providerName = resolvedProviderRuntime.providerName;
|
|
167
|
-
const capabilitySpec = resolveCodexCapabilitySpec({
|
|
168
|
-
model,
|
|
169
|
-
providerName
|
|
170
|
-
});
|
|
171
|
-
const externalModelProvider = resolveExternalModelProvider({
|
|
172
|
-
explicitModelProvider: pluginConfig.modelProvider,
|
|
173
|
-
providerName,
|
|
174
|
-
providerDisplayName: resolvedProviderRuntime.providerDisplayName,
|
|
175
|
-
pluginId: PLUGIN_ID
|
|
176
|
-
});
|
|
177
|
-
const userFacingModelRoute = buildUserFacingModelRoute({
|
|
178
|
-
externalModelProvider,
|
|
179
|
-
providerLocalModel: resolvedProviderRuntime.providerLocalModel,
|
|
180
|
-
resolvedModel: resolvedProviderRuntime.resolvedModel
|
|
181
|
-
});
|
|
182
|
-
const apiBase = readString(pluginConfig.apiBase) ?? resolvedProviderRuntime.apiBase ?? void 0;
|
|
183
|
-
const apiKey = readString(pluginConfig.apiKey) ?? resolvedProviderRuntime.apiKey ?? void 0;
|
|
184
|
-
if (!apiKey) {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`[codex] missing apiKey. Set plugins.entries.${PLUGIN_ID}.config.apiKey or providers.*.apiKey for model "${userFacingModelRoute}".`
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
if (capabilitySpec?.supportsResponsesApi === false) {
|
|
190
|
-
const capabilityProviderName = capabilitySpec.displayName ?? capabilitySpec.name ?? resolvedProviderRuntime.providerDisplayName ?? externalModelProvider;
|
|
191
|
-
throw new Error(
|
|
192
|
-
`[codex] model "${userFacingModelRoute}" is routed through "${capabilityProviderName}", which does not support the Responses API. Codex SDK currently only supports models available through the Responses API.`
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
const executionOptions = resolveCodexExecutionOptions({
|
|
196
|
-
config: nextConfig,
|
|
197
|
-
pluginConfig
|
|
198
|
-
});
|
|
199
|
-
const thinkingLevel = readThinkingLevel(runtimeParams.sessionMetadata.preferred_thinking) ?? readThinkingLevel(runtimeParams.sessionMetadata.thinking) ?? void 0;
|
|
200
|
-
return new CodexSdkNcpAgentRuntime({
|
|
201
|
-
sessionId: runtimeParams.sessionId,
|
|
202
|
-
apiKey,
|
|
203
|
-
apiBase,
|
|
204
|
-
model: resolvedProviderRuntime.providerLocalModel,
|
|
205
|
-
threadId: readString(runtimeParams.sessionMetadata.codex_thread_id) ?? null,
|
|
206
|
-
codexPathOverride: readString(pluginConfig.codexPathOverride),
|
|
207
|
-
env: readStringRecord(pluginConfig.env),
|
|
208
|
-
cliConfig: resolveCodexCliConfig({
|
|
159
|
+
return new DeferredCodexSdkNcpAgentRuntime(async () => {
|
|
160
|
+
const nextConfig = api.config;
|
|
161
|
+
const model = resolveCodexModel({
|
|
162
|
+
config: nextConfig,
|
|
209
163
|
pluginConfig,
|
|
164
|
+
sessionMetadata: runtimeParams.sessionMetadata
|
|
165
|
+
});
|
|
166
|
+
const resolvedProviderRuntime = resolveProviderRuntime(nextConfig, model);
|
|
167
|
+
const providerName = resolvedProviderRuntime.providerName;
|
|
168
|
+
const capabilitySpec = resolveCodexCapabilitySpec({
|
|
169
|
+
model,
|
|
170
|
+
providerName
|
|
171
|
+
});
|
|
172
|
+
const externalModelProvider = resolveExternalModelProvider({
|
|
173
|
+
explicitModelProvider: pluginConfig.modelProvider,
|
|
210
174
|
providerName,
|
|
211
175
|
providerDisplayName: resolvedProviderRuntime.providerDisplayName,
|
|
212
|
-
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
176
|
+
pluginId: PLUGIN_ID
|
|
177
|
+
});
|
|
178
|
+
const userFacingModelRoute = buildUserFacingModelRoute({
|
|
179
|
+
externalModelProvider,
|
|
180
|
+
providerLocalModel: resolvedProviderRuntime.providerLocalModel,
|
|
181
|
+
resolvedModel: resolvedProviderRuntime.resolvedModel
|
|
182
|
+
});
|
|
183
|
+
const upstreamApiBase = readString(pluginConfig.apiBase) ?? resolvedProviderRuntime.apiBase ?? void 0;
|
|
184
|
+
const apiKey = readString(pluginConfig.apiKey) ?? resolvedProviderRuntime.apiKey ?? void 0;
|
|
185
|
+
if (!apiKey) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`[codex] missing apiKey. Set plugins.entries.${PLUGIN_ID}.config.apiKey or providers.*.apiKey for model "${userFacingModelRoute}".`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
if (!upstreamApiBase) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`[codex] missing apiBase for model "${userFacingModelRoute}". Configure plugins.entries.${PLUGIN_ID}.config.apiBase or providers.*.apiBase.`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
let codexApiBase = upstreamApiBase;
|
|
196
|
+
let codexModelProviderOverride;
|
|
197
|
+
const supportsResponsesApi = await resolveCodexResponsesApiSupport({
|
|
198
|
+
capabilitySpec,
|
|
199
|
+
wireApi: readString(resolvedProviderRuntime.provider?.wireApi),
|
|
200
|
+
apiBase: upstreamApiBase,
|
|
201
|
+
apiKey,
|
|
202
|
+
extraHeaders: resolvedProviderRuntime.provider?.extraHeaders ?? null,
|
|
203
|
+
model: resolvedProviderRuntime.providerLocalModel
|
|
204
|
+
});
|
|
205
|
+
if (!supportsResponsesApi) {
|
|
206
|
+
const bridge = await ensureCodexOpenAiResponsesBridge({
|
|
207
|
+
upstreamApiBase,
|
|
208
|
+
upstreamApiKey: apiKey,
|
|
209
|
+
upstreamExtraHeaders: resolvedProviderRuntime.provider?.extraHeaders ?? void 0,
|
|
210
|
+
defaultModel: resolvedProviderRuntime.providerLocalModel,
|
|
211
|
+
modelPrefixes: [
|
|
212
|
+
providerName ?? "",
|
|
213
|
+
externalModelProvider,
|
|
214
|
+
resolvedProviderRuntime.providerDisplayName ?? ""
|
|
215
|
+
]
|
|
216
|
+
});
|
|
217
|
+
codexApiBase = bridge.baseUrl;
|
|
218
|
+
codexModelProviderOverride = buildCodexBridgeModelProviderId(
|
|
219
|
+
externalModelProvider
|
|
220
|
+
);
|
|
229
221
|
}
|
|
222
|
+
const executionOptions = resolveCodexExecutionOptions({
|
|
223
|
+
config: nextConfig,
|
|
224
|
+
pluginConfig
|
|
225
|
+
});
|
|
226
|
+
const thinkingLevel = readThinkingLevel(runtimeParams.sessionMetadata.preferred_thinking) ?? readThinkingLevel(runtimeParams.sessionMetadata.thinking) ?? void 0;
|
|
227
|
+
return new CodexSdkNcpAgentRuntime({
|
|
228
|
+
sessionId: runtimeParams.sessionId,
|
|
229
|
+
apiKey,
|
|
230
|
+
apiBase: codexApiBase,
|
|
231
|
+
model: resolvedProviderRuntime.providerLocalModel,
|
|
232
|
+
threadId: readString(runtimeParams.sessionMetadata.codex_thread_id) ?? null,
|
|
233
|
+
codexPathOverride: readString(pluginConfig.codexPathOverride),
|
|
234
|
+
env: readStringRecord(pluginConfig.env),
|
|
235
|
+
cliConfig: resolveCodexCliConfig({
|
|
236
|
+
pluginConfig,
|
|
237
|
+
providerName,
|
|
238
|
+
providerDisplayName: resolvedProviderRuntime.providerDisplayName,
|
|
239
|
+
apiBase: codexApiBase,
|
|
240
|
+
modelProviderOverride: codexModelProviderOverride
|
|
241
|
+
}),
|
|
242
|
+
stateManager: runtimeParams.stateManager,
|
|
243
|
+
sessionMetadata: runtimeParams.sessionMetadata,
|
|
244
|
+
setSessionMetadata: runtimeParams.setSessionMetadata,
|
|
245
|
+
inputBuilder: buildCodexInputBuilder(executionOptions.workingDirectory),
|
|
246
|
+
threadOptions: {
|
|
247
|
+
model,
|
|
248
|
+
sandboxMode: readString(pluginConfig.sandboxMode),
|
|
249
|
+
workingDirectory: executionOptions.workingDirectory,
|
|
250
|
+
skipGitRepoCheck: executionOptions.skipGitRepoCheck,
|
|
251
|
+
modelReasoningEffort: thinkingLevel,
|
|
252
|
+
networkAccessEnabled: readBoolean(pluginConfig.networkAccessEnabled),
|
|
253
|
+
webSearchMode: readString(pluginConfig.webSearchMode),
|
|
254
|
+
webSearchEnabled: readBoolean(pluginConfig.webSearchEnabled),
|
|
255
|
+
approvalPolicy: readString(pluginConfig.approvalPolicy),
|
|
256
|
+
additionalDirectories: readStringArray(pluginConfig.additionalDirectories)
|
|
257
|
+
}
|
|
258
|
+
});
|
|
230
259
|
});
|
|
231
260
|
}
|
|
232
261
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "NextClaw plugin that registers Codex SDK as an optional NCP runtime.",
|
|
6
6
|
"type": "module",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
]
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@nextclaw/core": "0.
|
|
24
|
+
"@nextclaw/core": "0.10.0",
|
|
25
|
+
"@nextclaw/ncp": "0.3.2",
|
|
25
26
|
"@nextclaw/ncp-toolkit": "0.4.2",
|
|
26
|
-
"@nextclaw/nextclaw-ncp-runtime-codex-sdk": "0.1.2"
|
|
27
|
-
"@nextclaw/ncp": "0.3.2"
|
|
27
|
+
"@nextclaw/nextclaw-ncp-runtime-codex-sdk": "0.1.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^20.17.6",
|