@janole/ai-sdk-provider-codex-asp 0.2.3 → 0.2.4

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.cjs CHANGED
@@ -1,6 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var child_process = require('child_process');
4
+ var crypto = require('crypto');
5
+ var promises = require('fs/promises');
6
+ var os = require('os');
7
+ var path = require('path');
8
+ var url = require('url');
4
9
  var provider = require('@ai-sdk/provider');
5
10
 
6
11
  // src/utils/object.ts
@@ -930,7 +935,7 @@ var DynamicToolsDispatcher = class {
930
935
  // package.json
931
936
  var package_default = {
932
937
  name: "@janole/ai-sdk-provider-codex-asp",
933
- version: "0.2.3"};
938
+ version: "0.2.4"};
934
939
 
935
940
  // src/package-info.ts
936
941
  var PACKAGE_NAME = package_default.name;
@@ -1317,8 +1322,6 @@ var CodexEventMapper = class {
1317
1322
  return parts;
1318
1323
  }
1319
1324
  };
1320
-
1321
- // src/protocol/prompt-mapper.ts
1322
1325
  function mapSystemPrompt(prompt) {
1323
1326
  const chunks = [];
1324
1327
  for (const message of prompt) {
@@ -1331,8 +1334,111 @@ function mapSystemPrompt(prompt) {
1331
1334
  }
1332
1335
  return chunks.length > 0 ? chunks.join("\n\n") : void 0;
1333
1336
  }
1334
- function mapPromptToTurnInput(prompt, isResume = false) {
1335
- if (isResume) {
1337
+ function textItem(text) {
1338
+ return { type: "text", text, text_elements: [] };
1339
+ }
1340
+ var MEDIA_TYPE_TO_EXT = {
1341
+ "image/png": ".png",
1342
+ "image/jpeg": ".jpg",
1343
+ "image/gif": ".gif",
1344
+ "image/webp": ".webp",
1345
+ "image/svg+xml": ".svg",
1346
+ "image/bmp": ".bmp",
1347
+ "image/tiff": ".tiff"
1348
+ };
1349
+ function extensionForMediaType(mediaType) {
1350
+ return MEDIA_TYPE_TO_EXT[mediaType] ?? ".bin";
1351
+ }
1352
+ var LocalFileWriter = class {
1353
+ async write(data, mediaType) {
1354
+ const ext = extensionForMediaType(mediaType);
1355
+ const filename = `codex-ai-sdk-${crypto.randomUUID()}${ext}`;
1356
+ const filepath = path.join(os.tmpdir(), filename);
1357
+ const buffer = typeof data === "string" ? Buffer.from(data, "base64") : data;
1358
+ await promises.writeFile(filepath, buffer);
1359
+ return url.pathToFileURL(filepath);
1360
+ }
1361
+ async cleanup(urls) {
1362
+ await Promise.allSettled(
1363
+ urls.filter((u) => u.protocol === "file:").map((u) => promises.unlink(u))
1364
+ );
1365
+ }
1366
+ };
1367
+ var PromptFileResolver = class {
1368
+ writer;
1369
+ written = [];
1370
+ constructor(writer) {
1371
+ this.writer = writer ?? new LocalFileWriter();
1372
+ }
1373
+ /**
1374
+ * Resolve inline file data and map user content to Codex input items.
1375
+ *
1376
+ * - Inline image data (base64 / Uint8Array) is written via the
1377
+ * {@link FileWriter} and converted to `localImage` or `image` items.
1378
+ * - URL-based image file parts are converted directly.
1379
+ * - Inline text file data is decoded and inlined as text.
1380
+ * - Unsupported media types are silently skipped.
1381
+ *
1382
+ * @param isResume - When true only the last user message is extracted.
1383
+ * When false (fresh thread) all user text is accumulated with images
1384
+ * flushing the text buffer to preserve ordering.
1385
+ */
1386
+ async resolve(prompt, isResume = false) {
1387
+ if (isResume) {
1388
+ return this.resolveResumed(prompt);
1389
+ }
1390
+ return this.resolveFresh(prompt);
1391
+ }
1392
+ /**
1393
+ * Remove all files created by previous {@link resolve} calls.
1394
+ * Best-effort — never throws.
1395
+ */
1396
+ async cleanup() {
1397
+ const urls = this.written.splice(0);
1398
+ if (urls.length > 0) {
1399
+ await this.writer.cleanup(urls);
1400
+ }
1401
+ }
1402
+ /**
1403
+ * Convert a resolved image URL to a Codex input item.
1404
+ */
1405
+ mapImageUrl(mediaType, data) {
1406
+ if (!mediaType.startsWith("image/")) {
1407
+ return null;
1408
+ }
1409
+ if (data.protocol === "file:") {
1410
+ return { type: "localImage", path: url.fileURLToPath(data) };
1411
+ }
1412
+ return { type: "image", url: data.href };
1413
+ }
1414
+ /**
1415
+ * Resolve a single file part: write inline data via the writer, then
1416
+ * convert to a Codex input item. Text files are decoded and returned
1417
+ * as text items. Returns `null` for unsupported media types.
1418
+ */
1419
+ async resolveFilePart(part) {
1420
+ const { mediaType, data } = part;
1421
+ if (mediaType.startsWith("text/")) {
1422
+ if (data instanceof URL) {
1423
+ return textItem(data.href);
1424
+ }
1425
+ const text = typeof data === "string" ? Buffer.from(data, "base64").toString("utf-8") : new TextDecoder().decode(data);
1426
+ return textItem(text);
1427
+ }
1428
+ if (mediaType.startsWith("image/") && !(data instanceof URL)) {
1429
+ const url = await this.writer.write(data, mediaType);
1430
+ this.written.push(url);
1431
+ return this.mapImageUrl(mediaType, url);
1432
+ }
1433
+ if (data instanceof URL) {
1434
+ return this.mapImageUrl(mediaType, data);
1435
+ }
1436
+ return null;
1437
+ }
1438
+ /**
1439
+ * Resume path: extract parts from the last user message individually.
1440
+ */
1441
+ async resolveResumed(prompt) {
1336
1442
  for (let i = prompt.length - 1; i >= 0; i--) {
1337
1443
  const message = prompt[i];
1338
1444
  if (message?.role === "user") {
@@ -1341,7 +1447,12 @@ function mapPromptToTurnInput(prompt, isResume = false) {
1341
1447
  if (part.type === "text") {
1342
1448
  const text = part.text.trim();
1343
1449
  if (text.length > 0) {
1344
- items.push({ type: "text", text, text_elements: [] });
1450
+ items.push(textItem(text));
1451
+ }
1452
+ } else if (part.type === "file") {
1453
+ const mapped = await this.resolveFilePart(part);
1454
+ if (mapped) {
1455
+ items.push(mapped);
1345
1456
  }
1346
1457
  }
1347
1458
  }
@@ -1350,21 +1461,45 @@ function mapPromptToTurnInput(prompt, isResume = false) {
1350
1461
  }
1351
1462
  return [];
1352
1463
  }
1353
- const chunks = [];
1354
- for (const message of prompt) {
1355
- if (message.role === "user") {
1356
- for (const part of message.content) {
1357
- if (part.type === "text") {
1358
- const text = part.text.trim();
1359
- if (text.length > 0) {
1360
- chunks.push(text);
1464
+ /**
1465
+ * Fresh thread path: accumulate text chunks across all user messages,
1466
+ * flushing before each image to preserve ordering.
1467
+ */
1468
+ async resolveFresh(prompt) {
1469
+ const items = [];
1470
+ const textChunks = [];
1471
+ const flushText = () => {
1472
+ if (textChunks.length > 0) {
1473
+ items.push(textItem(textChunks.join("\n\n")));
1474
+ textChunks.length = 0;
1475
+ }
1476
+ };
1477
+ for (const message of prompt) {
1478
+ if (message.role === "user") {
1479
+ for (const part of message.content) {
1480
+ if (part.type === "text") {
1481
+ const text = part.text.trim();
1482
+ if (text.length > 0) {
1483
+ textChunks.push(text);
1484
+ }
1485
+ } else if (part.type === "file") {
1486
+ const mapped = await this.resolveFilePart(part);
1487
+ if (mapped) {
1488
+ if (mapped.type === "text") {
1489
+ textChunks.push(mapped.text);
1490
+ } else {
1491
+ flushText();
1492
+ items.push(mapped);
1493
+ }
1494
+ }
1361
1495
  }
1362
1496
  }
1363
1497
  }
1364
1498
  }
1499
+ flushText();
1500
+ return items;
1365
1501
  }
1366
- return [{ type: "text", text: chunks.join("\n\n"), text_elements: [] }];
1367
- }
1502
+ };
1368
1503
 
1369
1504
  // src/model.ts
1370
1505
  function createEmptyUsage() {
@@ -1631,6 +1766,7 @@ var CodexLanguageModel = class {
1631
1766
  debugLog?.("outbound", "turn/interrupt", interruptParams);
1632
1767
  await client.request("turn/interrupt", interruptParams, interruptTimeoutMs);
1633
1768
  };
1769
+ const fileResolver = new PromptFileResolver();
1634
1770
  const stream = new ReadableStream({
1635
1771
  start: (controller) => {
1636
1772
  let closed = false;
@@ -1643,6 +1779,7 @@ var CodexLanguageModel = class {
1643
1779
  try {
1644
1780
  controller.close();
1645
1781
  } finally {
1782
+ await fileResolver.cleanup();
1646
1783
  await client.disconnect();
1647
1784
  }
1648
1785
  };
@@ -1654,6 +1791,7 @@ var CodexLanguageModel = class {
1654
1791
  try {
1655
1792
  controller.close();
1656
1793
  } finally {
1794
+ await fileResolver.cleanup();
1657
1795
  await client.disconnect();
1658
1796
  }
1659
1797
  };
@@ -1878,7 +2016,7 @@ var CodexLanguageModel = class {
1878
2016
  closeSuccessfully
1879
2017
  );
1880
2018
  }
1881
- const turnInput = mapPromptToTurnInput(options.prompt, !!resumeThreadId);
2019
+ const turnInput = await fileResolver.resolve(options.prompt, !!resumeThreadId);
1882
2020
  const turnStartParams = stripUndefined({
1883
2021
  threadId,
1884
2022
  input: turnInput,
@@ -1902,6 +2040,7 @@ var CodexLanguageModel = class {
1902
2040
  await interruptTurnIfPossible();
1903
2041
  } catch {
1904
2042
  }
2043
+ await fileResolver.cleanup();
1905
2044
  await client.disconnect();
1906
2045
  }
1907
2046
  });
@@ -2083,16 +2222,17 @@ exports.CodexWorker = CodexWorker;
2083
2222
  exports.CodexWorkerPool = CodexWorkerPool;
2084
2223
  exports.DynamicToolsDispatcher = DynamicToolsDispatcher;
2085
2224
  exports.JsonRpcError = JsonRpcError;
2225
+ exports.LocalFileWriter = LocalFileWriter;
2086
2226
  exports.PACKAGE_NAME = PACKAGE_NAME;
2087
2227
  exports.PACKAGE_VERSION = PACKAGE_VERSION;
2088
2228
  exports.PersistentTransport = PersistentTransport;
2229
+ exports.PromptFileResolver = PromptFileResolver;
2089
2230
  exports.StdioTransport = StdioTransport;
2090
2231
  exports.WebSocketTransport = WebSocketTransport;
2091
2232
  exports.codexAppServer = codexAppServer;
2092
2233
  exports.codexProviderMetadata = codexProviderMetadata;
2093
2234
  exports.createCodexAppServer = createCodexAppServer;
2094
2235
  exports.createCodexProvider = createCodexProvider;
2095
- exports.mapPromptToTurnInput = mapPromptToTurnInput;
2096
2236
  exports.mapSystemPrompt = mapSystemPrompt;
2097
2237
  exports.withProviderMetadata = withProviderMetadata;
2098
2238
  //# sourceMappingURL=index.cjs.map