@nextclaw/nextclaw-ncp-runtime-codex-sdk 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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CodexOptions, ThreadOptions } from '@openai/codex-sdk';
2
- import { NcpAgentRuntime, NcpAgentRunInput, NcpAgentRunOptions, NcpEndpointEvent } from '@nextclaw/ncp';
2
+ import { NcpAgentRuntime, NcpAgentRunInput, NcpAgentConversationStateManager, NcpAgentRunOptions, NcpEndpointEvent } from '@nextclaw/ncp';
3
3
 
4
4
  type CodexSdkNcpAgentRuntimeConfig = {
5
5
  sessionId: string;
@@ -14,6 +14,7 @@ type CodexSdkNcpAgentRuntimeConfig = {
14
14
  sessionMetadata?: Record<string, unknown>;
15
15
  setSessionMetadata?: (nextMetadata: Record<string, unknown>) => void;
16
16
  inputBuilder?: (input: NcpAgentRunInput) => Promise<string> | string;
17
+ stateManager?: NcpAgentConversationStateManager;
17
18
  };
18
19
  declare class CodexSdkNcpAgentRuntime implements NcpAgentRuntime {
19
20
  private readonly config;
@@ -26,6 +27,7 @@ declare class CodexSdkNcpAgentRuntime implements NcpAgentRuntime {
26
27
  private getCodex;
27
28
  private resolveThread;
28
29
  private buildTurnInput;
30
+ private emitEvent;
29
31
  private updateThreadId;
30
32
  }
31
33
 
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
- import { NcpEventType } from "@nextclaw/ncp";
2
+ import {
3
+ NcpEventType
4
+ } from "@nextclaw/ncp";
3
5
  import {
4
6
  mapCodexItemEvent
5
7
  } from "./codex-sdk-ncp-event-mapper.js";
@@ -30,6 +32,25 @@ function toAbortError(reason) {
30
32
  error.name = "AbortError";
31
33
  return error;
32
34
  }
35
+ function buildCliEnv(config) {
36
+ const env = {
37
+ ...config.env ?? {}
38
+ };
39
+ if (config.apiKey.trim()) {
40
+ env.OPENAI_API_KEY = config.apiKey;
41
+ }
42
+ if (config.apiBase?.trim()) {
43
+ env.OPENAI_BASE_URL = config.apiBase.trim();
44
+ }
45
+ return Object.keys(env).length > 0 ? env : void 0;
46
+ }
47
+ function normalizeThreadOptions(options, model) {
48
+ return {
49
+ ...options,
50
+ skipGitRepoCheck: options?.skipGitRepoCheck ?? true,
51
+ ...model ? { model } : {}
52
+ };
53
+ }
33
54
  class CodexSdkNcpAgentRuntime {
34
55
  constructor(config) {
35
56
  this.config = config;
@@ -48,15 +69,15 @@ class CodexSdkNcpAgentRuntime {
48
69
  const itemTextById = /* @__PURE__ */ new Map();
49
70
  const toolStateById = /* @__PURE__ */ new Map();
50
71
  let finished = false;
51
- yield {
72
+ yield* this.emitEvent({
52
73
  type: NcpEventType.RunStarted,
53
74
  payload: {
54
75
  sessionId: input.sessionId,
55
76
  messageId,
56
77
  runId
57
78
  }
58
- };
59
- yield {
79
+ });
80
+ yield* this.emitEvent({
60
81
  type: NcpEventType.RunMetadata,
61
82
  payload: {
62
83
  sessionId: input.sessionId,
@@ -69,7 +90,7 @@ class CodexSdkNcpAgentRuntime {
69
90
  supportsAbort: true
70
91
  }
71
92
  }
72
- };
93
+ });
73
94
  const thread = await this.resolveThread();
74
95
  const turnInput = await this.buildTurnInput(input);
75
96
  const streamed = await thread.runStreamed(turnInput, {
@@ -85,7 +106,7 @@ class CodexSdkNcpAgentRuntime {
85
106
  continue;
86
107
  }
87
108
  if (event.type === "turn.failed") {
88
- yield {
109
+ yield* this.emitEvent({
89
110
  type: NcpEventType.RunError,
90
111
  payload: {
91
112
  sessionId: input.sessionId,
@@ -93,12 +114,12 @@ class CodexSdkNcpAgentRuntime {
93
114
  runId,
94
115
  error: event.error.message
95
116
  }
96
- };
117
+ });
97
118
  finished = true;
98
119
  return;
99
120
  }
100
121
  if (event.type === "error") {
101
- yield {
122
+ yield* this.emitEvent({
102
123
  type: NcpEventType.RunError,
103
124
  payload: {
104
125
  sessionId: input.sessionId,
@@ -106,22 +127,24 @@ class CodexSdkNcpAgentRuntime {
106
127
  runId,
107
128
  error: event.message
108
129
  }
109
- };
130
+ });
110
131
  finished = true;
111
132
  return;
112
133
  }
113
134
  if (event.type === "item.started" || event.type === "item.updated" || event.type === "item.completed") {
114
- yield* mapCodexItemEvent({
135
+ for await (const mappedEvent of mapCodexItemEvent({
115
136
  sessionId: input.sessionId,
116
137
  messageId,
117
138
  event,
118
139
  itemTextById,
119
140
  toolStateById
120
- });
141
+ })) {
142
+ yield* this.emitEvent(mappedEvent);
143
+ }
121
144
  continue;
122
145
  }
123
146
  if (event.type === "turn.completed") {
124
- yield {
147
+ yield* this.emitEvent({
125
148
  type: NcpEventType.RunMetadata,
126
149
  payload: {
127
150
  sessionId: input.sessionId,
@@ -132,21 +155,21 @@ class CodexSdkNcpAgentRuntime {
132
155
  sessionId: input.sessionId
133
156
  }
134
157
  }
135
- };
136
- yield {
158
+ });
159
+ yield* this.emitEvent({
137
160
  type: NcpEventType.RunFinished,
138
161
  payload: {
139
162
  sessionId: input.sessionId,
140
163
  messageId,
141
164
  runId
142
165
  }
143
- };
166
+ });
144
167
  finished = true;
145
168
  return;
146
169
  }
147
170
  }
148
171
  if (!finished) {
149
- yield {
172
+ yield* this.emitEvent({
150
173
  type: NcpEventType.RunMetadata,
151
174
  payload: {
152
175
  sessionId: input.sessionId,
@@ -157,15 +180,15 @@ class CodexSdkNcpAgentRuntime {
157
180
  sessionId: input.sessionId
158
181
  }
159
182
  }
160
- };
161
- yield {
183
+ });
184
+ yield* this.emitEvent({
162
185
  type: NcpEventType.RunFinished,
163
186
  payload: {
164
187
  sessionId: input.sessionId,
165
188
  messageId,
166
189
  runId
167
190
  }
168
- };
191
+ });
169
192
  }
170
193
  } catch (error) {
171
194
  if (options?.signal?.aborted) {
@@ -176,12 +199,13 @@ class CodexSdkNcpAgentRuntime {
176
199
  }
177
200
  async getCodex() {
178
201
  if (!this.codexPromise) {
202
+ const env = buildCliEnv(this.config);
179
203
  this.codexPromise = codexLoader.loadCodexConstructor().then(
180
204
  (Ctor) => new Ctor({
181
205
  apiKey: this.config.apiKey,
182
206
  baseUrl: this.config.apiBase,
183
207
  ...this.config.codexPathOverride ? { codexPathOverride: this.config.codexPathOverride } : {},
184
- ...this.config.env ? { env: this.config.env } : {},
208
+ ...env ? { env } : {},
185
209
  ...this.config.cliConfig ? { config: this.config.cliConfig } : {}
186
210
  })
187
211
  );
@@ -193,10 +217,7 @@ class CodexSdkNcpAgentRuntime {
193
217
  return this.thread;
194
218
  }
195
219
  const codex = await this.getCodex();
196
- const threadOptions = {
197
- ...this.config.threadOptions,
198
- ...this.config.model ? { model: this.config.model } : {}
199
- };
220
+ const threadOptions = normalizeThreadOptions(this.config.threadOptions, this.config.model);
200
221
  this.thread = this.threadId ? codex.resumeThread(this.threadId, threadOptions) : codex.startThread(threadOptions);
201
222
  return this.thread;
202
223
  }
@@ -206,6 +227,10 @@ class CodexSdkNcpAgentRuntime {
206
227
  }
207
228
  return readUserText(input);
208
229
  }
230
+ async *emitEvent(event) {
231
+ await this.config.stateManager?.dispatch(event);
232
+ yield event;
233
+ }
209
234
  updateThreadId(nextThreadId) {
210
235
  const normalizedThreadId = nextThreadId.trim();
211
236
  if (!normalizedThreadId || normalizedThreadId === this.threadId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/nextclaw-ncp-runtime-codex-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "description": "Optional NCP runtime adapter backed by OpenAI Codex SDK.",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "@openai/codex-sdk": "^0.107.0",
20
- "@nextclaw/ncp": "0.3.0"
20
+ "@nextclaw/ncp": "0.3.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^20.17.6",