@love-moon/ai-sdk 0.2.30 → 0.2.32
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/client.d.ts +52 -7
- package/dist/client.js +245 -14
- package/dist/external-provider-registry.d.ts +4 -0
- package/dist/external-provider-registry.js +143 -0
- package/dist/providers/claude-agent-sdk-session.d.ts +9 -0
- package/dist/providers/claude-agent-sdk-session.js +78 -10
- package/dist/providers/codex-app-server-session.d.ts +12 -2
- package/dist/providers/codex-app-server-session.js +108 -11
- package/dist/providers/kimi-cli-session.d.ts +8 -0
- package/dist/providers/kimi-cli-session.js +80 -7
- package/dist/providers/opencode-sdk-session.d.ts +10 -0
- package/dist/providers/opencode-sdk-session.js +104 -13
- package/dist/session-factory.d.ts +5 -5
- package/dist/session-factory.js +42 -12
- package/dist/worker.js +1 -1
- package/package.json +2 -2
|
@@ -154,6 +154,9 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
154
154
|
this.currentTurn = null;
|
|
155
155
|
this.lastUsage = null;
|
|
156
156
|
this.lastAssistantInfo = null;
|
|
157
|
+
this.currentTurnStatus = null;
|
|
158
|
+
this.currentTurnActivityAt = 0;
|
|
159
|
+
this.now = typeof options.now === "function" ? options.now : () => Date.now();
|
|
157
160
|
this.turnDeadlineMs = getBoundedEnvInt("CONDUCTOR_TURN_DEADLINE_MS", DEFAULT_TURN_DEADLINE_MS, MIN_TURN_DEADLINE_MS, MAX_TURN_DEADLINE_MS);
|
|
158
161
|
this.client = null;
|
|
159
162
|
this.sdkModulePromise = null;
|
|
@@ -200,10 +203,14 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
200
203
|
return this.sessionId;
|
|
201
204
|
}
|
|
202
205
|
get threadOptions() {
|
|
203
|
-
const model =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
206
|
+
const model = this.sessionInfo?.model ||
|
|
207
|
+
(typeof this.options.model === "string" && this.options.model.trim()
|
|
208
|
+
? this.options.model.trim()
|
|
209
|
+
: this.backend);
|
|
210
|
+
return {
|
|
211
|
+
model,
|
|
212
|
+
modelProvider: this.sessionInfo?.modelProvider || undefined,
|
|
213
|
+
};
|
|
207
214
|
}
|
|
208
215
|
getSnapshot() {
|
|
209
216
|
return {
|
|
@@ -215,12 +222,16 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
215
222
|
useSessionFileReplyStream: this.usesSessionFileReplyStream(),
|
|
216
223
|
resumeReady: Boolean(this.sessionId),
|
|
217
224
|
manualResume: null,
|
|
225
|
+
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
218
226
|
pid: this.transport.pid || undefined,
|
|
219
227
|
};
|
|
220
228
|
}
|
|
221
229
|
getSessionInfo() {
|
|
222
230
|
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
223
231
|
}
|
|
232
|
+
getCurrentTurnStatus() {
|
|
233
|
+
return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
|
|
234
|
+
}
|
|
224
235
|
async ensureSessionInfo() {
|
|
225
236
|
await this.boot();
|
|
226
237
|
return this.getSessionInfo();
|
|
@@ -256,6 +267,39 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
256
267
|
getCurrentReplyTarget() {
|
|
257
268
|
return this.activeReplyTarget || this.lastReplyTarget || undefined;
|
|
258
269
|
}
|
|
270
|
+
touchTurnActivity() {
|
|
271
|
+
this.currentTurnActivityAt = this.now();
|
|
272
|
+
}
|
|
273
|
+
updateCurrentTurnStatus(payload) {
|
|
274
|
+
const updatedAtMs = this.now();
|
|
275
|
+
this.currentTurnActivityAt = updatedAtMs;
|
|
276
|
+
this.currentTurnStatus = {
|
|
277
|
+
...payload,
|
|
278
|
+
updated_at: new Date(updatedAtMs).toISOString(),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
markTurnStartedStatus() {
|
|
282
|
+
this.updateCurrentTurnStatus({
|
|
283
|
+
source: OPENCODE_PROVIDER_VARIANT,
|
|
284
|
+
reply_in_progress: true,
|
|
285
|
+
replyTo: this.getCurrentReplyTarget(),
|
|
286
|
+
phase: "turn_started",
|
|
287
|
+
status_line: "opencode is working",
|
|
288
|
+
thread_id: this.sessionId || undefined,
|
|
289
|
+
session_id: this.sessionId || undefined,
|
|
290
|
+
session_file_path: undefined,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
async failPendingTurnStart(error, onProgress = null) {
|
|
294
|
+
if (this.closeRequested || error?.reason === "session_closed") {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
await this.emitWorkingStatus({
|
|
298
|
+
phase: "turn_failed",
|
|
299
|
+
reply_in_progress: false,
|
|
300
|
+
status_done_line: error instanceof Error ? error.message : String(error),
|
|
301
|
+
}, onProgress);
|
|
302
|
+
}
|
|
259
303
|
async emitWorkingStatus(payload, onProgress = null) {
|
|
260
304
|
const normalized = {
|
|
261
305
|
source: OPENCODE_PROVIDER_VARIANT,
|
|
@@ -270,15 +314,18 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
270
314
|
session_id: this.sessionId || undefined,
|
|
271
315
|
session_file_path: undefined,
|
|
272
316
|
};
|
|
317
|
+
this.updateCurrentTurnStatus(normalized);
|
|
318
|
+
const snapshot = this.getCurrentTurnStatus();
|
|
273
319
|
if (typeof onProgress === "function") {
|
|
274
|
-
onProgress(
|
|
320
|
+
onProgress(snapshot);
|
|
275
321
|
}
|
|
276
322
|
if (typeof this.workingStatusHandler === "function") {
|
|
277
|
-
await this.workingStatusHandler(
|
|
323
|
+
await this.workingStatusHandler(snapshot);
|
|
278
324
|
}
|
|
279
|
-
this.emit("working_status",
|
|
325
|
+
this.emit("working_status", snapshot);
|
|
280
326
|
}
|
|
281
327
|
async emitAssistantMessage(text) {
|
|
328
|
+
this.touchTurnActivity();
|
|
282
329
|
const payload = {
|
|
283
330
|
text,
|
|
284
331
|
preserveWhitespace: true,
|
|
@@ -352,8 +399,27 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
352
399
|
};
|
|
353
400
|
}
|
|
354
401
|
let timer = null;
|
|
355
|
-
|
|
402
|
+
let settled = false;
|
|
403
|
+
const schedule = (reject) => {
|
|
404
|
+
const now = this.now();
|
|
405
|
+
const lastActivityAt = Number.isFinite(this.currentTurnActivityAt) && this.currentTurnActivityAt > 0
|
|
406
|
+
? this.currentTurnActivityAt
|
|
407
|
+
: now;
|
|
408
|
+
const elapsedMs = Math.max(0, now - lastActivityAt);
|
|
409
|
+
const waitMs = Math.max(1, this.turnDeadlineMs - elapsedMs);
|
|
356
410
|
timer = setTimeout(() => {
|
|
411
|
+
if (settled) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const activityNow = this.now();
|
|
415
|
+
const latestActivityAt = Number.isFinite(this.currentTurnActivityAt) && this.currentTurnActivityAt > 0
|
|
416
|
+
? this.currentTurnActivityAt
|
|
417
|
+
: activityNow;
|
|
418
|
+
if (activityNow - latestActivityAt < this.turnDeadlineMs) {
|
|
419
|
+
schedule(reject);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
settled = true;
|
|
357
423
|
try {
|
|
358
424
|
onTimeout?.();
|
|
359
425
|
}
|
|
@@ -361,14 +427,18 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
361
427
|
// best effort
|
|
362
428
|
}
|
|
363
429
|
reject(this.createTurnTimeoutError(this.turnDeadlineMs));
|
|
364
|
-
},
|
|
365
|
-
if (typeof timer
|
|
430
|
+
}, waitMs);
|
|
431
|
+
if (typeof timer?.unref === "function") {
|
|
366
432
|
timer.unref();
|
|
367
433
|
}
|
|
434
|
+
};
|
|
435
|
+
const promise = new Promise((_, reject) => {
|
|
436
|
+
schedule(reject);
|
|
368
437
|
});
|
|
369
438
|
return {
|
|
370
439
|
promise,
|
|
371
440
|
cleanup: () => {
|
|
441
|
+
settled = true;
|
|
372
442
|
if (timer) {
|
|
373
443
|
clearTimeout(timer);
|
|
374
444
|
}
|
|
@@ -536,9 +606,20 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
536
606
|
}
|
|
537
607
|
const changed = this.sessionId !== normalizedSessionId;
|
|
538
608
|
this.sessionId = normalizedSessionId;
|
|
609
|
+
const resolvedModel = typeof this.lastAssistantInfo?.model?.modelID === "string" && this.lastAssistantInfo.model.modelID.trim()
|
|
610
|
+
? this.lastAssistantInfo.model.modelID.trim()
|
|
611
|
+
: typeof this.options.model === "string" && this.options.model.trim()
|
|
612
|
+
? this.options.model.trim()
|
|
613
|
+
: undefined;
|
|
614
|
+
const resolvedModelProvider = typeof this.lastAssistantInfo?.model?.providerID === "string" && this.lastAssistantInfo.model.providerID.trim()
|
|
615
|
+
? this.lastAssistantInfo.model.providerID.trim()
|
|
616
|
+
: undefined;
|
|
539
617
|
this.sessionInfo = {
|
|
618
|
+
...(this.sessionInfo || {}),
|
|
540
619
|
backend: this.backend,
|
|
541
620
|
sessionId: normalizedSessionId,
|
|
621
|
+
model: resolvedModel,
|
|
622
|
+
modelProvider: resolvedModelProvider,
|
|
542
623
|
};
|
|
543
624
|
if (changed) {
|
|
544
625
|
this.trace(`session ready id=${normalizedSessionId}`);
|
|
@@ -874,6 +955,9 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
874
955
|
this.activeReplyTarget = "";
|
|
875
956
|
this.lastUsage = this.buildUsageFromAssistantInfo(currentTurn.lastAssistantInfo);
|
|
876
957
|
this.lastAssistantInfo = currentTurn.lastAssistantInfo || null;
|
|
958
|
+
this.applySessionInfo({
|
|
959
|
+
id: this.sessionId,
|
|
960
|
+
});
|
|
877
961
|
await this.emitTerminalWorkingStatus(currentTurn, {
|
|
878
962
|
phase: "turn_completed",
|
|
879
963
|
status_done_line: "opencode finished",
|
|
@@ -1058,14 +1142,21 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
1058
1142
|
if (!effectivePrompt) {
|
|
1059
1143
|
return buildEmptyTurnResult();
|
|
1060
1144
|
}
|
|
1061
|
-
await this.boot();
|
|
1062
1145
|
if (this.currentTurn) {
|
|
1063
1146
|
throw createTurnError("Opencode turn already running", {
|
|
1064
1147
|
reason: "turn_already_running",
|
|
1065
1148
|
});
|
|
1066
1149
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1150
|
+
this.markTurnStartedStatus();
|
|
1151
|
+
try {
|
|
1152
|
+
await this.boot();
|
|
1153
|
+
if (!this.client?.session || typeof this.client.session.promptAsync !== "function") {
|
|
1154
|
+
throw new Error("Opencode session client is unavailable");
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
catch (error) {
|
|
1158
|
+
await this.failPendingTurnStart(error, onProgress);
|
|
1159
|
+
throw error;
|
|
1069
1160
|
}
|
|
1070
1161
|
this.history.push({ role: "user", content: promptText });
|
|
1071
1162
|
const abortController = new AbortController();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export function normalizeBackend(backend: any):
|
|
2
|
-
export function isSupportedBackend(backend: any): boolean
|
|
3
|
-
export function providerVariantForBackend(backend: any):
|
|
4
|
-
export function assertSupportedBackend(backend: any):
|
|
5
|
-
export function createLocalAiSession(backend: any, options?: {}):
|
|
1
|
+
export function normalizeBackend(backend: any, options?: {}): Promise<any>;
|
|
2
|
+
export function isSupportedBackend(backend: any, options?: {}): Promise<boolean>;
|
|
3
|
+
export function providerVariantForBackend(backend: any, options?: {}): Promise<any>;
|
|
4
|
+
export function assertSupportedBackend(backend: any, options?: {}): Promise<any>;
|
|
5
|
+
export function createLocalAiSession(backend: any, options?: {}): Promise<any>;
|
|
6
6
|
export const DEFAULT_PROVIDER_VARIANT: "codex-app-server";
|
|
7
7
|
export const CLAUDE_PROVIDER_VARIANT: "claude-agent-sdk";
|
|
8
8
|
export const KIMI_PROVIDER_VARIANT: "kimi-cli-wire";
|
package/dist/session-factory.js
CHANGED
|
@@ -2,11 +2,12 @@ import { CodexAppServerSession } from "./providers/codex-app-server-session.js";
|
|
|
2
2
|
import { ClaudeAgentSdkSession } from "./providers/claude-agent-sdk-session.js";
|
|
3
3
|
import { KimiCliSession } from "./providers/kimi-cli-session.js";
|
|
4
4
|
import { OpencodeSdkSession } from "./providers/opencode-sdk-session.js";
|
|
5
|
+
import { getExternalProviderDescriptor, resolveExternalBackend, } from "./external-provider-registry.js";
|
|
5
6
|
export const DEFAULT_PROVIDER_VARIANT = "codex-app-server";
|
|
6
7
|
export const CLAUDE_PROVIDER_VARIANT = "claude-agent-sdk";
|
|
7
8
|
export const KIMI_PROVIDER_VARIANT = "kimi-cli-wire";
|
|
8
9
|
export const OPENCODE_PROVIDER_VARIANT = "opencode-sdk";
|
|
9
|
-
|
|
10
|
+
function normalizeBuiltInBackendName(backend) {
|
|
10
11
|
const normalized = String(backend || "").trim().toLowerCase();
|
|
11
12
|
if (normalized === "code") {
|
|
12
13
|
return "codex";
|
|
@@ -22,12 +23,23 @@ export function normalizeBackend(backend) {
|
|
|
22
23
|
}
|
|
23
24
|
return normalized;
|
|
24
25
|
}
|
|
25
|
-
export function
|
|
26
|
-
const normalized =
|
|
27
|
-
|
|
26
|
+
export async function normalizeBackend(backend, options = {}) {
|
|
27
|
+
const normalized = normalizeBuiltInBackendName(backend);
|
|
28
|
+
if (normalized === "codex" || normalized === "claude" || normalized === "kimi" || normalized === "opencode") {
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
return await resolveExternalBackend(normalized, options);
|
|
32
|
+
}
|
|
33
|
+
export async function isSupportedBackend(backend, options = {}) {
|
|
34
|
+
const normalized = await normalizeBackend(backend, options);
|
|
35
|
+
if (normalized === "codex" || normalized === "claude" || normalized === "kimi" || normalized === "opencode") {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
39
|
+
return Boolean(descriptor);
|
|
28
40
|
}
|
|
29
|
-
export function providerVariantForBackend(backend) {
|
|
30
|
-
const normalized = normalizeBackend(backend);
|
|
41
|
+
export async function providerVariantForBackend(backend, options = {}) {
|
|
42
|
+
const normalized = await normalizeBackend(backend, options);
|
|
31
43
|
if (normalized === "claude") {
|
|
32
44
|
return CLAUDE_PROVIDER_VARIANT;
|
|
33
45
|
}
|
|
@@ -37,17 +49,28 @@ export function providerVariantForBackend(backend) {
|
|
|
37
49
|
if (normalized === "opencode") {
|
|
38
50
|
return OPENCODE_PROVIDER_VARIANT;
|
|
39
51
|
}
|
|
52
|
+
if (normalized === "codex") {
|
|
53
|
+
return DEFAULT_PROVIDER_VARIANT;
|
|
54
|
+
}
|
|
55
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
56
|
+
if (descriptor?.variant) {
|
|
57
|
+
return descriptor.variant;
|
|
58
|
+
}
|
|
40
59
|
return DEFAULT_PROVIDER_VARIANT;
|
|
41
60
|
}
|
|
42
|
-
export function assertSupportedBackend(backend) {
|
|
43
|
-
const normalized = normalizeBackend(backend);
|
|
61
|
+
export async function assertSupportedBackend(backend, options = {}) {
|
|
62
|
+
const normalized = await normalizeBackend(backend, options);
|
|
44
63
|
if (normalized === "codex" || normalized === "claude" || normalized === "kimi" || normalized === "opencode") {
|
|
45
64
|
return normalized;
|
|
46
65
|
}
|
|
47
|
-
|
|
66
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
67
|
+
if (descriptor) {
|
|
68
|
+
return normalized;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Unsupported AI SDK backend "${backend}". Built-in backends are codex app-server, claude agent-sdk, kimi cli wire, and opencode sdk. Set AISDK_PROVIDER_PATH to load external providers.`);
|
|
48
71
|
}
|
|
49
|
-
export function createLocalAiSession(backend, options = {}) {
|
|
50
|
-
const normalized = assertSupportedBackend(backend);
|
|
72
|
+
export async function createLocalAiSession(backend, options = {}) {
|
|
73
|
+
const normalized = await assertSupportedBackend(backend, options);
|
|
51
74
|
if (normalized === "claude") {
|
|
52
75
|
return new ClaudeAgentSdkSession(normalized, options);
|
|
53
76
|
}
|
|
@@ -57,7 +80,14 @@ export function createLocalAiSession(backend, options = {}) {
|
|
|
57
80
|
if (normalized === "opencode") {
|
|
58
81
|
return new OpencodeSdkSession(normalized, options);
|
|
59
82
|
}
|
|
60
|
-
|
|
83
|
+
if (normalized === "codex") {
|
|
84
|
+
return new CodexAppServerSession(normalized, options);
|
|
85
|
+
}
|
|
86
|
+
const descriptor = await getExternalProviderDescriptor(normalized, options);
|
|
87
|
+
if (!descriptor) {
|
|
88
|
+
throw new Error(`External AI SDK provider "${normalized}" is unavailable.`);
|
|
89
|
+
}
|
|
90
|
+
return await descriptor.createSession(normalized, options);
|
|
61
91
|
}
|
|
62
92
|
export { CodexAppServerSession };
|
|
63
93
|
export { ClaudeAgentSdkSession };
|
package/dist/worker.js
CHANGED
|
@@ -33,7 +33,7 @@ async function handleCreate(message) {
|
|
|
33
33
|
throw new Error("AI worker session already created");
|
|
34
34
|
}
|
|
35
35
|
sessionCreated = true;
|
|
36
|
-
session = createLocalAiSession(message.backend, {
|
|
36
|
+
session = await createLocalAiSession(message.backend, {
|
|
37
37
|
...(message.options && typeof message.options === "object" ? message.options : {}),
|
|
38
38
|
logger: {
|
|
39
39
|
log: (line) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/ai-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
"@types/node": "^22.10.2",
|
|
26
26
|
"typescript": "^5.6.3"
|
|
27
27
|
},
|
|
28
|
-
"gitCommitId": "
|
|
28
|
+
"gitCommitId": "c749d4b"
|
|
29
29
|
}
|