@janole/ai-sdk-provider-codex-asp 0.2.1 → 0.2.3
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/README.md +1 -1
- package/dist/index.cjs +181 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -3
- package/dist/index.d.ts +49 -3
- package/dist/index.js +181 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`@janole/ai-sdk-provider-codex-asp` is a [Vercel AI SDK](https://ai-sdk.dev/) v6 custom provider for the Codex App Server Protocol.
|
|
4
4
|
|
|
5
|
-
Status: POC feature-complete for language model usage. Currently tested with [codex-cli](https://github.com/openai/codex/releases/tag/rust-v0.
|
|
5
|
+
Status: POC feature-complete for language model usage. Currently tested with [codex-cli](https://github.com/openai/codex/releases/tag/rust-v0.106.0) 0.106.0.
|
|
6
6
|
|
|
7
7
|
- `LanguageModelV3` provider implementation
|
|
8
8
|
- Streaming (`streamText`) and non-streaming (`generateText`)
|
package/dist/index.cjs
CHANGED
|
@@ -274,6 +274,7 @@ var AppServerClient = class {
|
|
|
274
274
|
// src/client/transport-persistent.ts
|
|
275
275
|
var PersistentTransport = class {
|
|
276
276
|
pool;
|
|
277
|
+
signal;
|
|
277
278
|
worker = null;
|
|
278
279
|
pendingInitializeId = null;
|
|
279
280
|
initializeIntercepted = false;
|
|
@@ -282,9 +283,10 @@ var PersistentTransport = class {
|
|
|
282
283
|
closeListeners = /* @__PURE__ */ new Set();
|
|
283
284
|
constructor(settings) {
|
|
284
285
|
this.pool = settings.pool;
|
|
286
|
+
this.signal = settings.signal;
|
|
285
287
|
}
|
|
286
288
|
async connect() {
|
|
287
|
-
this.worker = this.pool.acquire();
|
|
289
|
+
this.worker = await this.pool.acquire(stripUndefined({ signal: this.signal }));
|
|
288
290
|
await this.worker.ensureConnected();
|
|
289
291
|
}
|
|
290
292
|
disconnect() {
|
|
@@ -724,6 +726,7 @@ var CodexWorker = class {
|
|
|
724
726
|
var CodexWorkerPool = class {
|
|
725
727
|
workers;
|
|
726
728
|
shutdownCalled = false;
|
|
729
|
+
waiters = [];
|
|
727
730
|
constructor(settings) {
|
|
728
731
|
const size = settings.poolSize ?? 1;
|
|
729
732
|
const idleTimeoutMs = settings.idleTimeoutMs ?? 3e5;
|
|
@@ -735,7 +738,7 @@ var CodexWorkerPool = class {
|
|
|
735
738
|
})
|
|
736
739
|
);
|
|
737
740
|
}
|
|
738
|
-
acquire() {
|
|
741
|
+
async acquire(options) {
|
|
739
742
|
if (this.shutdownCalled) {
|
|
740
743
|
throw new CodexProviderError("Worker pool has been shut down.");
|
|
741
744
|
}
|
|
@@ -743,20 +746,61 @@ var CodexWorkerPool = class {
|
|
|
743
746
|
(w) => w.state === "idle" || w.state === "disconnected"
|
|
744
747
|
);
|
|
745
748
|
if (!worker) {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
+
if (options?.signal?.aborted) {
|
|
750
|
+
throw new CodexProviderError("Worker acquisition aborted while waiting.");
|
|
751
|
+
}
|
|
752
|
+
return new Promise((resolve, reject) => {
|
|
753
|
+
const waiter = {
|
|
754
|
+
resolve,
|
|
755
|
+
reject,
|
|
756
|
+
signal: options?.signal,
|
|
757
|
+
abortHandler: void 0
|
|
758
|
+
};
|
|
759
|
+
if (waiter.signal) {
|
|
760
|
+
waiter.abortHandler = () => {
|
|
761
|
+
this.removeWaiter(waiter);
|
|
762
|
+
waiter.reject(new CodexProviderError("Worker acquisition aborted while waiting."));
|
|
763
|
+
};
|
|
764
|
+
waiter.signal.addEventListener("abort", waiter.abortHandler, { once: true });
|
|
765
|
+
}
|
|
766
|
+
this.waiters.push(waiter);
|
|
767
|
+
});
|
|
749
768
|
}
|
|
750
769
|
worker.acquire();
|
|
751
770
|
return worker;
|
|
752
771
|
}
|
|
753
772
|
release(worker) {
|
|
754
|
-
|
|
773
|
+
const waiter = this.waiters.shift();
|
|
774
|
+
if (waiter) {
|
|
775
|
+
this.clearWaiterAbortHandler(waiter);
|
|
776
|
+
waiter.resolve(worker);
|
|
777
|
+
} else {
|
|
778
|
+
worker.release();
|
|
779
|
+
}
|
|
755
780
|
}
|
|
756
781
|
async shutdown() {
|
|
757
782
|
this.shutdownCalled = true;
|
|
783
|
+
while (this.waiters.length > 0) {
|
|
784
|
+
const waiter = this.waiters.shift();
|
|
785
|
+
this.clearWaiterAbortHandler(waiter);
|
|
786
|
+
waiter.reject(new CodexProviderError("Worker pool has been shut down."));
|
|
787
|
+
}
|
|
758
788
|
await Promise.all(this.workers.map((w) => w.shutdown()));
|
|
759
789
|
}
|
|
790
|
+
removeWaiter(target) {
|
|
791
|
+
const index = this.waiters.indexOf(target);
|
|
792
|
+
if (index >= 0) {
|
|
793
|
+
this.waiters.splice(index, 1);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/** Remove the abort listener so it doesn't fire after the waiter is already served. */
|
|
797
|
+
clearWaiterAbortHandler(waiter) {
|
|
798
|
+
if (!waiter.signal || !waiter.abortHandler) {
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
waiter.signal.removeEventListener("abort", waiter.abortHandler);
|
|
802
|
+
waiter.abortHandler = void 0;
|
|
803
|
+
}
|
|
760
804
|
};
|
|
761
805
|
|
|
762
806
|
// src/dynamic-tools.ts
|
|
@@ -886,7 +930,7 @@ var DynamicToolsDispatcher = class {
|
|
|
886
930
|
// package.json
|
|
887
931
|
var package_default = {
|
|
888
932
|
name: "@janole/ai-sdk-provider-codex-asp",
|
|
889
|
-
version: "0.2.
|
|
933
|
+
version: "0.2.3"};
|
|
890
934
|
|
|
891
935
|
// src/package-info.ts
|
|
892
936
|
var PACKAGE_NAME = package_default.name;
|
|
@@ -932,15 +976,28 @@ function toFinishReason(status) {
|
|
|
932
976
|
}
|
|
933
977
|
}
|
|
934
978
|
var CodexEventMapper = class {
|
|
979
|
+
options;
|
|
935
980
|
streamStarted = false;
|
|
936
981
|
openTextParts = /* @__PURE__ */ new Set();
|
|
982
|
+
textDeltaReceived = /* @__PURE__ */ new Set();
|
|
937
983
|
openReasoningParts = /* @__PURE__ */ new Set();
|
|
938
984
|
openToolCalls = /* @__PURE__ */ new Map();
|
|
985
|
+
planSequenceByTurnId = /* @__PURE__ */ new Map();
|
|
939
986
|
threadId;
|
|
940
987
|
latestUsage;
|
|
988
|
+
constructor(options) {
|
|
989
|
+
this.options = {
|
|
990
|
+
emitPlanUpdates: options?.emitPlanUpdates ?? true
|
|
991
|
+
};
|
|
992
|
+
}
|
|
941
993
|
setThreadId(threadId) {
|
|
942
994
|
this.threadId = threadId;
|
|
943
995
|
}
|
|
996
|
+
nextPlanSequence(turnId) {
|
|
997
|
+
const next = (this.planSequenceByTurnId.get(turnId) ?? 0) + 1;
|
|
998
|
+
this.planSequenceByTurnId.set(turnId, next);
|
|
999
|
+
return next;
|
|
1000
|
+
}
|
|
944
1001
|
map(event) {
|
|
945
1002
|
const parts = [];
|
|
946
1003
|
const withMeta = (part) => withProviderMetadata(part, this.threadId);
|
|
@@ -1020,6 +1077,7 @@ var CodexEventMapper = class {
|
|
|
1020
1077
|
parts.push(withMeta({ type: "text-start", id: delta.itemId }));
|
|
1021
1078
|
}
|
|
1022
1079
|
parts.push(withMeta({ type: "text-delta", id: delta.itemId, delta: delta.delta }));
|
|
1080
|
+
this.textDeltaReceived.add(delta.itemId);
|
|
1023
1081
|
break;
|
|
1024
1082
|
}
|
|
1025
1083
|
case "item/completed": {
|
|
@@ -1028,9 +1086,19 @@ var CodexEventMapper = class {
|
|
|
1028
1086
|
if (!item?.id) {
|
|
1029
1087
|
break;
|
|
1030
1088
|
}
|
|
1031
|
-
if (item.type === "agentMessage"
|
|
1032
|
-
|
|
1033
|
-
|
|
1089
|
+
if (item.type === "agentMessage") {
|
|
1090
|
+
if (!this.textDeltaReceived.has(item.id) && item.text) {
|
|
1091
|
+
pushStreamStart();
|
|
1092
|
+
if (!this.openTextParts.has(item.id)) {
|
|
1093
|
+
this.openTextParts.add(item.id);
|
|
1094
|
+
parts.push(withMeta({ type: "text-start", id: item.id }));
|
|
1095
|
+
}
|
|
1096
|
+
parts.push(withMeta({ type: "text-delta", id: item.id, delta: item.text }));
|
|
1097
|
+
}
|
|
1098
|
+
if (this.openTextParts.has(item.id)) {
|
|
1099
|
+
parts.push(withMeta({ type: "text-end", id: item.id }));
|
|
1100
|
+
this.openTextParts.delete(item.id);
|
|
1101
|
+
}
|
|
1034
1102
|
} else if (item.type === "commandExecution" && this.openToolCalls.has(item.id)) {
|
|
1035
1103
|
const tracked = this.openToolCalls.get(item.id);
|
|
1036
1104
|
const output = item.aggregatedOutput ?? tracked.output;
|
|
@@ -1059,6 +1127,64 @@ var CodexEventMapper = class {
|
|
|
1059
1127
|
}
|
|
1060
1128
|
break;
|
|
1061
1129
|
}
|
|
1130
|
+
case "item/reasoning/summaryPartAdded": {
|
|
1131
|
+
const params = event.params ?? {};
|
|
1132
|
+
if (params.itemId) {
|
|
1133
|
+
pushReasoningDelta(params.itemId, "\n\n");
|
|
1134
|
+
}
|
|
1135
|
+
break;
|
|
1136
|
+
}
|
|
1137
|
+
// codex/event/agent_reasoning mirrors canonical reasoning summary
|
|
1138
|
+
// stream events in current logs. Ignore wrapper to avoid duplicate
|
|
1139
|
+
// reasoning text in consumers.
|
|
1140
|
+
case "codex/event/agent_reasoning":
|
|
1141
|
+
break;
|
|
1142
|
+
// codex/event/agent_reasoning_section_break is the wrapper form of
|
|
1143
|
+
// item/reasoning/summaryPartAdded (identical 1:1). Handled by the
|
|
1144
|
+
// canonical event above — skip the wrapper to avoid double "\n\n".
|
|
1145
|
+
case "codex/event/agent_reasoning_section_break":
|
|
1146
|
+
break;
|
|
1147
|
+
case "turn/plan/updated": {
|
|
1148
|
+
if (!this.options.emitPlanUpdates) {
|
|
1149
|
+
break;
|
|
1150
|
+
}
|
|
1151
|
+
const params = event.params ?? {};
|
|
1152
|
+
const turnId = params.turnId;
|
|
1153
|
+
const plan = params.plan;
|
|
1154
|
+
if (turnId && plan) {
|
|
1155
|
+
pushStreamStart();
|
|
1156
|
+
const planSequence = this.nextPlanSequence(turnId);
|
|
1157
|
+
const toolCallId = `plan:${turnId}:${planSequence}`;
|
|
1158
|
+
const toolName = "codex_plan_update";
|
|
1159
|
+
parts.push(withMeta({
|
|
1160
|
+
type: "tool-call",
|
|
1161
|
+
toolCallId,
|
|
1162
|
+
toolName,
|
|
1163
|
+
input: JSON.stringify({}),
|
|
1164
|
+
providerExecuted: true,
|
|
1165
|
+
dynamic: true
|
|
1166
|
+
}));
|
|
1167
|
+
parts.push(withMeta({
|
|
1168
|
+
type: "tool-result",
|
|
1169
|
+
toolCallId,
|
|
1170
|
+
toolName,
|
|
1171
|
+
result: { plan, explanation: params.explanation ?? void 0 }
|
|
1172
|
+
}));
|
|
1173
|
+
}
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1176
|
+
// codex/event/plan_update is the wrapper form of turn/plan/updated (1:1).
|
|
1177
|
+
case "codex/event/plan_update":
|
|
1178
|
+
break;
|
|
1179
|
+
// NOTE: turn/diff/updated and codex/event/turn_diff are intentionally
|
|
1180
|
+
// NOT mapped. They carry full unified diffs (often 50-100 KB) which,
|
|
1181
|
+
// when emitted as reasoning deltas, crash or freeze the frontend
|
|
1182
|
+
// markdown renderer. If these need to surface in the UI, they should
|
|
1183
|
+
// use a dedicated part type with lazy/collapsed rendering — not
|
|
1184
|
+
// reasoning text.
|
|
1185
|
+
case "turn/diff/updated":
|
|
1186
|
+
case "codex/event/turn_diff":
|
|
1187
|
+
break;
|
|
1062
1188
|
case "item/commandExecution/outputDelta": {
|
|
1063
1189
|
const delta = event.params ?? {};
|
|
1064
1190
|
if (delta.itemId && delta.delta && this.openToolCalls.has(delta.itemId)) {
|
|
@@ -1074,6 +1200,43 @@ var CodexEventMapper = class {
|
|
|
1074
1200
|
}
|
|
1075
1201
|
break;
|
|
1076
1202
|
}
|
|
1203
|
+
case "codex/event/mcp_tool_call_begin": {
|
|
1204
|
+
const params = event.params ?? {};
|
|
1205
|
+
const callId = params.msg?.call_id;
|
|
1206
|
+
const inv = params.msg?.invocation;
|
|
1207
|
+
if (callId && inv) {
|
|
1208
|
+
pushStreamStart();
|
|
1209
|
+
const toolName = `mcp:${inv.server}/${inv.tool}`;
|
|
1210
|
+
this.openToolCalls.set(callId, { toolName, output: "" });
|
|
1211
|
+
parts.push(withMeta({
|
|
1212
|
+
type: "tool-call",
|
|
1213
|
+
toolCallId: callId,
|
|
1214
|
+
toolName,
|
|
1215
|
+
input: JSON.stringify(inv.arguments ?? {}),
|
|
1216
|
+
providerExecuted: true,
|
|
1217
|
+
dynamic: true
|
|
1218
|
+
}));
|
|
1219
|
+
}
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
case "codex/event/mcp_tool_call_end": {
|
|
1223
|
+
const params = event.params ?? {};
|
|
1224
|
+
const callId = params.msg?.call_id;
|
|
1225
|
+
if (callId && this.openToolCalls.has(callId)) {
|
|
1226
|
+
const tracked = this.openToolCalls.get(callId);
|
|
1227
|
+
const result = params.msg?.result;
|
|
1228
|
+
const textParts = result?.Ok?.content?.filter((c) => c.type === "text").map((c) => c.text) ?? [];
|
|
1229
|
+
const output = textParts.join("\n") || (result?.Err ? JSON.stringify(result.Err) : "");
|
|
1230
|
+
parts.push(withMeta({
|
|
1231
|
+
type: "tool-result",
|
|
1232
|
+
toolCallId: callId,
|
|
1233
|
+
toolName: tracked.toolName,
|
|
1234
|
+
result: { output }
|
|
1235
|
+
}));
|
|
1236
|
+
this.openToolCalls.delete(callId);
|
|
1237
|
+
}
|
|
1238
|
+
break;
|
|
1239
|
+
}
|
|
1077
1240
|
case "item/mcpToolCall/progress": {
|
|
1078
1241
|
const params = event.params ?? {};
|
|
1079
1242
|
if (params.itemId && params.message) {
|
|
@@ -1143,6 +1306,9 @@ var CodexEventMapper = class {
|
|
|
1143
1306
|
}
|
|
1144
1307
|
this.openToolCalls.clear();
|
|
1145
1308
|
const completed = event.params ?? {};
|
|
1309
|
+
if (completed.turn?.id) {
|
|
1310
|
+
this.planSequenceByTurnId.delete(completed.turn.id);
|
|
1311
|
+
}
|
|
1146
1312
|
const usage = this.latestUsage ?? EMPTY_USAGE;
|
|
1147
1313
|
parts.push(withMeta({ type: "finish", finishReason: toFinishReason(completed.turn?.status), usage }));
|
|
1148
1314
|
break;
|
|
@@ -1433,7 +1599,7 @@ var CodexLanguageModel = class {
|
|
|
1433
1599
|
});
|
|
1434
1600
|
}
|
|
1435
1601
|
doStream(options) {
|
|
1436
|
-
const transport = this.config.providerSettings.transportFactory ? this.config.providerSettings.transportFactory() : this.config.providerSettings.transport?.type === "websocket" ? new WebSocketTransport(this.config.providerSettings.transport.websocket) : new StdioTransport(this.config.providerSettings.transport?.stdio);
|
|
1602
|
+
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);
|
|
1437
1603
|
const packetLogger = this.config.providerSettings.debug?.logPackets === true ? this.config.providerSettings.debug.logger ?? ((packet) => {
|
|
1438
1604
|
if (packet.direction === "inbound") {
|
|
1439
1605
|
console.debug("[codex packet]", packet.message);
|
|
@@ -1448,7 +1614,9 @@ var CodexLanguageModel = class {
|
|
|
1448
1614
|
const client = new AppServerClient(transport, stripUndefined({
|
|
1449
1615
|
onPacket: packetLogger
|
|
1450
1616
|
}));
|
|
1451
|
-
const mapper = new CodexEventMapper(
|
|
1617
|
+
const mapper = new CodexEventMapper(stripUndefined({
|
|
1618
|
+
emitPlanUpdates: this.config.providerSettings.emitPlanUpdates
|
|
1619
|
+
}));
|
|
1452
1620
|
let activeThreadId;
|
|
1453
1621
|
let activeTurnId;
|
|
1454
1622
|
const interruptTimeoutMs = this.config.providerSettings.interruptTimeoutMs ?? 1e4;
|
|
@@ -1845,7 +2013,7 @@ function createCodexAppServer(settings = {}) {
|
|
|
1845
2013
|
});
|
|
1846
2014
|
}
|
|
1847
2015
|
const persistentPool = persistentPoolHandle?.pool ?? null;
|
|
1848
|
-
const effectiveTransportFactory = persistentPool ? () => new PersistentTransport({ pool: persistentPool }) : baseTransportFactory;
|
|
2016
|
+
const effectiveTransportFactory = persistentPool ? (signal) => new PersistentTransport(stripUndefined({ pool: persistentPool, signal })) : baseTransportFactory;
|
|
1849
2017
|
const resolvedSettings = Object.freeze(stripUndefined({
|
|
1850
2018
|
defaultModel: settings.defaultModel,
|
|
1851
2019
|
experimentalApi: settings.experimentalApi,
|