@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk 0.1.5 → 0.1.7

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.
@@ -0,0 +1,23 @@
1
+ import { Config } from '@nextclaw/core';
2
+ import { NcpAgentRuntime } from '@nextclaw/ncp';
3
+ import { RuntimeFactoryParams } from '@nextclaw/ncp-toolkit';
4
+
5
+ type PluginApi = {
6
+ config: Config;
7
+ pluginConfig?: Record<string, unknown>;
8
+ registerNcpAgentRuntime: (registration: {
9
+ kind: string;
10
+ label?: string;
11
+ createRuntime: (params: RuntimeFactoryParams) => NcpAgentRuntime;
12
+ }) => void;
13
+ };
14
+ type PluginDefinition = {
15
+ id: string;
16
+ name: string;
17
+ description: string;
18
+ configSchema: Record<string, unknown>;
19
+ register: (api: PluginApi) => void;
20
+ };
21
+ declare const plugin: PluginDefinition;
22
+
23
+ export { plugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,232 @@
1
+ import {
2
+ findProviderByModel,
3
+ findProviderByName,
4
+ getApiBase,
5
+ buildRequestedSkillsUserPrompt,
6
+ getProvider,
7
+ getProviderName,
8
+ getWorkspacePath,
9
+ SkillsLoader
10
+ } from "@nextclaw/core";
11
+ import {
12
+ CodexSdkNcpAgentRuntime
13
+ } from "@nextclaw/nextclaw-ncp-runtime-codex-sdk";
14
+ const PLUGIN_ID = "nextclaw-ncp-runtime-plugin-codex-sdk";
15
+ const CODEX_RUNTIME_KIND = "codex";
16
+ function readString(value) {
17
+ if (typeof value !== "string") {
18
+ return void 0;
19
+ }
20
+ const trimmed = value.trim();
21
+ return trimmed || void 0;
22
+ }
23
+ function readBoolean(value) {
24
+ return typeof value === "boolean" ? value : void 0;
25
+ }
26
+ function readRecord(value) {
27
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
28
+ return void 0;
29
+ }
30
+ return value;
31
+ }
32
+ function readStringArray(value) {
33
+ if (!Array.isArray(value)) {
34
+ return void 0;
35
+ }
36
+ const values = value.map((entry) => readString(entry)).filter((entry) => Boolean(entry));
37
+ return values.length > 0 ? values : void 0;
38
+ }
39
+ function resolveCodexCapabilitySpec(params) {
40
+ const providerSpec = params.providerName ? findProviderByName(params.providerName) : void 0;
41
+ const modelSpec = params.model ? findProviderByModel(params.model) : void 0;
42
+ return providerSpec?.isGateway ? modelSpec ?? providerSpec : providerSpec ?? modelSpec;
43
+ }
44
+ function readStringRecord(value) {
45
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
46
+ return void 0;
47
+ }
48
+ const out = {};
49
+ for (const [entryKey, entryValue] of Object.entries(value)) {
50
+ if (typeof entryValue !== "string") {
51
+ continue;
52
+ }
53
+ const normalized = entryValue.trim();
54
+ if (!normalized) {
55
+ continue;
56
+ }
57
+ out[entryKey] = normalized;
58
+ }
59
+ return Object.keys(out).length > 0 ? out : void 0;
60
+ }
61
+ function readThinkingLevel(value) {
62
+ if (typeof value !== "string") {
63
+ return void 0;
64
+ }
65
+ const normalized = value.trim().toLowerCase();
66
+ if (normalized === "minimal" || normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "xhigh") {
67
+ return normalized;
68
+ }
69
+ return void 0;
70
+ }
71
+ function resolveCodexExecutionOptions(params) {
72
+ const configuredWorkingDirectory = readString(params.pluginConfig.workingDirectory);
73
+ const workspace = getWorkspacePath(
74
+ configuredWorkingDirectory ?? params.config.agents.defaults.workspace
75
+ );
76
+ return {
77
+ workingDirectory: workspace,
78
+ skipGitRepoCheck: readBoolean(params.pluginConfig.skipGitRepoCheck) ?? true
79
+ };
80
+ }
81
+ function resolveCodexCliConfig(params) {
82
+ const explicitConfig = readRecord(params.pluginConfig.config);
83
+ const modelProvider = readString(params.pluginConfig.modelProvider) ?? readString(params.providerName) ?? "openai";
84
+ const preferredAuthMethod = readString(params.pluginConfig.preferredAuthMethod) ?? "apikey";
85
+ const apiBase = readString(params.pluginConfig.apiBase) ?? readString(params.apiBase);
86
+ const config = {
87
+ model_provider: modelProvider,
88
+ preferred_auth_method: preferredAuthMethod
89
+ };
90
+ if (modelProvider && apiBase) {
91
+ config.model_providers = {
92
+ [modelProvider]: {
93
+ name: modelProvider,
94
+ base_url: apiBase,
95
+ wire_api: "responses",
96
+ requires_openai_auth: true
97
+ }
98
+ };
99
+ }
100
+ return {
101
+ ...config,
102
+ ...explicitConfig ?? {}
103
+ };
104
+ }
105
+ function stripProviderPrefix(model, providerName) {
106
+ const normalizedModel = model.trim();
107
+ const normalizedProvider = providerName?.trim().toLowerCase();
108
+ if (!normalizedModel || !normalizedProvider) {
109
+ return normalizedModel;
110
+ }
111
+ const prefix = `${normalizedProvider}/`;
112
+ if (!normalizedModel.toLowerCase().startsWith(prefix)) {
113
+ return normalizedModel;
114
+ }
115
+ return normalizedModel.slice(prefix.length);
116
+ }
117
+ function readRequestedSkills(metadata) {
118
+ const raw = metadata.requested_skills ?? metadata.requestedSkills;
119
+ if (!Array.isArray(raw)) {
120
+ return [];
121
+ }
122
+ return raw.map((entry) => readString(entry)).filter((entry) => Boolean(entry)).slice(0, 8);
123
+ }
124
+ function readUserText(input) {
125
+ for (let index = input.messages.length - 1; index >= 0; index -= 1) {
126
+ const message = input.messages[index];
127
+ if (message?.role !== "user") {
128
+ continue;
129
+ }
130
+ const text = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("").trim();
131
+ if (text) {
132
+ return text;
133
+ }
134
+ }
135
+ return "";
136
+ }
137
+ function buildCodexInputBuilder(workspace) {
138
+ const skillsLoader = new SkillsLoader(workspace);
139
+ return async (input) => {
140
+ const userText = readUserText(input);
141
+ const metadata = input.metadata && typeof input.metadata === "object" && !Array.isArray(input.metadata) ? input.metadata : {};
142
+ const requestedSkills = readRequestedSkills(metadata);
143
+ return buildRequestedSkillsUserPrompt(skillsLoader, requestedSkills, userText);
144
+ };
145
+ }
146
+ function resolveCodexModel(params) {
147
+ return readString(params.sessionMetadata.preferred_model) ?? readString(params.sessionMetadata.model) ?? readString(params.pluginConfig.model) ?? params.config.agents.defaults.model;
148
+ }
149
+ const plugin = {
150
+ id: PLUGIN_ID,
151
+ name: "NextClaw Codex NCP Runtime",
152
+ description: "Registers NCP session type `codex` backed by OpenAI Codex SDK.",
153
+ configSchema: {
154
+ type: "object",
155
+ additionalProperties: true,
156
+ properties: {}
157
+ },
158
+ register(api) {
159
+ const pluginConfig = readRecord(api.pluginConfig) ?? {};
160
+ api.registerNcpAgentRuntime({
161
+ kind: CODEX_RUNTIME_KIND,
162
+ label: "Codex",
163
+ createRuntime: (runtimeParams) => {
164
+ const nextConfig = api.config;
165
+ const model = resolveCodexModel({
166
+ config: nextConfig,
167
+ pluginConfig,
168
+ sessionMetadata: runtimeParams.sessionMetadata
169
+ });
170
+ const provider = getProvider(nextConfig, model);
171
+ const providerName = getProviderName(nextConfig, model);
172
+ const capabilitySpec = resolveCodexCapabilitySpec({
173
+ model,
174
+ providerName
175
+ });
176
+ const apiBase = readString(pluginConfig.apiBase) ?? getApiBase(nextConfig, model) ?? void 0;
177
+ const apiKey = readString(pluginConfig.apiKey) ?? provider?.apiKey ?? void 0;
178
+ if (!apiKey) {
179
+ throw new Error(
180
+ `[codex] missing apiKey. Set plugins.entries.${PLUGIN_ID}.config.apiKey or providers.*.apiKey for model "${model}".`
181
+ );
182
+ }
183
+ if (capabilitySpec?.supportsResponsesApi === false) {
184
+ const capabilityProviderName = capabilitySpec.displayName ?? capabilitySpec.name ?? providerName ?? "provider";
185
+ throw new Error(
186
+ `[codex] model "${model}" is routed through "${capabilityProviderName}", which does not support the Responses API. Codex SDK currently only supports models available through the Responses API.`
187
+ );
188
+ }
189
+ const executionOptions = resolveCodexExecutionOptions({
190
+ config: nextConfig,
191
+ pluginConfig
192
+ });
193
+ const thinkingLevel = readThinkingLevel(runtimeParams.sessionMetadata.preferred_thinking) ?? readThinkingLevel(runtimeParams.sessionMetadata.thinking) ?? void 0;
194
+ return new CodexSdkNcpAgentRuntime({
195
+ sessionId: runtimeParams.sessionId,
196
+ apiKey,
197
+ apiBase,
198
+ model: stripProviderPrefix(model, providerName),
199
+ threadId: readString(runtimeParams.sessionMetadata.codex_thread_id) ?? null,
200
+ codexPathOverride: readString(pluginConfig.codexPathOverride),
201
+ env: readStringRecord(pluginConfig.env),
202
+ cliConfig: resolveCodexCliConfig({
203
+ pluginConfig,
204
+ model,
205
+ providerName,
206
+ apiBase
207
+ }),
208
+ stateManager: runtimeParams.stateManager,
209
+ sessionMetadata: runtimeParams.sessionMetadata,
210
+ setSessionMetadata: runtimeParams.setSessionMetadata,
211
+ inputBuilder: buildCodexInputBuilder(executionOptions.workingDirectory),
212
+ threadOptions: {
213
+ model,
214
+ sandboxMode: readString(pluginConfig.sandboxMode),
215
+ workingDirectory: executionOptions.workingDirectory,
216
+ skipGitRepoCheck: executionOptions.skipGitRepoCheck,
217
+ modelReasoningEffort: thinkingLevel,
218
+ networkAccessEnabled: readBoolean(pluginConfig.networkAccessEnabled),
219
+ webSearchMode: readString(pluginConfig.webSearchMode),
220
+ webSearchEnabled: readBoolean(pluginConfig.webSearchEnabled),
221
+ approvalPolicy: readString(pluginConfig.approvalPolicy),
222
+ additionalDirectories: readStringArray(pluginConfig.additionalDirectories)
223
+ }
224
+ });
225
+ }
226
+ });
227
+ }
228
+ };
229
+ var index_default = plugin;
230
+ export {
231
+ index_default as default
232
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
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.9.3",
24
+ "@nextclaw/core": "0.9.5",
25
25
  "@nextclaw/ncp": "0.3.1",
26
- "@nextclaw/nextclaw-ncp-runtime-codex-sdk": "0.1.1",
27
- "@nextclaw/ncp-toolkit": "0.4.1"
26
+ "@nextclaw/ncp-toolkit": "0.4.1",
27
+ "@nextclaw/nextclaw-ncp-runtime-codex-sdk": "0.1.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^20.17.6",