@love-moon/ai-sdk 0.2.16

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.
@@ -0,0 +1,67 @@
1
+ export function createAiSession(backend: any, options?: {}): import("./session-factory.js").CodexAppServerSession | RemoteAiSession;
2
+ export class RemoteAiSession extends EventEmitter<[never]> {
3
+ constructor(backend: any, options?: {});
4
+ backend: string;
5
+ options: {};
6
+ logger: any;
7
+ variant: string;
8
+ threadIdValue: any;
9
+ threadOptionsValue: {
10
+ model: string;
11
+ };
12
+ useSessionFileReplyStreamValue: boolean;
13
+ sessionInfo: any;
14
+ snapshot: {
15
+ backend: string;
16
+ provider: string;
17
+ sessionId: any;
18
+ useSessionFileReplyStream: boolean;
19
+ workerReady: boolean;
20
+ };
21
+ sessionMessageHandler: any;
22
+ workingStatusHandler: any;
23
+ pendingRequests: Map<any, any>;
24
+ nextRequestId: number;
25
+ closed: boolean;
26
+ workerExited: boolean;
27
+ child: import("child_process").ChildProcessWithoutNullStreams;
28
+ stdoutReader: readline.Interface;
29
+ stderrReader: readline.Interface;
30
+ exitPromise: Promise<any>;
31
+ resolveReady: ((value: any) => void) | null;
32
+ rejectReady: ((reason?: any) => void) | null;
33
+ readyPromise: Promise<any>;
34
+ get threadId(): any;
35
+ get threadOptions(): {
36
+ model: string;
37
+ };
38
+ getSnapshot(): {
39
+ sessionInfo: any;
40
+ backend: string;
41
+ provider: string;
42
+ sessionId: any;
43
+ useSessionFileReplyStream: boolean;
44
+ workerReady: boolean;
45
+ };
46
+ usesSessionFileReplyStream(): boolean;
47
+ getSessionInfo(): any;
48
+ setSessionMessageHandler(handler: any): void;
49
+ setWorkingStatusHandler(handler: any): void;
50
+ setSessionReplyTarget(replyTo: any): void;
51
+ ensureSessionInfo(): Promise<any>;
52
+ getSessionUsageSummary(): Promise<any>;
53
+ runTurn(promptText: any, options?: {}): Promise<any>;
54
+ close(): Promise<void>;
55
+ callWorker(method: any, args?: any[], { progressHandler }?: {
56
+ progressHandler?: null | undefined;
57
+ }): Promise<any>;
58
+ handleWorkerStderr(line: any): void;
59
+ handleWorkerLine(line: any): void;
60
+ applyReadyPayload(payload: any): void;
61
+ handleWorkerResponse(payload: any): void;
62
+ handleWorkerProgress(payload: any): void;
63
+ handleWorkerEvent(payload: any): Promise<void>;
64
+ rejectPendingRequests(error: any): void;
65
+ }
66
+ import { EventEmitter } from "node:events";
67
+ import readline from "node:readline";
package/dist/client.js ADDED
@@ -0,0 +1,359 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { spawn } from "node:child_process";
3
+ import { fileURLToPath } from "node:url";
4
+ import readline from "node:readline";
5
+ import { assertSupportedBackend, createLocalAiSession, DEFAULT_PROVIDER_VARIANT, } from "./session-factory.js";
6
+ import { normalizeLogger, reviveError } from "./shared.js";
7
+ const WORKER_PATH = fileURLToPath(new URL("./worker.js", import.meta.url));
8
+ function sanitizeOptionsForWorker(options = {}) {
9
+ const sanitized = {};
10
+ for (const [key, value] of Object.entries(options)) {
11
+ if (typeof value === "function" || key === "logger") {
12
+ continue;
13
+ }
14
+ sanitized[key] = value;
15
+ }
16
+ return sanitized;
17
+ }
18
+ function createSessionClosedError() {
19
+ const error = new Error("AI session worker closed");
20
+ error.reason = "session_closed";
21
+ return error;
22
+ }
23
+ function toSerializablePayload(payload) {
24
+ return `${JSON.stringify(payload)}\n`;
25
+ }
26
+ export class RemoteAiSession extends EventEmitter {
27
+ constructor(backend, options = {}) {
28
+ super();
29
+ this.backend = assertSupportedBackend(backend);
30
+ this.options = options;
31
+ this.logger = normalizeLogger(options.logger);
32
+ this.variant = DEFAULT_PROVIDER_VARIANT;
33
+ this.threadIdValue =
34
+ typeof options.resumeSessionId === "string" && options.resumeSessionId.trim()
35
+ ? options.resumeSessionId.trim()
36
+ : "";
37
+ this.threadOptionsValue = {
38
+ model: this.backend || "unknown",
39
+ };
40
+ this.useSessionFileReplyStreamValue = true;
41
+ this.sessionInfo = null;
42
+ this.snapshot = {
43
+ backend: this.backend,
44
+ provider: this.variant,
45
+ sessionId: this.threadIdValue || undefined,
46
+ useSessionFileReplyStream: this.useSessionFileReplyStreamValue,
47
+ workerReady: false,
48
+ };
49
+ this.sessionMessageHandler = null;
50
+ this.workingStatusHandler = null;
51
+ this.pendingRequests = new Map();
52
+ this.nextRequestId = 1;
53
+ this.closed = false;
54
+ this.workerExited = false;
55
+ this.child = spawn(process.execPath, [WORKER_PATH], {
56
+ env: process.env,
57
+ stdio: ["pipe", "pipe", "pipe"],
58
+ });
59
+ this.stdoutReader = readline.createInterface({ input: this.child.stdout });
60
+ this.stderrReader = readline.createInterface({ input: this.child.stderr });
61
+ this.stdoutReader.on("line", (line) => {
62
+ this.handleWorkerLine(line);
63
+ });
64
+ this.stderrReader.on("line", (line) => {
65
+ this.handleWorkerStderr(line);
66
+ });
67
+ this.exitPromise = new Promise((resolve) => {
68
+ this.child.once("exit", (code, signal) => {
69
+ this.workerExited = true;
70
+ if (this.rejectReady) {
71
+ this.rejectReady(createSessionClosedError());
72
+ this.resolveReady = null;
73
+ this.rejectReady = null;
74
+ }
75
+ this.rejectPendingRequests(createSessionClosedError());
76
+ this.emit("process.exited", {
77
+ pid: this.child.pid || null,
78
+ code,
79
+ signal,
80
+ });
81
+ resolve({ code, signal });
82
+ });
83
+ });
84
+ this.readyPromise = new Promise((resolve, reject) => {
85
+ this.resolveReady = resolve;
86
+ this.rejectReady = reject;
87
+ });
88
+ this.readyPromise.catch(() => {
89
+ // prevent unhandled rejection when callers only use sync getters then close immediately
90
+ });
91
+ this.child.on("error", (error) => {
92
+ this.rejectReady?.(error);
93
+ this.rejectPendingRequests(error);
94
+ });
95
+ this.child.stdin.write(toSerializablePayload({
96
+ type: "create",
97
+ backend: this.backend,
98
+ options: sanitizeOptionsForWorker(options),
99
+ }));
100
+ }
101
+ get threadId() {
102
+ return this.threadIdValue;
103
+ }
104
+ get threadOptions() {
105
+ return { ...this.threadOptionsValue };
106
+ }
107
+ getSnapshot() {
108
+ return {
109
+ ...this.snapshot,
110
+ sessionInfo: this.sessionInfo ? { ...this.sessionInfo } : null,
111
+ };
112
+ }
113
+ usesSessionFileReplyStream() {
114
+ return Boolean(this.useSessionFileReplyStreamValue);
115
+ }
116
+ getSessionInfo() {
117
+ return this.sessionInfo ? { ...this.sessionInfo } : null;
118
+ }
119
+ setSessionMessageHandler(handler) {
120
+ this.sessionMessageHandler = typeof handler === "function" ? handler : null;
121
+ }
122
+ setWorkingStatusHandler(handler) {
123
+ this.workingStatusHandler = typeof handler === "function" ? handler : null;
124
+ }
125
+ setSessionReplyTarget(replyTo) {
126
+ void this.callWorker("setSessionReplyTarget", [replyTo]).catch((error) => {
127
+ if (error?.reason !== "session_closed") {
128
+ this.logger.log?.(`[ai-sdk] failed to set session reply target: ${error?.message || error}`);
129
+ }
130
+ });
131
+ }
132
+ async ensureSessionInfo() {
133
+ return this.callWorker("ensureSessionInfo", []);
134
+ }
135
+ async getSessionUsageSummary() {
136
+ return this.callWorker("getSessionUsageSummary", []);
137
+ }
138
+ async runTurn(promptText, options = {}) {
139
+ const { onProgress, ...restOptions } = options || {};
140
+ return this.callWorker("runTurn", [promptText, restOptions], {
141
+ progressHandler: typeof onProgress === "function" ? onProgress : null,
142
+ });
143
+ }
144
+ async close() {
145
+ if (this.closed) {
146
+ return;
147
+ }
148
+ this.closed = true;
149
+ try {
150
+ if (!this.workerExited) {
151
+ await this.callWorker("close", []);
152
+ }
153
+ }
154
+ catch {
155
+ // best effort
156
+ }
157
+ if (!this.workerExited) {
158
+ try {
159
+ this.child.kill("SIGTERM");
160
+ }
161
+ catch {
162
+ // ignore
163
+ }
164
+ }
165
+ await Promise.race([
166
+ this.exitPromise,
167
+ new Promise((resolve) => {
168
+ const timer = setTimeout(resolve, 1500);
169
+ if (typeof timer.unref === "function") {
170
+ timer.unref();
171
+ }
172
+ }),
173
+ ]);
174
+ if (!this.workerExited) {
175
+ try {
176
+ this.child.kill("SIGKILL");
177
+ }
178
+ catch {
179
+ // ignore
180
+ }
181
+ await this.exitPromise;
182
+ }
183
+ }
184
+ async callWorker(method, args = [], { progressHandler = null } = {}) {
185
+ if (this.closed) {
186
+ throw createSessionClosedError();
187
+ }
188
+ await this.readyPromise;
189
+ if (this.workerExited) {
190
+ throw createSessionClosedError();
191
+ }
192
+ const requestId = this.nextRequestId++;
193
+ const payload = {
194
+ type: "request",
195
+ id: requestId,
196
+ method,
197
+ args,
198
+ };
199
+ return new Promise((resolve, reject) => {
200
+ this.pendingRequests.set(requestId, {
201
+ resolve,
202
+ reject,
203
+ progressHandler,
204
+ });
205
+ try {
206
+ this.child.stdin.write(toSerializablePayload(payload));
207
+ }
208
+ catch (error) {
209
+ this.pendingRequests.delete(requestId);
210
+ reject(error);
211
+ }
212
+ });
213
+ }
214
+ handleWorkerStderr(line) {
215
+ const normalized = String(line || "").trim();
216
+ if (!normalized) {
217
+ return;
218
+ }
219
+ this.logger.log?.(`[ai-sdk worker] ${normalized}`);
220
+ }
221
+ handleWorkerLine(line) {
222
+ const normalized = String(line || "").trim();
223
+ if (!normalized) {
224
+ return;
225
+ }
226
+ let payload;
227
+ try {
228
+ payload = JSON.parse(normalized);
229
+ }
230
+ catch {
231
+ this.logger.log?.(`[ai-sdk worker] invalid stdout: ${normalized}`);
232
+ return;
233
+ }
234
+ switch (payload?.type) {
235
+ case "ready":
236
+ this.applyReadyPayload(payload);
237
+ this.resolveReady?.(payload);
238
+ this.resolveReady = null;
239
+ this.rejectReady = null;
240
+ return;
241
+ case "create_error": {
242
+ const error = reviveError(payload.error);
243
+ this.rejectReady?.(error);
244
+ this.resolveReady = null;
245
+ this.rejectReady = null;
246
+ return;
247
+ }
248
+ case "response":
249
+ this.handleWorkerResponse(payload);
250
+ return;
251
+ case "progress":
252
+ this.handleWorkerProgress(payload);
253
+ return;
254
+ case "event":
255
+ this.handleWorkerEvent(payload);
256
+ return;
257
+ default:
258
+ return;
259
+ }
260
+ }
261
+ applyReadyPayload(payload) {
262
+ const snapshot = payload?.snapshot && typeof payload.snapshot === "object" ? payload.snapshot : {};
263
+ this.snapshot = {
264
+ ...this.snapshot,
265
+ ...snapshot,
266
+ workerReady: true,
267
+ workerPid: payload?.workerPid || undefined,
268
+ workerProcessPid: payload?.workerProcessPid || undefined,
269
+ };
270
+ if (snapshot?.sessionInfo) {
271
+ this.sessionInfo = { ...snapshot.sessionInfo };
272
+ }
273
+ if (snapshot?.sessionId) {
274
+ this.threadIdValue = String(snapshot.sessionId);
275
+ }
276
+ if (snapshot?.useSessionFileReplyStream !== undefined) {
277
+ this.useSessionFileReplyStreamValue = Boolean(snapshot.useSessionFileReplyStream);
278
+ }
279
+ }
280
+ handleWorkerResponse(payload) {
281
+ const pending = this.pendingRequests.get(payload.id);
282
+ if (!pending) {
283
+ return;
284
+ }
285
+ this.pendingRequests.delete(payload.id);
286
+ if (payload.error) {
287
+ pending.reject(reviveError(payload.error));
288
+ return;
289
+ }
290
+ if (payload.result && typeof payload.result === "object" && payload.result.sessionId) {
291
+ this.threadIdValue = String(payload.result.sessionId);
292
+ }
293
+ if (payload.result && typeof payload.result === "object" && payload.result.sessionFilePath) {
294
+ this.sessionInfo = {
295
+ ...(this.sessionInfo || {
296
+ backend: this.backend,
297
+ sessionId: this.threadIdValue || undefined,
298
+ }),
299
+ sessionFilePath: payload.result.sessionFilePath,
300
+ };
301
+ }
302
+ pending.resolve(payload.result);
303
+ }
304
+ handleWorkerProgress(payload) {
305
+ const pending = this.pendingRequests.get(payload.requestId);
306
+ if (!pending || typeof pending.progressHandler !== "function") {
307
+ return;
308
+ }
309
+ try {
310
+ pending.progressHandler(payload.payload);
311
+ }
312
+ catch {
313
+ // best effort
314
+ }
315
+ }
316
+ async handleWorkerEvent(payload) {
317
+ const name = String(payload?.name || "").trim();
318
+ const eventPayload = payload?.payload;
319
+ if (name === "log" && eventPayload?.message) {
320
+ this.logger.log?.(String(eventPayload.message));
321
+ this.emit("log", eventPayload);
322
+ return;
323
+ }
324
+ if (name === "session" && eventPayload && typeof eventPayload === "object") {
325
+ this.sessionInfo = { ...eventPayload };
326
+ if (eventPayload.sessionId) {
327
+ this.threadIdValue = String(eventPayload.sessionId);
328
+ }
329
+ this.snapshot = {
330
+ ...this.snapshot,
331
+ sessionId: this.threadIdValue || undefined,
332
+ sessionInfo: this.sessionInfo,
333
+ };
334
+ }
335
+ if (name === "assistant_message" && typeof this.sessionMessageHandler === "function") {
336
+ await this.sessionMessageHandler(eventPayload);
337
+ }
338
+ if (name === "working_status" && typeof this.workingStatusHandler === "function") {
339
+ await this.workingStatusHandler(eventPayload);
340
+ }
341
+ this.emit(name, eventPayload);
342
+ }
343
+ rejectPendingRequests(error) {
344
+ if (this.pendingRequests.size === 0) {
345
+ return;
346
+ }
347
+ for (const pending of this.pendingRequests.values()) {
348
+ pending.reject(error);
349
+ }
350
+ this.pendingRequests.clear();
351
+ }
352
+ }
353
+ export function createAiSession(backend, options = {}) {
354
+ const normalizedBackend = assertSupportedBackend(backend);
355
+ if (process.env.CONDUCTOR_AI_SDK_DISABLE_WORKER === "1") {
356
+ return createLocalAiSession(normalizedBackend, options);
357
+ }
358
+ return new RemoteAiSession(normalizedBackend, options);
359
+ }
@@ -0,0 +1 @@
1
+ export { createAiSession, RemoteAiSession } from "./client.js";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { createAiSession, RemoteAiSession } from "./client.js";
@@ -0,0 +1,168 @@
1
+ export class CodexAppServerSession extends EventEmitter<[never]> {
2
+ constructor(backend: any, options?: {});
3
+ backend: string;
4
+ options: {};
5
+ logger: any;
6
+ cwd: any;
7
+ resumeSessionId: any;
8
+ sessionId: any;
9
+ threadPath: string;
10
+ sessionInfo: any;
11
+ history: any[];
12
+ pendingHistorySeed: boolean;
13
+ closeRequested: boolean;
14
+ closed: boolean;
15
+ closeWaiters: Set<any>;
16
+ sessionMessageHandler: any;
17
+ workingStatusHandler: any;
18
+ activeReplyTarget: string;
19
+ lastReplyTarget: string;
20
+ manualResumeReady: boolean;
21
+ nativeSessionId: string;
22
+ rateLimits: any;
23
+ tokenUsage: any;
24
+ turnDeadlineMs: any;
25
+ currentTurn: {
26
+ turnId: string;
27
+ fullText: string;
28
+ activeAssistantMessageId: string;
29
+ activeAssistantMessageText: string;
30
+ resolve: null;
31
+ reject: null;
32
+ } | null;
33
+ bootPromise: Promise<void> | null;
34
+ booted: boolean;
35
+ transport: CodexAppServerTransport;
36
+ writeLog(message: any): void;
37
+ trace(message: any): void;
38
+ get threadId(): any;
39
+ get threadOptions(): {
40
+ model: string;
41
+ };
42
+ getSnapshot(): {
43
+ backend: string;
44
+ provider: string;
45
+ cwd: any;
46
+ sessionId: any;
47
+ sessionInfo: any;
48
+ useSessionFileReplyStream: boolean;
49
+ resumeReady: boolean;
50
+ manualResume: {
51
+ ready: boolean;
52
+ command: string;
53
+ } | null;
54
+ pid: number | undefined;
55
+ };
56
+ getSessionInfo(): any;
57
+ ensureSessionInfo(): Promise<any>;
58
+ getSessionUsageSummary(): Promise<{
59
+ sessionId: any;
60
+ sessionFilePath: string | undefined;
61
+ tokenUsagePercent: number | undefined;
62
+ contextUsagePercent: number | undefined;
63
+ tokenUsage: any;
64
+ rateLimits: any;
65
+ manualResume: {
66
+ ready: boolean;
67
+ command: string;
68
+ } | null;
69
+ }>;
70
+ usesSessionFileReplyStream(): boolean;
71
+ setSessionMessageHandler(handler: any): void;
72
+ setWorkingStatusHandler(handler: any): void;
73
+ setSessionReplyTarget(replyTo: any): void;
74
+ getCurrentReplyTarget(): string | undefined;
75
+ boot(): Promise<void>;
76
+ bootInternal(): Promise<void>;
77
+ applyThreadInfo(thread: any, { resumeReady }?: {
78
+ resumeReady?: boolean | undefined;
79
+ }): void;
80
+ applySessionConfigured(params: any): void;
81
+ resolveContextUsagePercent(): number | undefined;
82
+ normalizeWorkingStatusPayload(payload: any): {
83
+ source: string;
84
+ reply_in_progress: boolean;
85
+ replyTo: any;
86
+ state: any;
87
+ phase: any;
88
+ status_line: any;
89
+ status_done_line: any;
90
+ reply_preview: any;
91
+ thread_id: any;
92
+ };
93
+ emitWorkingStatus(payload: any): Promise<void>;
94
+ emitAssistantMessage(text: any): Promise<void>;
95
+ finalizeActiveAssistantMessage(currentTurn?: {
96
+ turnId: string;
97
+ fullText: string;
98
+ activeAssistantMessageId: string;
99
+ activeAssistantMessageText: string;
100
+ resolve: null;
101
+ reject: null;
102
+ } | null, { messageId }?: {
103
+ messageId?: string | undefined;
104
+ }): Promise<boolean>;
105
+ createSessionClosedError(): Error;
106
+ createTurnTimeoutError(timeoutMs: any): Error;
107
+ createCloseGuard(): {
108
+ promise: Promise<any>;
109
+ cleanup: () => void;
110
+ };
111
+ createTurnTimeoutGuard(): {
112
+ promise: Promise<any>;
113
+ cleanup: () => void;
114
+ };
115
+ flushCloseWaiters(): void;
116
+ buildPrompt(promptText: any, { useInitialImages }?: {
117
+ useInitialImages?: boolean | undefined;
118
+ }): string;
119
+ getCurrentTurn(): {
120
+ turnId: string;
121
+ fullText: string;
122
+ activeAssistantMessageId: string;
123
+ activeAssistantMessageText: string;
124
+ resolve: null;
125
+ reject: null;
126
+ } | null;
127
+ ensureCurrentTurn(params: any): {
128
+ turnId: string;
129
+ fullText: string;
130
+ activeAssistantMessageId: string;
131
+ activeAssistantMessageText: string;
132
+ resolve: null;
133
+ reject: null;
134
+ } | null;
135
+ queueAssistantDelta(delta: any, { messageId }?: {
136
+ messageId?: string | undefined;
137
+ }): Promise<void>;
138
+ handleNotification(method: any, params?: {}): Promise<void>;
139
+ handleTransportFailure(error: any): void;
140
+ handleTransportExit(payload: any): void;
141
+ maybeEmitAuthRequired(error: any): void;
142
+ interruptCurrentTurn(): Promise<void>;
143
+ runTurn(promptText: any, { useInitialImages }?: {
144
+ useInitialImages?: boolean | undefined;
145
+ }): Promise<{
146
+ text: string;
147
+ usage: null;
148
+ items: never[];
149
+ events: never[];
150
+ provider?: undefined;
151
+ metadata?: undefined;
152
+ } | {
153
+ text: string;
154
+ usage: any;
155
+ items: any;
156
+ events: never[];
157
+ provider: string;
158
+ metadata: {
159
+ source: string;
160
+ threadId: any;
161
+ threadPath: string | undefined;
162
+ nativeSessionId: string | undefined;
163
+ };
164
+ }>;
165
+ close(): Promise<void>;
166
+ }
167
+ import { EventEmitter } from "node:events";
168
+ import { CodexAppServerTransport } from "../transports/codex-app-server-transport.js";