@janole/ai-sdk-provider-codex-asp 0.2.4 → 0.3.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.js CHANGED
@@ -278,6 +278,7 @@ var AppServerClient = class {
278
278
  var PersistentTransport = class {
279
279
  pool;
280
280
  signal;
281
+ threadId;
281
282
  worker = null;
282
283
  pendingInitializeId = null;
283
284
  initializeIntercepted = false;
@@ -287,9 +288,10 @@ var PersistentTransport = class {
287
288
  constructor(settings) {
288
289
  this.pool = settings.pool;
289
290
  this.signal = settings.signal;
291
+ this.threadId = settings.threadId;
290
292
  }
291
293
  async connect() {
292
- this.worker = await this.pool.acquire(stripUndefined({ signal: this.signal }));
294
+ this.worker = await this.pool.acquire(stripUndefined({ signal: this.signal, threadId: this.threadId }));
293
295
  await this.worker.ensureConnected();
294
296
  }
295
297
  disconnect() {
@@ -661,6 +663,7 @@ var CodexWorker = class {
661
663
  await this.inner.connect();
662
664
  }
663
665
  acquire() {
666
+ this.clearSessionListeners();
664
667
  if (this.idleTimer) {
665
668
  clearTimeout(this.idleTimer);
666
669
  this.idleTimer = null;
@@ -745,8 +748,17 @@ var CodexWorkerPool = class {
745
748
  if (this.shutdownCalled) {
746
749
  throw new CodexProviderError("Worker pool has been shut down.");
747
750
  }
751
+ if (options?.threadId) {
752
+ const reserved = this.workers.find(
753
+ (w) => (w.state === "idle" || w.state === "disconnected") && w.pendingToolCall?.threadId === options.threadId
754
+ );
755
+ if (reserved) {
756
+ reserved.acquire();
757
+ return reserved;
758
+ }
759
+ }
748
760
  const worker = this.workers.find(
749
- (w) => w.state === "idle" || w.state === "disconnected"
761
+ (w) => (w.state === "idle" || w.state === "disconnected") && !w.pendingToolCall
750
762
  );
751
763
  if (!worker) {
752
764
  if (options?.signal?.aborted) {
@@ -754,6 +766,7 @@ var CodexWorkerPool = class {
754
766
  }
755
767
  return new Promise((resolve, reject) => {
756
768
  const waiter = {
769
+ threadId: options?.threadId,
757
770
  resolve,
758
771
  reject,
759
772
  signal: options?.signal,
@@ -773,6 +786,16 @@ var CodexWorkerPool = class {
773
786
  return worker;
774
787
  }
775
788
  release(worker) {
789
+ worker.clearSessionListeners();
790
+ if (worker.pendingToolCall) {
791
+ const idx = this.waiters.findIndex((w) => w.threadId === worker.pendingToolCall?.threadId);
792
+ if (idx >= 0) {
793
+ const [waiter2] = this.waiters.splice(idx, 1);
794
+ this.clearWaiterAbortHandler(waiter2);
795
+ waiter2.resolve(worker);
796
+ return;
797
+ }
798
+ }
776
799
  const waiter = this.waiters.shift();
777
800
  if (waiter) {
778
801
  this.clearWaiterAbortHandler(waiter);
@@ -933,7 +956,7 @@ var DynamicToolsDispatcher = class {
933
956
  // package.json
934
957
  var package_default = {
935
958
  name: "@janole/ai-sdk-provider-codex-asp",
936
- version: "0.2.4"};
959
+ version: "0.3.1"};
937
960
 
938
961
  // src/package-info.ts
939
962
  var PACKAGE_NAME = package_default.name;
@@ -941,14 +964,14 @@ var PACKAGE_VERSION = package_default.version;
941
964
 
942
965
  // src/protocol/provider-metadata.ts
943
966
  var CODEX_PROVIDER_ID = "@janole/ai-sdk-provider-codex-asp";
944
- function codexProviderMetadata(threadId) {
967
+ function codexProviderMetadata(threadId, turnId) {
945
968
  if (!threadId) {
946
969
  return void 0;
947
970
  }
948
- return { [CODEX_PROVIDER_ID]: { threadId } };
971
+ return { [CODEX_PROVIDER_ID]: stripUndefined({ threadId, turnId }) };
949
972
  }
950
- function withProviderMetadata(part, threadId) {
951
- const meta = codexProviderMetadata(threadId);
973
+ function withProviderMetadata(part, threadId, turnId) {
974
+ const meta = codexProviderMetadata(threadId, turnId);
952
975
  return meta ? { ...part, providerMetadata: meta } : part;
953
976
  }
954
977
 
@@ -987,6 +1010,7 @@ var CodexEventMapper = class {
987
1010
  openToolCalls = /* @__PURE__ */ new Map();
988
1011
  planSequenceByTurnId = /* @__PURE__ */ new Map();
989
1012
  threadId;
1013
+ turnId;
990
1014
  latestUsage;
991
1015
  constructor(options) {
992
1016
  this.options = {
@@ -996,6 +1020,12 @@ var CodexEventMapper = class {
996
1020
  setThreadId(threadId) {
997
1021
  this.threadId = threadId;
998
1022
  }
1023
+ setTurnId(turnId) {
1024
+ this.turnId = turnId;
1025
+ }
1026
+ getTurnId() {
1027
+ return this.turnId;
1028
+ }
999
1029
  nextPlanSequence(turnId) {
1000
1030
  const next = (this.planSequenceByTurnId.get(turnId) ?? 0) + 1;
1001
1031
  this.planSequenceByTurnId.set(turnId, next);
@@ -1003,7 +1033,7 @@ var CodexEventMapper = class {
1003
1033
  }
1004
1034
  map(event) {
1005
1035
  const parts = [];
1006
- const withMeta = (part) => withProviderMetadata(part, this.threadId);
1036
+ const withMeta = (part) => withProviderMetadata(part, this.threadId, this.turnId);
1007
1037
  const pushStreamStart = () => {
1008
1038
  if (!this.streamStarted) {
1009
1039
  parts.push({ type: "stream-start", warnings: [] });
@@ -1023,6 +1053,10 @@ var CodexEventMapper = class {
1023
1053
  };
1024
1054
  switch (event.method) {
1025
1055
  case "turn/started": {
1056
+ const turnStartedParams = event.params;
1057
+ if (turnStartedParams?.turn?.id) {
1058
+ this.turnId = turnStartedParams.turn.id;
1059
+ }
1026
1060
  pushStreamStart();
1027
1061
  break;
1028
1062
  }
@@ -1320,6 +1354,75 @@ var CodexEventMapper = class {
1320
1354
  return parts;
1321
1355
  }
1322
1356
  };
1357
+
1358
+ // src/session.ts
1359
+ var CodexSessionImpl = class {
1360
+ _threadId;
1361
+ _turnId;
1362
+ _active = true;
1363
+ client;
1364
+ interruptTimeoutMs;
1365
+ constructor(opts) {
1366
+ this.client = opts.client;
1367
+ this._threadId = opts.threadId;
1368
+ this._turnId = opts.turnId;
1369
+ this.interruptTimeoutMs = opts.interruptTimeoutMs;
1370
+ }
1371
+ get threadId() {
1372
+ return this._threadId;
1373
+ }
1374
+ get turnId() {
1375
+ return this._turnId;
1376
+ }
1377
+ /** @internal Called by the model when turn/started arrives with a turnId. */
1378
+ setTurnId(turnId) {
1379
+ this._turnId = turnId;
1380
+ }
1381
+ /** @internal Called by the model when the turn completes or the stream closes. */
1382
+ markInactive() {
1383
+ this._active = false;
1384
+ }
1385
+ isActive() {
1386
+ return this._active;
1387
+ }
1388
+ /**
1389
+ * Inject follow-up input into the current thread.
1390
+ *
1391
+ * Uses turn/start which the app-server routes through steer_input when a
1392
+ * turn is already active, or starts a new turn otherwise. This avoids the
1393
+ * strict timing requirements of turn/steer (which needs codex/event/task_started
1394
+ * before it accepts input). We may revisit turn/steer in the future.
1395
+ */
1396
+ async injectMessage(input) {
1397
+ if (!this._active) {
1398
+ throw new Error("Session is no longer active.");
1399
+ }
1400
+ const userInput = typeof input === "string" ? [{ type: "text", text: input, text_elements: [] }] : input;
1401
+ const turnStartParams = {
1402
+ threadId: this._threadId,
1403
+ input: userInput
1404
+ };
1405
+ const result = await this.client.request("turn/start", turnStartParams);
1406
+ const newTurnId = result.turnId ?? result.turn?.id;
1407
+ if (newTurnId) {
1408
+ this._turnId = newTurnId;
1409
+ }
1410
+ }
1411
+ async interrupt() {
1412
+ if (!this._active || !this._turnId) {
1413
+ return;
1414
+ }
1415
+ const interruptParams = {
1416
+ threadId: this._threadId,
1417
+ turnId: this._turnId
1418
+ };
1419
+ await this.client.request(
1420
+ "turn/interrupt",
1421
+ interruptParams,
1422
+ this.interruptTimeoutMs
1423
+ );
1424
+ }
1425
+ };
1323
1426
  function mapSystemPrompt(prompt) {
1324
1427
  const chunks = [];
1325
1428
  for (const message of prompt) {
@@ -1732,7 +1835,8 @@ var CodexLanguageModel = class {
1732
1835
  });
1733
1836
  }
1734
1837
  doStream(options) {
1735
- const transport = this.config.providerSettings.transportFactory ? this.config.providerSettings.transportFactory(options.abortSignal) : this.config.providerSettings.transport?.type === "websocket" ? new WebSocketTransport(this.config.providerSettings.transport.websocket) : new StdioTransport(this.config.providerSettings.transport?.stdio);
1838
+ const resumeThreadId = extractResumeThreadId(options.prompt);
1839
+ const transport = this.config.providerSettings.transportFactory ? this.config.providerSettings.transportFactory(options.abortSignal, resumeThreadId) : this.config.providerSettings.transport?.type === "websocket" ? new WebSocketTransport(this.config.providerSettings.transport.websocket) : new StdioTransport(this.config.providerSettings.transport?.stdio);
1736
1840
  const packetLogger = this.config.providerSettings.debug?.logPackets === true ? this.config.providerSettings.debug.logger ?? ((packet) => {
1737
1841
  if (packet.direction === "inbound") {
1738
1842
  console.debug("[codex packet]", packet.message);
@@ -1752,6 +1856,7 @@ var CodexLanguageModel = class {
1752
1856
  }));
1753
1857
  let activeThreadId;
1754
1858
  let activeTurnId;
1859
+ let session;
1755
1860
  const interruptTimeoutMs = this.config.providerSettings.interruptTimeoutMs ?? 1e4;
1756
1861
  const interruptTurnIfPossible = async () => {
1757
1862
  if (!activeThreadId || !activeTurnId) {
@@ -1772,6 +1877,7 @@ var CodexLanguageModel = class {
1772
1877
  if (closed) {
1773
1878
  return;
1774
1879
  }
1880
+ session?.markInactive();
1775
1881
  controller.enqueue({ type: "error", error });
1776
1882
  closed = true;
1777
1883
  try {
@@ -1785,6 +1891,7 @@ var CodexLanguageModel = class {
1785
1891
  if (closed) {
1786
1892
  return;
1787
1893
  }
1894
+ session?.markInactive();
1788
1895
  closed = true;
1789
1896
  try {
1790
1897
  controller.close();
@@ -1891,6 +1998,11 @@ var CodexLanguageModel = class {
1891
1998
  approvalsDispatcher.attach(client);
1892
1999
  client.onAnyNotification((method, params) => {
1893
2000
  const parts = mapper.map({ method, params });
2001
+ const mappedTurnId = mapper.getTurnId();
2002
+ if (mappedTurnId && mappedTurnId !== activeTurnId) {
2003
+ activeTurnId = mappedTurnId;
2004
+ session?.setTurnId(mappedTurnId);
2005
+ }
1894
2006
  for (const part of parts) {
1895
2007
  controller.enqueue(part);
1896
2008
  if (part.type === "finish") {
@@ -1927,7 +2039,6 @@ var CodexLanguageModel = class {
1927
2039
  await client.request("initialize", initializeParams);
1928
2040
  await client.notification("initialized");
1929
2041
  debugLog?.("inbound", "prompt", options.prompt);
1930
- const resumeThreadId = extractResumeThreadId(options.prompt);
1931
2042
  debugLog?.("inbound", "extractResumeThreadId", { resumeThreadId });
1932
2043
  const developerInstructions = mapSystemPrompt(options.prompt);
1933
2044
  let threadId;
@@ -1988,10 +2099,13 @@ var CodexLanguageModel = class {
1988
2099
  }
1989
2100
  }
1990
2101
  } else {
2102
+ const mcpServers = this.config.providerSettings.mcpServers;
2103
+ const config = mcpServers ? { mcp_servers: mcpServers } : void 0;
1991
2104
  const threadStartParams = stripUndefined({
1992
2105
  model: this.config.providerSettings.defaultModel ?? this.modelId,
1993
2106
  dynamicTools,
1994
2107
  developerInstructions,
2108
+ config,
1995
2109
  cwd: this.config.providerSettings.defaultThreadSettings?.cwd,
1996
2110
  approvalPolicy: this.config.providerSettings.defaultThreadSettings?.approvalPolicy,
1997
2111
  sandbox: this.config.providerSettings.defaultThreadSettings?.sandbox
@@ -2023,17 +2137,26 @@ var CodexLanguageModel = class {
2023
2137
  sandboxPolicy: this.config.providerSettings.defaultTurnSettings?.sandboxPolicy,
2024
2138
  model: this.config.providerSettings.defaultTurnSettings?.model,
2025
2139
  effort: this.config.providerSettings.defaultTurnSettings?.effort,
2026
- summary: this.config.providerSettings.defaultTurnSettings?.summary
2140
+ summary: this.config.providerSettings.defaultTurnSettings?.summary,
2141
+ outputSchema: options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0
2027
2142
  });
2028
2143
  debugLog?.("outbound", "turn/start", turnStartParams);
2029
2144
  const turnStartResult = await client.request("turn/start", turnStartParams);
2030
2145
  activeTurnId = extractTurnId(turnStartResult);
2146
+ session = new CodexSessionImpl({
2147
+ client,
2148
+ threadId: activeThreadId,
2149
+ turnId: activeTurnId,
2150
+ interruptTimeoutMs
2151
+ });
2152
+ this.config.providerSettings.onSessionCreated?.(session);
2031
2153
  } catch (error) {
2032
2154
  await closeWithError(error);
2033
2155
  }
2034
2156
  })();
2035
2157
  },
2036
2158
  cancel: async () => {
2159
+ session?.markInactive();
2037
2160
  try {
2038
2161
  await interruptTurnIfPossible();
2039
2162
  } catch {
@@ -2130,6 +2253,12 @@ function acquirePersistentPool(settings) {
2130
2253
  }
2131
2254
 
2132
2255
  // src/provider.ts
2256
+ var poolHandleCleanup = new FinalizationRegistry(
2257
+ (handle) => {
2258
+ void handle.release().catch(() => {
2259
+ });
2260
+ }
2261
+ );
2133
2262
  function createNoSuchModelError(modelId, modelType) {
2134
2263
  return new NoSuchModelError({ modelId, modelType });
2135
2264
  }
@@ -2150,7 +2279,7 @@ function createCodexAppServer(settings = {}) {
2150
2279
  });
2151
2280
  }
2152
2281
  const persistentPool = persistentPoolHandle?.pool ?? null;
2153
- const effectiveTransportFactory = persistentPool ? (signal) => new PersistentTransport(stripUndefined({ pool: persistentPool, signal })) : baseTransportFactory;
2282
+ const effectiveTransportFactory = persistentPool ? (signal, threadId) => new PersistentTransport(stripUndefined({ pool: persistentPool, signal, threadId })) : baseTransportFactory;
2154
2283
  const resolvedSettings = Object.freeze(stripUndefined({
2155
2284
  defaultModel: settings.defaultModel,
2156
2285
  experimentalApi: settings.experimentalApi,
@@ -2168,12 +2297,15 @@ function createCodexAppServer(settings = {}) {
2168
2297
  defaultTurnSettings: settings.defaultTurnSettings ? { ...settings.defaultTurnSettings } : void 0,
2169
2298
  compaction: settings.compaction ? { ...settings.compaction } : void 0,
2170
2299
  transportFactory: effectiveTransportFactory,
2300
+ mcpServers: settings.mcpServers ? { ...settings.mcpServers } : void 0,
2171
2301
  tools: settings.tools ? { ...settings.tools } : void 0,
2172
2302
  toolHandlers: settings.toolHandlers ? { ...settings.toolHandlers } : void 0,
2173
2303
  toolTimeoutMs: settings.toolTimeoutMs,
2174
2304
  interruptTimeoutMs: settings.interruptTimeoutMs,
2175
2305
  approvals: settings.approvals ? { ...settings.approvals } : void 0,
2176
- debug: settings.debug ? { ...settings.debug } : void 0
2306
+ debug: settings.debug ? { ...settings.debug } : void 0,
2307
+ emitPlanUpdates: settings.emitPlanUpdates,
2308
+ onSessionCreated: settings.onSessionCreated
2177
2309
  }));
2178
2310
  const createLanguageModel = (modelId, modelSettings = {}) => new CodexLanguageModel(modelId, modelSettings, {
2179
2311
  provider: CODEX_PROVIDER_ID,
@@ -2195,15 +2327,44 @@ function createCodexAppServer(settings = {}) {
2195
2327
  imageModel(modelId) {
2196
2328
  throw createNoSuchModelError(modelId, "imageModel");
2197
2329
  },
2330
+ async listModels(params) {
2331
+ const transport = effectiveTransportFactory ? effectiveTransportFactory() : resolvedSettings.transport?.type === "websocket" ? new WebSocketTransport(resolvedSettings.transport.websocket) : new StdioTransport(resolvedSettings.transport?.stdio);
2332
+ const client = new AppServerClient(transport);
2333
+ try {
2334
+ await client.connect();
2335
+ const initializeParams = stripUndefined({
2336
+ clientInfo: resolvedSettings.clientInfo ?? {
2337
+ name: PACKAGE_NAME,
2338
+ version: PACKAGE_VERSION
2339
+ }
2340
+ });
2341
+ await client.request("initialize", initializeParams);
2342
+ await client.notification("initialized");
2343
+ const models = [];
2344
+ let cursor;
2345
+ do {
2346
+ const response = await client.request("model/list", stripUndefined({ ...params, cursor }));
2347
+ models.push(...response.data);
2348
+ cursor = response.nextCursor ?? void 0;
2349
+ } while (cursor);
2350
+ return models;
2351
+ } finally {
2352
+ await client.disconnect();
2353
+ }
2354
+ },
2198
2355
  async shutdown() {
2199
2356
  if (!persistentPoolHandle) {
2200
2357
  return;
2201
2358
  }
2359
+ poolHandleCleanup.unregister(provider);
2202
2360
  const handle = persistentPoolHandle;
2203
2361
  persistentPoolHandle = null;
2204
2362
  await handle.release();
2205
2363
  }
2206
2364
  });
2365
+ if (persistentPoolHandle) {
2366
+ poolHandleCleanup.register(provider, persistentPoolHandle, provider);
2367
+ }
2207
2368
  return provider;
2208
2369
  }
2209
2370
  var codexAppServer = createCodexAppServer();