@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
package/dist/client.d.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
export function createAiSession(backend: any, options?: {}):
|
|
1
|
+
export function createAiSession(backend: any, options?: {}): RemoteAiSession | LocalAiSessionProxy;
|
|
2
2
|
export class RemoteAiSession extends EventEmitter<[never]> {
|
|
3
3
|
constructor(backend: any, options?: {});
|
|
4
|
-
backend: string;
|
|
5
4
|
options: {};
|
|
6
5
|
logger: any;
|
|
7
|
-
variant: string;
|
|
8
6
|
threadIdValue: any;
|
|
9
7
|
threadOptionsValue: {
|
|
10
8
|
model: any;
|
|
11
9
|
};
|
|
12
10
|
useSessionFileReplyStreamValue: boolean;
|
|
13
11
|
sessionInfo: any;
|
|
12
|
+
currentTurnStatus: any;
|
|
14
13
|
snapshot: {
|
|
15
|
-
backend:
|
|
16
|
-
provider:
|
|
14
|
+
backend: undefined;
|
|
15
|
+
provider: undefined;
|
|
17
16
|
sessionId: any;
|
|
18
17
|
useSessionFileReplyStream: boolean;
|
|
19
18
|
workerReady: boolean;
|
|
@@ -31,20 +30,26 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
31
30
|
resolveReady: ((value: any) => void) | null;
|
|
32
31
|
rejectReady: ((reason?: any) => void) | null;
|
|
33
32
|
readyPromise: Promise<any>;
|
|
33
|
+
initializeSession(backend: any, options: any): Promise<void>;
|
|
34
|
+
backend: any;
|
|
35
|
+
variant: any;
|
|
34
36
|
get threadId(): any;
|
|
35
37
|
get threadOptions(): {
|
|
38
|
+
modelProvider?: any;
|
|
36
39
|
model: any;
|
|
37
40
|
};
|
|
38
41
|
getSnapshot(): {
|
|
42
|
+
currentTurnStatus: any;
|
|
39
43
|
sessionInfo: any;
|
|
40
|
-
backend:
|
|
41
|
-
provider:
|
|
44
|
+
backend: undefined;
|
|
45
|
+
provider: undefined;
|
|
42
46
|
sessionId: any;
|
|
43
47
|
useSessionFileReplyStream: boolean;
|
|
44
48
|
workerReady: boolean;
|
|
45
49
|
};
|
|
46
50
|
usesSessionFileReplyStream(): boolean;
|
|
47
51
|
getSessionInfo(): any;
|
|
52
|
+
getCurrentTurnStatus(): any;
|
|
48
53
|
setSessionMessageHandler(handler: any): void;
|
|
49
54
|
setWorkingStatusHandler(handler: any): void;
|
|
50
55
|
setSessionReplyTarget(replyTo: any): void;
|
|
@@ -63,5 +68,45 @@ export class RemoteAiSession extends EventEmitter<[never]> {
|
|
|
63
68
|
handleWorkerEvent(payload: any): Promise<void>;
|
|
64
69
|
rejectPendingRequests(error: any): void;
|
|
65
70
|
}
|
|
71
|
+
declare class LocalAiSessionProxy extends EventEmitter<[never]> {
|
|
72
|
+
constructor(backend: any, options?: {});
|
|
73
|
+
backend: any;
|
|
74
|
+
options: {};
|
|
75
|
+
threadIdValue: any;
|
|
76
|
+
threadOptionsValue: {
|
|
77
|
+
model: any;
|
|
78
|
+
};
|
|
79
|
+
useSessionFileReplyStreamValue: boolean;
|
|
80
|
+
sessionInfo: any;
|
|
81
|
+
currentTurnStatus: any;
|
|
82
|
+
session: any;
|
|
83
|
+
closed: boolean;
|
|
84
|
+
sessionMessageHandler: any;
|
|
85
|
+
workingStatusHandler: any;
|
|
86
|
+
replyTarget: any;
|
|
87
|
+
snapshot: {
|
|
88
|
+
backend: undefined;
|
|
89
|
+
provider: undefined;
|
|
90
|
+
sessionId: any;
|
|
91
|
+
useSessionFileReplyStream: boolean;
|
|
92
|
+
workerReady: boolean;
|
|
93
|
+
};
|
|
94
|
+
readyPromise: Promise<any>;
|
|
95
|
+
initializeSession(backend: any, options: any): Promise<any>;
|
|
96
|
+
get threadId(): any;
|
|
97
|
+
get threadOptions(): any;
|
|
98
|
+
getSnapshot(): any;
|
|
99
|
+
usesSessionFileReplyStream(): boolean;
|
|
100
|
+
getSessionInfo(): any;
|
|
101
|
+
getCurrentTurnStatus(): any;
|
|
102
|
+
setSessionMessageHandler(handler: any): void;
|
|
103
|
+
setWorkingStatusHandler(handler: any): void;
|
|
104
|
+
setSessionReplyTarget(replyTarget: any): void;
|
|
105
|
+
ensureSessionInfo(): Promise<any>;
|
|
106
|
+
getSessionUsageSummary(): Promise<any>;
|
|
107
|
+
runTurn(promptText: any, options?: {}): Promise<any>;
|
|
108
|
+
close(): Promise<void>;
|
|
109
|
+
}
|
|
66
110
|
import { EventEmitter } from "node:events";
|
|
67
111
|
import readline from "node:readline";
|
|
112
|
+
export {};
|
package/dist/client.js
CHANGED
|
@@ -26,10 +26,8 @@ function toSerializablePayload(payload) {
|
|
|
26
26
|
export class RemoteAiSession extends EventEmitter {
|
|
27
27
|
constructor(backend, options = {}) {
|
|
28
28
|
super();
|
|
29
|
-
this.backend = assertSupportedBackend(backend);
|
|
30
29
|
this.options = options;
|
|
31
30
|
this.logger = normalizeLogger(options.logger);
|
|
32
|
-
this.variant = providerVariantForBackend(this.backend);
|
|
33
31
|
this.threadIdValue =
|
|
34
32
|
typeof options.resumeSessionId === "string" && options.resumeSessionId.trim()
|
|
35
33
|
? options.resumeSessionId.trim()
|
|
@@ -37,13 +35,14 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
37
35
|
this.threadOptionsValue = {
|
|
38
36
|
model: typeof options.model === "string" && options.model.trim()
|
|
39
37
|
? options.model.trim()
|
|
40
|
-
:
|
|
38
|
+
: String(backend || "unknown").trim() || "unknown",
|
|
41
39
|
};
|
|
42
40
|
this.useSessionFileReplyStreamValue = true;
|
|
43
41
|
this.sessionInfo = null;
|
|
42
|
+
this.currentTurnStatus = null;
|
|
44
43
|
this.snapshot = {
|
|
45
|
-
backend:
|
|
46
|
-
provider:
|
|
44
|
+
backend: undefined,
|
|
45
|
+
provider: undefined,
|
|
47
46
|
sessionId: this.threadIdValue || undefined,
|
|
48
47
|
useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
|
|
49
48
|
workerReady: false,
|
|
@@ -94,21 +93,50 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
94
93
|
this.rejectReady?.(error);
|
|
95
94
|
this.rejectPendingRequests(error);
|
|
96
95
|
});
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
void this.initializeSession(backend, options);
|
|
97
|
+
}
|
|
98
|
+
async initializeSession(backend, options) {
|
|
99
|
+
try {
|
|
100
|
+
this.backend = await assertSupportedBackend(backend, options);
|
|
101
|
+
this.variant = await providerVariantForBackend(backend, options);
|
|
102
|
+
this.snapshot.backend = this.backend;
|
|
103
|
+
this.snapshot.provider = this.variant;
|
|
104
|
+
this.threadOptionsValue.model =
|
|
105
|
+
typeof options.model === "string" && options.model.trim() ? options.model.trim() : this.backend || "unknown";
|
|
106
|
+
this.child.stdin.write(toSerializablePayload({
|
|
107
|
+
type: "create",
|
|
108
|
+
backend: this.backend,
|
|
109
|
+
options: sanitizeOptionsForWorker(options),
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this.rejectReady?.(error);
|
|
114
|
+
this.resolveReady = null;
|
|
115
|
+
this.rejectReady = null;
|
|
116
|
+
this.rejectPendingRequests(error);
|
|
117
|
+
try {
|
|
118
|
+
this.child.kill("SIGTERM");
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// ignore
|
|
122
|
+
}
|
|
123
|
+
}
|
|
102
124
|
}
|
|
103
125
|
get threadId() {
|
|
104
126
|
return this.threadIdValue;
|
|
105
127
|
}
|
|
106
128
|
get threadOptions() {
|
|
107
|
-
|
|
129
|
+
const sessionInfo = this.sessionInfo && typeof this.sessionInfo === "object" ? this.sessionInfo : null;
|
|
130
|
+
return {
|
|
131
|
+
...this.threadOptionsValue,
|
|
132
|
+
...(sessionInfo?.model ? { model: sessionInfo.model } : {}),
|
|
133
|
+
...(sessionInfo?.modelProvider ? { modelProvider: sessionInfo.modelProvider } : {}),
|
|
134
|
+
};
|
|
108
135
|
}
|
|
109
136
|
getSnapshot() {
|
|
110
137
|
return {
|
|
111
138
|
...this.snapshot,
|
|
139
|
+
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
112
140
|
sessionInfo: this.sessionInfo ? { ...this.sessionInfo } : null,
|
|
113
141
|
};
|
|
114
142
|
}
|
|
@@ -118,6 +146,9 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
118
146
|
getSessionInfo() {
|
|
119
147
|
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
120
148
|
}
|
|
149
|
+
getCurrentTurnStatus() {
|
|
150
|
+
return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
|
|
151
|
+
}
|
|
121
152
|
setSessionMessageHandler(handler) {
|
|
122
153
|
this.sessionMessageHandler = typeof handler === "function" ? handler : null;
|
|
123
154
|
}
|
|
@@ -278,6 +309,9 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
278
309
|
if (snapshot?.useSessionFileReplyStream !== undefined) {
|
|
279
310
|
this.useSessionFileReplyStreamValue = Boolean(snapshot.useSessionFileReplyStream);
|
|
280
311
|
}
|
|
312
|
+
if (snapshot?.currentTurnStatus && typeof snapshot.currentTurnStatus === "object") {
|
|
313
|
+
this.currentTurnStatus = { ...snapshot.currentTurnStatus };
|
|
314
|
+
}
|
|
281
315
|
}
|
|
282
316
|
handleWorkerResponse(payload) {
|
|
283
317
|
const pending = this.pendingRequests.get(payload.id);
|
|
@@ -305,6 +339,9 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
305
339
|
}
|
|
306
340
|
handleWorkerProgress(payload) {
|
|
307
341
|
const pending = this.pendingRequests.get(payload.requestId);
|
|
342
|
+
if (payload?.payload && typeof payload.payload === "object") {
|
|
343
|
+
this.currentTurnStatus = { ...payload.payload };
|
|
344
|
+
}
|
|
308
345
|
if (!pending || typeof pending.progressHandler !== "function") {
|
|
309
346
|
return;
|
|
310
347
|
}
|
|
@@ -337,6 +374,11 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
337
374
|
if (name === "assistant_message" && typeof this.sessionMessageHandler === "function") {
|
|
338
375
|
await this.sessionMessageHandler(eventPayload);
|
|
339
376
|
}
|
|
377
|
+
if (name === "working_status") {
|
|
378
|
+
if (eventPayload && typeof eventPayload === "object") {
|
|
379
|
+
this.currentTurnStatus = { ...eventPayload };
|
|
380
|
+
}
|
|
381
|
+
}
|
|
340
382
|
if (name === "working_status" && typeof this.workingStatusHandler === "function") {
|
|
341
383
|
await this.workingStatusHandler(eventPayload);
|
|
342
384
|
}
|
|
@@ -352,10 +394,199 @@ export class RemoteAiSession extends EventEmitter {
|
|
|
352
394
|
this.pendingRequests.clear();
|
|
353
395
|
}
|
|
354
396
|
}
|
|
397
|
+
const LOCAL_SESSION_EVENT_NAMES = ["session", "assistant_message", "working_status", "auth_required", "process.exited"];
|
|
398
|
+
class LocalAiSessionProxy extends EventEmitter {
|
|
399
|
+
constructor(backend, options = {}) {
|
|
400
|
+
super();
|
|
401
|
+
this.backend = undefined;
|
|
402
|
+
this.options = options;
|
|
403
|
+
this.threadIdValue =
|
|
404
|
+
typeof options.resumeSessionId === "string" && options.resumeSessionId.trim()
|
|
405
|
+
? options.resumeSessionId.trim()
|
|
406
|
+
: "";
|
|
407
|
+
this.threadOptionsValue = {
|
|
408
|
+
model: typeof options.model === "string" && options.model.trim()
|
|
409
|
+
? options.model.trim()
|
|
410
|
+
: String(backend || "unknown").trim() || "unknown",
|
|
411
|
+
};
|
|
412
|
+
this.useSessionFileReplyStreamValue = true;
|
|
413
|
+
this.sessionInfo = null;
|
|
414
|
+
this.currentTurnStatus = null;
|
|
415
|
+
this.session = null;
|
|
416
|
+
this.closed = false;
|
|
417
|
+
this.sessionMessageHandler = null;
|
|
418
|
+
this.workingStatusHandler = null;
|
|
419
|
+
this.replyTarget = undefined;
|
|
420
|
+
this.snapshot = {
|
|
421
|
+
backend: undefined,
|
|
422
|
+
provider: undefined,
|
|
423
|
+
sessionId: this.threadIdValue || undefined,
|
|
424
|
+
useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
|
|
425
|
+
workerReady: false,
|
|
426
|
+
};
|
|
427
|
+
this.readyPromise = this.initializeSession(backend, options);
|
|
428
|
+
this.readyPromise.catch(() => {
|
|
429
|
+
// keep parity with RemoteAiSession and avoid unhandled rejections
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
async initializeSession(backend, options) {
|
|
433
|
+
const session = await createLocalAiSession(backend, options);
|
|
434
|
+
if (this.closed) {
|
|
435
|
+
await session.close?.();
|
|
436
|
+
return session;
|
|
437
|
+
}
|
|
438
|
+
this.session = session;
|
|
439
|
+
for (const eventName of LOCAL_SESSION_EVENT_NAMES) {
|
|
440
|
+
if (typeof session.on === "function") {
|
|
441
|
+
session.on(eventName, async (payload) => {
|
|
442
|
+
if (eventName === "session" && payload && typeof payload === "object") {
|
|
443
|
+
this.sessionInfo = { ...payload };
|
|
444
|
+
if (payload.sessionId) {
|
|
445
|
+
this.threadIdValue = String(payload.sessionId);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (eventName === "working_status" && payload && typeof payload === "object") {
|
|
449
|
+
this.currentTurnStatus = { ...payload };
|
|
450
|
+
}
|
|
451
|
+
this.emit(eventName, payload);
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (this.sessionMessageHandler && typeof session.setSessionMessageHandler === "function") {
|
|
456
|
+
session.setSessionMessageHandler(this.sessionMessageHandler);
|
|
457
|
+
}
|
|
458
|
+
if (this.workingStatusHandler && typeof session.setWorkingStatusHandler === "function") {
|
|
459
|
+
session.setWorkingStatusHandler(this.workingStatusHandler);
|
|
460
|
+
}
|
|
461
|
+
if (this.replyTarget !== undefined && typeof session.setSessionReplyTarget === "function") {
|
|
462
|
+
session.setSessionReplyTarget(this.replyTarget);
|
|
463
|
+
}
|
|
464
|
+
const snapshot = typeof session.getSnapshot === "function" ? session.getSnapshot() : {};
|
|
465
|
+
this.backend = snapshot.backend || session.backend || this.backend;
|
|
466
|
+
this.threadIdValue = session.threadId || snapshot.sessionId || this.threadIdValue;
|
|
467
|
+
this.threadOptionsValue = session.threadOptions ? { ...session.threadOptions } : this.threadOptionsValue;
|
|
468
|
+
this.useSessionFileReplyStreamValue =
|
|
469
|
+
typeof session.usesSessionFileReplyStream === "function"
|
|
470
|
+
? Boolean(session.usesSessionFileReplyStream())
|
|
471
|
+
: snapshot.useSessionFileReplyStream !== undefined
|
|
472
|
+
? Boolean(snapshot.useSessionFileReplyStream)
|
|
473
|
+
: this.useSessionFileReplyStreamValue;
|
|
474
|
+
this.sessionInfo =
|
|
475
|
+
typeof session.getSessionInfo === "function"
|
|
476
|
+
? session.getSessionInfo()
|
|
477
|
+
: snapshot.sessionInfo && typeof snapshot.sessionInfo === "object"
|
|
478
|
+
? { ...snapshot.sessionInfo }
|
|
479
|
+
: this.sessionInfo;
|
|
480
|
+
this.snapshot = {
|
|
481
|
+
...this.snapshot,
|
|
482
|
+
...snapshot,
|
|
483
|
+
backend: this.backend,
|
|
484
|
+
sessionId: this.threadIdValue || undefined,
|
|
485
|
+
sessionInfo: this.sessionInfo,
|
|
486
|
+
currentTurnStatus: typeof session.getCurrentTurnStatus === "function" ? session.getCurrentTurnStatus() : this.currentTurnStatus,
|
|
487
|
+
useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
|
|
488
|
+
workerReady: true,
|
|
489
|
+
};
|
|
490
|
+
return session;
|
|
491
|
+
}
|
|
492
|
+
get threadId() {
|
|
493
|
+
return this.session?.threadId || this.threadIdValue;
|
|
494
|
+
}
|
|
495
|
+
get threadOptions() {
|
|
496
|
+
return this.session?.threadOptions ? { ...this.session.threadOptions } : { ...this.threadOptionsValue };
|
|
497
|
+
}
|
|
498
|
+
getSnapshot() {
|
|
499
|
+
const sessionSnapshot = typeof this.session?.getSnapshot === "function" ? this.session.getSnapshot() : null;
|
|
500
|
+
if (sessionSnapshot) {
|
|
501
|
+
return {
|
|
502
|
+
...this.snapshot,
|
|
503
|
+
...sessionSnapshot,
|
|
504
|
+
backend: sessionSnapshot.backend || this.snapshot.backend,
|
|
505
|
+
currentTurnStatus: typeof this.session?.getCurrentTurnStatus === "function"
|
|
506
|
+
? this.session.getCurrentTurnStatus()
|
|
507
|
+
: sessionSnapshot.currentTurnStatus || this.currentTurnStatus || null,
|
|
508
|
+
sessionId: sessionSnapshot.sessionId || this.threadIdValue || undefined,
|
|
509
|
+
sessionInfo: typeof this.session?.getSessionInfo === "function"
|
|
510
|
+
? this.session.getSessionInfo()
|
|
511
|
+
: sessionSnapshot.sessionInfo || this.sessionInfo || null,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
return {
|
|
515
|
+
...this.snapshot,
|
|
516
|
+
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
517
|
+
sessionInfo: this.sessionInfo ? { ...this.sessionInfo } : null,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
usesSessionFileReplyStream() {
|
|
521
|
+
if (typeof this.session?.usesSessionFileReplyStream === "function") {
|
|
522
|
+
return Boolean(this.session.usesSessionFileReplyStream());
|
|
523
|
+
}
|
|
524
|
+
return Boolean(this.useSessionFileReplyStreamValue);
|
|
525
|
+
}
|
|
526
|
+
getSessionInfo() {
|
|
527
|
+
if (typeof this.session?.getSessionInfo === "function") {
|
|
528
|
+
return this.session.getSessionInfo();
|
|
529
|
+
}
|
|
530
|
+
return this.sessionInfo ? { ...this.sessionInfo } : null;
|
|
531
|
+
}
|
|
532
|
+
getCurrentTurnStatus() {
|
|
533
|
+
if (typeof this.session?.getCurrentTurnStatus === "function") {
|
|
534
|
+
return this.session.getCurrentTurnStatus();
|
|
535
|
+
}
|
|
536
|
+
return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
|
|
537
|
+
}
|
|
538
|
+
setSessionMessageHandler(handler) {
|
|
539
|
+
this.sessionMessageHandler = typeof handler === "function" ? handler : null;
|
|
540
|
+
if (typeof this.session?.setSessionMessageHandler === "function") {
|
|
541
|
+
this.session.setSessionMessageHandler(this.sessionMessageHandler);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
setWorkingStatusHandler(handler) {
|
|
545
|
+
this.workingStatusHandler = typeof handler === "function" ? handler : null;
|
|
546
|
+
if (typeof this.session?.setWorkingStatusHandler === "function") {
|
|
547
|
+
this.session.setWorkingStatusHandler(this.workingStatusHandler);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
setSessionReplyTarget(replyTarget) {
|
|
551
|
+
this.replyTarget = replyTarget;
|
|
552
|
+
if (typeof this.session?.setSessionReplyTarget === "function") {
|
|
553
|
+
this.session.setSessionReplyTarget(replyTarget);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async ensureSessionInfo() {
|
|
557
|
+
const session = await this.readyPromise;
|
|
558
|
+
const sessionInfo = await session.ensureSessionInfo();
|
|
559
|
+
this.sessionInfo = sessionInfo && typeof sessionInfo === "object" ? { ...sessionInfo } : sessionInfo;
|
|
560
|
+
if (sessionInfo?.sessionId) {
|
|
561
|
+
this.threadIdValue = String(sessionInfo.sessionId);
|
|
562
|
+
}
|
|
563
|
+
return sessionInfo;
|
|
564
|
+
}
|
|
565
|
+
async getSessionUsageSummary() {
|
|
566
|
+
const session = await this.readyPromise;
|
|
567
|
+
return await session.getSessionUsageSummary();
|
|
568
|
+
}
|
|
569
|
+
async runTurn(promptText, options = {}) {
|
|
570
|
+
const session = await this.readyPromise;
|
|
571
|
+
return await session.runTurn(promptText, options);
|
|
572
|
+
}
|
|
573
|
+
async close() {
|
|
574
|
+
if (this.closed) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
this.closed = true;
|
|
578
|
+
try {
|
|
579
|
+
const session = await this.readyPromise;
|
|
580
|
+
await session.close?.();
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
// best effort
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
355
587
|
export function createAiSession(backend, options = {}) {
|
|
356
|
-
const normalizedBackend = assertSupportedBackend(backend);
|
|
357
588
|
if (process.env.CONDUCTOR_AI_SDK_DISABLE_WORKER === "1") {
|
|
358
|
-
return
|
|
589
|
+
return new LocalAiSessionProxy(backend, options);
|
|
359
590
|
}
|
|
360
|
-
return new RemoteAiSession(
|
|
591
|
+
return new RemoteAiSession(backend, options);
|
|
361
592
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export function getExternalProviderRegistry(options?: {}): Promise<any>;
|
|
2
|
+
export function resolveExternalBackend(backend: any, options?: {}): Promise<any>;
|
|
3
|
+
export function getExternalProviderDescriptor(backend: any, options?: {}): Promise<any>;
|
|
4
|
+
export function resetExternalProviderRegistryForTests(): void;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { loadEnvConfig } from "./shared.js";
|
|
4
|
+
const BUILT_IN_BACKENDS = new Set(["codex", "claude", "kimi", "opencode"]);
|
|
5
|
+
const registryPromises = new Map();
|
|
6
|
+
let externalProviderImportNonce = 0;
|
|
7
|
+
function normalizeProviderPathEnv(value) {
|
|
8
|
+
return String(value || "").trim();
|
|
9
|
+
}
|
|
10
|
+
function listProviderModulePathsFromValue(rawValue) {
|
|
11
|
+
const raw = normalizeProviderPathEnv(rawValue);
|
|
12
|
+
if (!raw) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const parts = raw
|
|
16
|
+
.split(process.platform === "win32" ? ";" : ":")
|
|
17
|
+
.map((item) => item.trim())
|
|
18
|
+
.filter(Boolean);
|
|
19
|
+
return [...new Set(parts)];
|
|
20
|
+
}
|
|
21
|
+
function resolveProviderPathEnv(options = {}) {
|
|
22
|
+
const envValue = normalizeProviderPathEnv(process.env.AISDK_PROVIDER_PATH);
|
|
23
|
+
if (envValue) {
|
|
24
|
+
return envValue;
|
|
25
|
+
}
|
|
26
|
+
const envConfig = loadEnvConfig(options.configFile);
|
|
27
|
+
return normalizeProviderPathEnv(envConfig?.AISDK_PROVIDER_PATH);
|
|
28
|
+
}
|
|
29
|
+
function normalizeName(value) {
|
|
30
|
+
return String(value || "").trim().toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
function createRegistry() {
|
|
33
|
+
return {
|
|
34
|
+
descriptors: [],
|
|
35
|
+
byBackend: new Map(),
|
|
36
|
+
aliasToBackend: new Map(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function registerAlias(registry, alias, backend, sourcePath) {
|
|
40
|
+
const normalizedAlias = normalizeName(alias);
|
|
41
|
+
if (!normalizedAlias) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (BUILT_IN_BACKENDS.has(normalizedAlias) && normalizedAlias !== backend) {
|
|
45
|
+
throw new Error(`External AI SDK provider alias "${normalizedAlias}" from ${sourcePath} conflicts with built-in backend "${normalizedAlias}".`);
|
|
46
|
+
}
|
|
47
|
+
const existingBackend = registry.aliasToBackend.get(normalizedAlias);
|
|
48
|
+
if (existingBackend && existingBackend !== backend) {
|
|
49
|
+
throw new Error(`External AI SDK provider alias "${normalizedAlias}" from ${sourcePath} conflicts with backend "${existingBackend}".`);
|
|
50
|
+
}
|
|
51
|
+
registry.aliasToBackend.set(normalizedAlias, backend);
|
|
52
|
+
}
|
|
53
|
+
function validateDescriptor(descriptor, sourcePath) {
|
|
54
|
+
if (!descriptor || typeof descriptor !== "object") {
|
|
55
|
+
throw new Error(`External AI SDK provider module ${sourcePath} contains an invalid provider descriptor.`);
|
|
56
|
+
}
|
|
57
|
+
const backend = normalizeName(descriptor.backend);
|
|
58
|
+
if (!backend) {
|
|
59
|
+
throw new Error(`External AI SDK provider module ${sourcePath} is missing provider.backend.`);
|
|
60
|
+
}
|
|
61
|
+
if (BUILT_IN_BACKENDS.has(backend)) {
|
|
62
|
+
throw new Error(`External AI SDK provider backend "${backend}" from ${sourcePath} conflicts with a built-in backend.`);
|
|
63
|
+
}
|
|
64
|
+
const variant = String(descriptor.variant || "").trim();
|
|
65
|
+
if (!variant) {
|
|
66
|
+
throw new Error(`External AI SDK provider "${backend}" from ${sourcePath} is missing provider.variant.`);
|
|
67
|
+
}
|
|
68
|
+
if (typeof descriptor.createSession !== "function") {
|
|
69
|
+
throw new Error(`External AI SDK provider "${backend}" from ${sourcePath} is missing provider.createSession().`);
|
|
70
|
+
}
|
|
71
|
+
const aliases = Array.isArray(descriptor.aliases) ? descriptor.aliases.map((item) => normalizeName(item)).filter(Boolean) : [];
|
|
72
|
+
return {
|
|
73
|
+
backend,
|
|
74
|
+
variant,
|
|
75
|
+
aliases,
|
|
76
|
+
createSession: descriptor.createSession,
|
|
77
|
+
isSupported: typeof descriptor.isSupported === "function" ? descriptor.isSupported : null,
|
|
78
|
+
sourcePath,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function loadRegistry(modulePaths) {
|
|
82
|
+
const registry = createRegistry();
|
|
83
|
+
for (const modulePath of modulePaths) {
|
|
84
|
+
let importedModule;
|
|
85
|
+
try {
|
|
86
|
+
const resolvedPath = path.isAbsolute(modulePath) ? modulePath : path.resolve(modulePath);
|
|
87
|
+
const moduleUrl = pathToFileURL(resolvedPath);
|
|
88
|
+
moduleUrl.searchParams.set("conductor-ai-sdk-provider-attempt", String(++externalProviderImportNonce));
|
|
89
|
+
importedModule = await import(moduleUrl.href);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw new Error(`Failed to load external AI SDK provider module ${modulePath}: ${error?.message || error}`);
|
|
93
|
+
}
|
|
94
|
+
const providers = Array.isArray(importedModule?.providers) ? importedModule.providers : [];
|
|
95
|
+
if (providers.length === 0) {
|
|
96
|
+
throw new Error(`External AI SDK provider module ${modulePath} must export a non-empty providers array.`);
|
|
97
|
+
}
|
|
98
|
+
for (const rawDescriptor of providers) {
|
|
99
|
+
const descriptor = validateDescriptor(rawDescriptor, modulePath);
|
|
100
|
+
if (registry.byBackend.has(descriptor.backend)) {
|
|
101
|
+
throw new Error(`External AI SDK provider backend "${descriptor.backend}" is declared more than once (latest: ${modulePath}).`);
|
|
102
|
+
}
|
|
103
|
+
registry.descriptors.push(descriptor);
|
|
104
|
+
registry.byBackend.set(descriptor.backend, descriptor);
|
|
105
|
+
registerAlias(registry, descriptor.backend, descriptor.backend, modulePath);
|
|
106
|
+
for (const alias of descriptor.aliases) {
|
|
107
|
+
registerAlias(registry, alias, descriptor.backend, modulePath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return registry;
|
|
112
|
+
}
|
|
113
|
+
export async function getExternalProviderRegistry(options = {}) {
|
|
114
|
+
const providerPathEnv = resolveProviderPathEnv(options);
|
|
115
|
+
if (!registryPromises.has(providerPathEnv)) {
|
|
116
|
+
const loadPromise = loadRegistry(listProviderModulePathsFromValue(providerPathEnv)).catch((error) => {
|
|
117
|
+
registryPromises.delete(providerPathEnv);
|
|
118
|
+
throw error;
|
|
119
|
+
});
|
|
120
|
+
registryPromises.set(providerPathEnv, loadPromise);
|
|
121
|
+
}
|
|
122
|
+
return registryPromises.get(providerPathEnv);
|
|
123
|
+
}
|
|
124
|
+
export async function resolveExternalBackend(backend, options = {}) {
|
|
125
|
+
const normalized = normalizeName(backend);
|
|
126
|
+
if (!normalized) {
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
const registry = await getExternalProviderRegistry(options);
|
|
130
|
+
return registry.aliasToBackend.get(normalized) || normalized;
|
|
131
|
+
}
|
|
132
|
+
export async function getExternalProviderDescriptor(backend, options = {}) {
|
|
133
|
+
const normalized = normalizeName(backend);
|
|
134
|
+
if (!normalized) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const registry = await getExternalProviderRegistry(options);
|
|
138
|
+
const resolvedBackend = registry.aliasToBackend.get(normalized) || normalized;
|
|
139
|
+
return registry.byBackend.get(resolvedBackend) || null;
|
|
140
|
+
}
|
|
141
|
+
export function resetExternalProviderRegistryForTests() {
|
|
142
|
+
registryPromises.clear();
|
|
143
|
+
}
|
|
@@ -31,6 +31,9 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
|
|
|
31
31
|
} | null;
|
|
32
32
|
lastResult: any;
|
|
33
33
|
rateLimitInfo: any;
|
|
34
|
+
currentTurnStatus: any;
|
|
35
|
+
currentTurnActivityAt: number;
|
|
36
|
+
now: any;
|
|
34
37
|
turnDeadlineMs: any;
|
|
35
38
|
sdkModulePromise: Promise<any> | Promise<typeof import("@anthropic-ai/claude-agent-sdk")> | null;
|
|
36
39
|
env: any;
|
|
@@ -39,6 +42,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
|
|
|
39
42
|
get threadId(): any;
|
|
40
43
|
get threadOptions(): {
|
|
41
44
|
model: any;
|
|
45
|
+
modelProvider: any;
|
|
42
46
|
};
|
|
43
47
|
getSnapshot(): {
|
|
44
48
|
backend: string;
|
|
@@ -55,11 +59,13 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
|
|
|
55
59
|
ready: boolean;
|
|
56
60
|
command: string;
|
|
57
61
|
} | null;
|
|
62
|
+
currentTurnStatus: any;
|
|
58
63
|
};
|
|
59
64
|
getSessionInfo(): {
|
|
60
65
|
backend: string;
|
|
61
66
|
sessionId: any;
|
|
62
67
|
} | null;
|
|
68
|
+
getCurrentTurnStatus(): any;
|
|
63
69
|
ensureSessionInfo(): Promise<{
|
|
64
70
|
backend: string;
|
|
65
71
|
sessionId: any;
|
|
@@ -81,6 +87,9 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
|
|
|
81
87
|
setWorkingStatusHandler(handler: any): void;
|
|
82
88
|
setSessionReplyTarget(replyTo: any): void;
|
|
83
89
|
getCurrentReplyTarget(): string | undefined;
|
|
90
|
+
touchTurnActivity(): void;
|
|
91
|
+
updateCurrentTurnStatus(payload: any): void;
|
|
92
|
+
markTurnStartedStatus(): void;
|
|
84
93
|
emitWorkingStatus(payload: any, onProgress?: null): Promise<void>;
|
|
85
94
|
emitAssistantMessage(text: any): Promise<void>;
|
|
86
95
|
emitTerminalWorkingStatus(currentTurn: any, payload: any, onProgress?: null): Promise<void>;
|