@love-moon/ai-sdk 0.2.39 → 0.2.40

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,633 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { randomUUID } from "node:crypto";
5
+ import { EventEmitter } from "node:events";
6
+ import readline from "node:readline";
7
+ import { emitLog, getBoundedEnvInt, loadEnvConfig, normalizeLogger, parseCommandParts, proxyToEnv, sanitizeForLog, } from "../shared.js";
8
+ const DEFAULT_TURN_DEADLINE_MS = 12 * 60 * 1000;
9
+ const MIN_TURN_DEADLINE_MS = 30 * 1000;
10
+ const MAX_TURN_DEADLINE_MS = 30 * 60 * 1000;
11
+ const KIMI_PRINT_PROVIDER_VARIANT = "kimi-cli-print";
12
+ const DEFAULT_KIMI_COMMAND = "kimi";
13
+ function createTurnError(message, extras = {}) {
14
+ const error = new Error(message);
15
+ for (const [key, value] of Object.entries(extras)) {
16
+ error[key] = value;
17
+ }
18
+ return error;
19
+ }
20
+ function buildEmptyTurnResult() {
21
+ return {
22
+ text: "",
23
+ usage: null,
24
+ items: [],
25
+ events: [],
26
+ };
27
+ }
28
+ function normalizeKimiBackend(backend) {
29
+ const normalized = String(backend || "").trim().toLowerCase();
30
+ if (normalized === "kimi-cli" || normalized === "kimi-code") {
31
+ return "kimi";
32
+ }
33
+ return normalized || "kimi";
34
+ }
35
+ function injectJsonSchemaPrompt(promptText, jsonSchema) {
36
+ const schemaText = typeof jsonSchema === "string" ? jsonSchema : JSON.stringify(jsonSchema, null, 2);
37
+ return `You must respond with valid JSON that strictly conforms to the following JSON Schema. Do not include any markdown formatting or explanation outside the JSON object.
38
+
39
+ JSON Schema:
40
+ ${schemaText}
41
+
42
+ ${promptText}`;
43
+ }
44
+ function buildHistoryPrompt(history, promptText) {
45
+ let effectivePrompt = String(promptText || "").trim();
46
+ if (!effectivePrompt) {
47
+ return "";
48
+ }
49
+ const historyText = Array.isArray(history)
50
+ ? history
51
+ .map((item) => {
52
+ const role = String(item?.role || "").toLowerCase() === "assistant" ? "Assistant" : "User";
53
+ const content = String(item?.content || "").trim();
54
+ return content ? `${role}: ${content}` : "";
55
+ })
56
+ .filter(Boolean)
57
+ .join("\n\n")
58
+ : "";
59
+ if (!historyText) {
60
+ return effectivePrompt;
61
+ }
62
+ return [
63
+ "Continue the existing conversation with this history.",
64
+ "",
65
+ historyText,
66
+ "",
67
+ `User: ${effectivePrompt}`,
68
+ ].join("\n");
69
+ }
70
+ function statusLineForPhase(phase) {
71
+ switch (phase) {
72
+ case "turn_started":
73
+ return "Kimi is working on it";
74
+ case "reasoning":
75
+ return "Kimi is thinking";
76
+ case "command_execution":
77
+ return "Kimi is calling a tool";
78
+ case "message_aggregation":
79
+ return "Kimi is writing the reply";
80
+ case "turn_completed":
81
+ return "Kimi finished";
82
+ case "turn_failed":
83
+ return "Kimi failed";
84
+ default:
85
+ return "Kimi is working";
86
+ }
87
+ }
88
+ function filterKimiPrintBaseArgs(args) {
89
+ const filtered = [];
90
+ let skipNext = false;
91
+ for (const rawArg of Array.isArray(args) ? args : []) {
92
+ const arg = String(rawArg || "");
93
+ if (!arg) {
94
+ continue;
95
+ }
96
+ if (skipNext) {
97
+ skipNext = false;
98
+ continue;
99
+ }
100
+ if (arg === "--wire" || arg === "--print" || arg === "--final-message-only") {
101
+ continue;
102
+ }
103
+ if (arg === "--input-format" ||
104
+ arg === "--output-format" ||
105
+ arg === "--prompt" ||
106
+ arg === "--command" ||
107
+ arg === "--session" ||
108
+ arg === "--work-dir" ||
109
+ arg === "--model") {
110
+ skipNext = true;
111
+ continue;
112
+ }
113
+ if (arg.startsWith("--input-format=") ||
114
+ arg.startsWith("--output-format=") ||
115
+ arg.startsWith("--prompt=") ||
116
+ arg.startsWith("--command=") ||
117
+ arg.startsWith("--session=") ||
118
+ arg.startsWith("--work-dir=") ||
119
+ arg.startsWith("--model=")) {
120
+ continue;
121
+ }
122
+ filtered.push(arg);
123
+ }
124
+ return filtered;
125
+ }
126
+ function normalizeTextContent(content) {
127
+ if (typeof content === "string") {
128
+ return content;
129
+ }
130
+ if (Array.isArray(content)) {
131
+ return content
132
+ .map((part) => (part?.type === "text" && typeof part.text === "string" ? part.text : ""))
133
+ .filter(Boolean)
134
+ .join("");
135
+ }
136
+ return "";
137
+ }
138
+ function guessMimeType(filePath) {
139
+ const ext = path.extname(String(filePath || "")).toLowerCase();
140
+ switch (ext) {
141
+ case ".png":
142
+ return "image/png";
143
+ case ".jpg":
144
+ case ".jpeg":
145
+ return "image/jpeg";
146
+ case ".gif":
147
+ return "image/gif";
148
+ case ".webp":
149
+ return "image/webp";
150
+ case ".bmp":
151
+ return "image/bmp";
152
+ case ".svg":
153
+ return "image/svg+xml";
154
+ default:
155
+ return "application/octet-stream";
156
+ }
157
+ }
158
+ function filePathToDataUri(filePath) {
159
+ const buffer = fs.readFileSync(filePath);
160
+ const mimeType = guessMimeType(filePath);
161
+ return `data:${mimeType};base64,${buffer.toString("base64")}`;
162
+ }
163
+ export class KimiPrintSession extends EventEmitter {
164
+ constructor(backend, options = {}) {
165
+ super();
166
+ this.backend = normalizeKimiBackend(backend);
167
+ this.options = options;
168
+ this.logger = normalizeLogger(options.logger);
169
+ this.cwd =
170
+ typeof options.cwd === "string" && options.cwd.trim()
171
+ ? options.cwd.trim()
172
+ : process.cwd();
173
+ this.resumeSessionId = typeof options.resumeSessionId === "string" ? options.resumeSessionId.trim() : "";
174
+ this.sessionId = this.resumeSessionId || randomUUID();
175
+ this.sessionInfo = {
176
+ backend: this.backend,
177
+ sessionId: this.sessionId,
178
+ model: typeof options.model === "string" && options.model.trim()
179
+ ? options.model.trim()
180
+ : this.backend,
181
+ };
182
+ this.history = Array.isArray(options.initialHistory) ? [...options.initialHistory] : [];
183
+ this.pendingHistorySeed = this.history.length > 0;
184
+ this.closeRequested = false;
185
+ this.closed = false;
186
+ this.currentTurn = null;
187
+ this.currentTurnStatus = null;
188
+ this.sessionAnnounced = false;
189
+ this.sessionMessageHandler = null;
190
+ this.workingStatusHandler = null;
191
+ this.activeReplyTarget = "";
192
+ this.lastReplyTarget = "";
193
+ this.turnDeadlineMs = getBoundedEnvInt("CONDUCTOR_TURN_DEADLINE_MS", DEFAULT_TURN_DEADLINE_MS, MIN_TURN_DEADLINE_MS, MAX_TURN_DEADLINE_MS);
194
+ const envConfig = loadEnvConfig(options.configFile);
195
+ const proxyEnv = proxyToEnv(envConfig);
196
+ this.env = {
197
+ ...(envConfig && typeof envConfig === "object" ? envConfig : {}),
198
+ ...proxyEnv,
199
+ ...(options.env && typeof options.env === "object" ? options.env : {}),
200
+ };
201
+ const commandLine = process.env.CONDUCTOR_KIMI_COMMAND ||
202
+ options.commandLine ||
203
+ process.env.CONDUCTOR_CLI_COMMAND ||
204
+ DEFAULT_KIMI_COMMAND;
205
+ const { command, args } = parseCommandParts(commandLine);
206
+ if (!command) {
207
+ throw new Error("Invalid kimi print command");
208
+ }
209
+ this.command = command;
210
+ this.baseArgs = filterKimiPrintBaseArgs(args);
211
+ }
212
+ writeLog(message) {
213
+ emitLog(this.logger, message);
214
+ }
215
+ trace(message) {
216
+ this.writeLog(`[${this.backend}] [kimi-print] ${message}`);
217
+ }
218
+ get threadId() {
219
+ return this.sessionId;
220
+ }
221
+ get threadOptions() {
222
+ return {
223
+ model: this.sessionInfo?.model ||
224
+ (typeof this.options.model === "string" && this.options.model.trim()
225
+ ? this.options.model.trim()
226
+ : this.backend),
227
+ };
228
+ }
229
+ buildManualResumeCommand() {
230
+ return `kimi --work-dir ${this.cwd} --session ${this.sessionId}`;
231
+ }
232
+ getSnapshot() {
233
+ return {
234
+ backend: this.backend,
235
+ provider: KIMI_PRINT_PROVIDER_VARIANT,
236
+ cwd: this.cwd,
237
+ sessionId: this.sessionId || undefined,
238
+ sessionInfo: this.getSessionInfo(),
239
+ useSessionFileReplyStream: this.usesSessionFileReplyStream(),
240
+ resumeReady: Boolean(this.sessionId),
241
+ manualResume: this.sessionId
242
+ ? {
243
+ ready: true,
244
+ command: this.buildManualResumeCommand(),
245
+ }
246
+ : null,
247
+ currentTurnStatus: this.getCurrentTurnStatus(),
248
+ pid: this.currentTurn?.child?.pid || undefined,
249
+ };
250
+ }
251
+ getSessionInfo() {
252
+ return this.sessionInfo ? { ...this.sessionInfo } : null;
253
+ }
254
+ getCurrentTurnStatus() {
255
+ return this.currentTurnStatus ? { ...this.currentTurnStatus } : null;
256
+ }
257
+ async ensureSessionInfo() {
258
+ this.announceSession();
259
+ return this.getSessionInfo();
260
+ }
261
+ async getSessionUsageSummary() {
262
+ return {
263
+ sessionId: this.sessionId || undefined,
264
+ sessionFilePath: undefined,
265
+ tokenUsagePercent: undefined,
266
+ contextUsagePercent: undefined,
267
+ tokenUsage: null,
268
+ rateLimits: null,
269
+ manualResume: this.sessionId
270
+ ? {
271
+ ready: true,
272
+ command: this.buildManualResumeCommand(),
273
+ }
274
+ : null,
275
+ };
276
+ }
277
+ usesSessionFileReplyStream() {
278
+ return true;
279
+ }
280
+ setSessionMessageHandler(handler) {
281
+ this.sessionMessageHandler = typeof handler === "function" ? handler : null;
282
+ }
283
+ setWorkingStatusHandler(handler) {
284
+ this.workingStatusHandler = typeof handler === "function" ? handler : null;
285
+ }
286
+ setSessionReplyTarget(replyTo) {
287
+ const normalizedReplyTo = typeof replyTo === "string" ? replyTo.trim() : "";
288
+ this.activeReplyTarget = normalizedReplyTo;
289
+ if (normalizedReplyTo) {
290
+ this.lastReplyTarget = normalizedReplyTo;
291
+ }
292
+ }
293
+ getCurrentReplyTarget() {
294
+ return this.activeReplyTarget || this.lastReplyTarget || undefined;
295
+ }
296
+ announceSession() {
297
+ if (this.sessionAnnounced) {
298
+ return;
299
+ }
300
+ this.sessionAnnounced = true;
301
+ this.emit("session", this.getSessionInfo());
302
+ }
303
+ createSessionClosedError() {
304
+ return createTurnError("Kimi print session closed", {
305
+ reason: "session_closed",
306
+ });
307
+ }
308
+ updateCurrentTurnStatus(payload) {
309
+ const updatedAtMs = Date.now();
310
+ this.currentTurnStatus = {
311
+ source: KIMI_PRINT_PROVIDER_VARIANT,
312
+ replyTo: this.getCurrentReplyTarget(),
313
+ thread_id: this.sessionId || undefined,
314
+ session_id: this.sessionId || undefined,
315
+ ...payload,
316
+ updated_at: new Date(updatedAtMs).toISOString(),
317
+ };
318
+ }
319
+ async emitWorkingStatus(payload, onProgress = null) {
320
+ this.updateCurrentTurnStatus(payload);
321
+ const normalized = this.getCurrentTurnStatus();
322
+ if (typeof onProgress === "function") {
323
+ await onProgress(normalized);
324
+ }
325
+ if (typeof this.workingStatusHandler === "function") {
326
+ await this.workingStatusHandler(normalized);
327
+ }
328
+ this.emit("working_status", normalized);
329
+ return normalized;
330
+ }
331
+ async emitAssistantMessage(text) {
332
+ const normalizedText = typeof text === "string" ? text : "";
333
+ if (!normalizedText) {
334
+ return;
335
+ }
336
+ const payload = {
337
+ text: normalizedText,
338
+ preserveWhitespace: true,
339
+ replyTo: this.getCurrentReplyTarget(),
340
+ sessionId: this.sessionId || undefined,
341
+ backend: this.backend,
342
+ provider: KIMI_PRINT_PROVIDER_VARIANT,
343
+ };
344
+ if (typeof this.sessionMessageHandler === "function") {
345
+ await this.sessionMessageHandler(payload);
346
+ }
347
+ this.emit("assistant_message", payload);
348
+ }
349
+ buildPrompt(promptText) {
350
+ const normalizedPrompt = String(promptText || "").trim();
351
+ if (!normalizedPrompt) {
352
+ return "";
353
+ }
354
+ if (!this.pendingHistorySeed) {
355
+ return normalizedPrompt;
356
+ }
357
+ this.pendingHistorySeed = false;
358
+ return buildHistoryPrompt(this.history, normalizedPrompt);
359
+ }
360
+ buildPrintArgs() {
361
+ const args = [...this.baseArgs];
362
+ args.push("--print");
363
+ args.push("--input-format=stream-json");
364
+ args.push("--output-format=stream-json");
365
+ args.push(`--work-dir=${this.cwd}`);
366
+ if (this.sessionId) {
367
+ args.push(`--session=${this.sessionId}`);
368
+ }
369
+ if (typeof this.options.model === "string" && this.options.model.trim()) {
370
+ args.push(`--model=${this.options.model.trim()}`);
371
+ }
372
+ return args;
373
+ }
374
+ buildUserMessage(promptText, { useInitialImages = false } = {}) {
375
+ const images = useInitialImages && Array.isArray(this.options.initialImages)
376
+ ? this.options.initialImages.filter((item) => typeof item === "string" && item.trim())
377
+ : [];
378
+ if (images.length === 0) {
379
+ return {
380
+ role: "user",
381
+ content: promptText,
382
+ };
383
+ }
384
+ const content = [];
385
+ if (promptText) {
386
+ content.push({ type: "text", text: promptText });
387
+ }
388
+ for (const imagePath of images) {
389
+ content.push({
390
+ type: "image_url",
391
+ image_url: {
392
+ url: filePathToDataUri(imagePath),
393
+ },
394
+ });
395
+ }
396
+ return {
397
+ role: "user",
398
+ content,
399
+ };
400
+ }
401
+ maybeEmitAuthRequired(stderrTail) {
402
+ const lastMessage = Array.isArray(stderrTail) ? String(stderrTail.filter(Boolean).at(-1) || "") : "";
403
+ const normalized = lastMessage.toLowerCase();
404
+ if (!normalized.includes("login") &&
405
+ !normalized.includes("auth") &&
406
+ !normalized.includes("api key") &&
407
+ !normalized.includes("credential") &&
408
+ !normalized.includes("llm is not set")) {
409
+ return;
410
+ }
411
+ this.emit("auth_required", {
412
+ reason: "login_required",
413
+ message: lastMessage || "Kimi authentication required",
414
+ });
415
+ }
416
+ async runTurn(promptText, { useInitialImages = false, onProgress = null, jsonSchema = null } = {}) {
417
+ if (this.closeRequested || this.closed) {
418
+ throw this.createSessionClosedError();
419
+ }
420
+ if (this.currentTurn) {
421
+ throw createTurnError("Kimi print turn already running", {
422
+ reason: "turn_already_running",
423
+ });
424
+ }
425
+ let effectivePrompt = this.buildPrompt(promptText);
426
+ const imagePaths = useInitialImages && Array.isArray(this.options.initialImages)
427
+ ? this.options.initialImages.filter((item) => typeof item === "string" && item.trim())
428
+ : [];
429
+ if (jsonSchema && typeof jsonSchema === "object") {
430
+ const promptWithSchema = effectivePrompt || (imagePaths.length > 0 ? "Analyze the attached images." : "");
431
+ if (promptWithSchema) {
432
+ effectivePrompt = injectJsonSchemaPrompt(promptWithSchema, jsonSchema);
433
+ }
434
+ }
435
+ if (!effectivePrompt && imagePaths.length === 0) {
436
+ return buildEmptyTurnResult();
437
+ }
438
+ this.announceSession();
439
+ this.history.push({ role: "user", content: String(promptText || "") });
440
+ const currentTurn = {
441
+ child: null,
442
+ fullText: "",
443
+ items: [],
444
+ stderrTail: [],
445
+ settled: false,
446
+ };
447
+ this.currentTurn = currentTurn;
448
+ try {
449
+ await this.emitWorkingStatus({
450
+ phase: "turn_started",
451
+ reply_in_progress: true,
452
+ status_line: statusLineForPhase("turn_started"),
453
+ }, onProgress);
454
+ const args = this.buildPrintArgs();
455
+ this.trace(`spawn ${[this.command, ...args].join(" ")}`);
456
+ const result = await new Promise((resolve, reject) => {
457
+ const child = spawn(this.command, args, {
458
+ cwd: this.cwd,
459
+ env: {
460
+ ...process.env,
461
+ PWD: this.cwd,
462
+ ...this.env,
463
+ },
464
+ stdio: ["pipe", "pipe", "pipe"],
465
+ });
466
+ currentTurn.child = child;
467
+ const stdoutReader = readline.createInterface({ input: child.stdout });
468
+ const stderrReader = readline.createInterface({ input: child.stderr });
469
+ let timeoutId = null;
470
+ const settle = (error, value = null) => {
471
+ if (currentTurn.settled) {
472
+ return;
473
+ }
474
+ currentTurn.settled = true;
475
+ if (timeoutId) {
476
+ clearTimeout(timeoutId);
477
+ }
478
+ stdoutReader.close();
479
+ stderrReader.close();
480
+ if (error) {
481
+ reject(error);
482
+ return;
483
+ }
484
+ resolve(value);
485
+ };
486
+ timeoutId = setTimeout(() => {
487
+ try {
488
+ child.kill("SIGTERM");
489
+ }
490
+ catch {
491
+ // ignore
492
+ }
493
+ settle(createTurnError("Kimi print turn timed out", {
494
+ reason: "turn_timeout",
495
+ }));
496
+ }, this.turnDeadlineMs);
497
+ stdoutReader.on("line", (line) => {
498
+ const normalizedLine = String(line || "").trim();
499
+ if (!normalizedLine) {
500
+ return;
501
+ }
502
+ let payload = null;
503
+ try {
504
+ payload = JSON.parse(normalizedLine);
505
+ }
506
+ catch {
507
+ payload = { role: "raw", content: normalizedLine };
508
+ }
509
+ currentTurn.items.push(payload);
510
+ const role = String(payload?.role || "").trim().toLowerCase();
511
+ if (role === "assistant") {
512
+ const assistantText = normalizeTextContent(payload.content);
513
+ if (Array.isArray(payload?.tool_calls) && payload.tool_calls.length > 0) {
514
+ void this.emitWorkingStatus({
515
+ phase: "command_execution",
516
+ reply_in_progress: true,
517
+ status_line: statusLineForPhase("command_execution"),
518
+ }, onProgress);
519
+ }
520
+ else {
521
+ void this.emitWorkingStatus({
522
+ phase: "message_aggregation",
523
+ reply_in_progress: true,
524
+ status_line: statusLineForPhase("message_aggregation"),
525
+ }, onProgress);
526
+ }
527
+ if (assistantText) {
528
+ currentTurn.fullText += assistantText;
529
+ void this.emitAssistantMessage(assistantText);
530
+ }
531
+ return;
532
+ }
533
+ if (role === "tool") {
534
+ void this.emitWorkingStatus({
535
+ phase: "command_execution",
536
+ reply_in_progress: true,
537
+ status_line: statusLineForPhase("command_execution"),
538
+ }, onProgress);
539
+ }
540
+ });
541
+ stderrReader.on("line", (line) => {
542
+ const normalizedLine = String(line || "");
543
+ if (!normalizedLine.trim()) {
544
+ return;
545
+ }
546
+ currentTurn.stderrTail.push(normalizedLine);
547
+ if (currentTurn.stderrTail.length > 20) {
548
+ currentTurn.stderrTail.shift();
549
+ }
550
+ this.writeLog(`[kimi-print] stderr ${sanitizeForLog(normalizedLine, 300)}`);
551
+ });
552
+ child.on("error", (error) => {
553
+ settle(error);
554
+ });
555
+ child.on("exit", (code, signal) => {
556
+ if (this.closeRequested) {
557
+ settle(this.createSessionClosedError());
558
+ return;
559
+ }
560
+ if (code !== 0) {
561
+ this.maybeEmitAuthRequired(currentTurn.stderrTail);
562
+ const stderrSummary = sanitizeForLog(currentTurn.stderrTail.filter(Boolean).at(-1), 200);
563
+ settle(createTurnError(stderrSummary ? `Kimi print failed: ${stderrSummary}` : "Kimi print failed", {
564
+ reason: code === 75 ? "retryable_turn_failed" : "turn_failed",
565
+ retryable: code === 75,
566
+ code,
567
+ signal,
568
+ stderr: [...currentTurn.stderrTail],
569
+ }));
570
+ return;
571
+ }
572
+ settle(null, {
573
+ text: currentTurn.fullText,
574
+ items: [...currentTurn.items],
575
+ });
576
+ });
577
+ const userMessage = this.buildUserMessage(effectivePrompt, { useInitialImages });
578
+ child.stdin.write(`${JSON.stringify(userMessage)}\n`);
579
+ child.stdin.end();
580
+ });
581
+ if (result?.text) {
582
+ this.history.push({ role: "assistant", content: result.text });
583
+ }
584
+ this.activeReplyTarget = "";
585
+ await this.emitWorkingStatus({
586
+ phase: "turn_completed",
587
+ reply_in_progress: false,
588
+ status_line: statusLineForPhase("turn_completed"),
589
+ }, onProgress);
590
+ return {
591
+ text: result?.text || "",
592
+ usage: null,
593
+ items: result?.items || [],
594
+ events: result?.items || [],
595
+ provider: this.backend,
596
+ metadata: {
597
+ source: KIMI_PRINT_PROVIDER_VARIANT,
598
+ sessionId: this.sessionId || undefined,
599
+ },
600
+ };
601
+ }
602
+ catch (error) {
603
+ if (!this.closeRequested && error?.reason !== "session_closed") {
604
+ await this.emitWorkingStatus({
605
+ phase: "turn_failed",
606
+ reply_in_progress: false,
607
+ status_line: statusLineForPhase("turn_failed"),
608
+ status_done_line: error?.message || "Kimi print failed",
609
+ }, onProgress);
610
+ }
611
+ throw error;
612
+ }
613
+ finally {
614
+ this.currentTurn = null;
615
+ }
616
+ }
617
+ async close() {
618
+ if (this.closed) {
619
+ return;
620
+ }
621
+ this.closeRequested = true;
622
+ this.closed = true;
623
+ const child = this.currentTurn?.child;
624
+ if (child) {
625
+ try {
626
+ child.kill("SIGTERM");
627
+ }
628
+ catch {
629
+ // ignore
630
+ }
631
+ }
632
+ }
633
+ }
@@ -164,9 +164,10 @@ export class OpencodeSdkSession extends EventEmitter<[never]> {
164
164
  handleTransportFailure(error: any): void;
165
165
  handleTransportExit(payload: any): void;
166
166
  interruptCurrentTurn(): Promise<boolean>;
167
- runTurn(promptText: any, { useInitialImages, onProgress }?: {
167
+ runTurn(promptText: any, { useInitialImages, onProgress, jsonSchema }?: {
168
168
  useInitialImages?: boolean | undefined;
169
169
  onProgress?: null | undefined;
170
+ jsonSchema?: null | undefined;
170
171
  }): Promise<{
171
172
  text: string;
172
173
  usage: null;
@@ -1136,7 +1136,7 @@ export class OpencodeSdkSession extends EventEmitter {
1136
1136
  }
1137
1137
  return true;
1138
1138
  }
1139
- async runTurn(promptText, { useInitialImages = false, onProgress = null } = {}) {
1139
+ async runTurn(promptText, { useInitialImages = false, onProgress = null, jsonSchema = null } = {}) {
1140
1140
  if (this.closeRequested) {
1141
1141
  throw this.createSessionClosedError();
1142
1142
  }
@@ -1213,6 +1213,9 @@ export class OpencodeSdkSession extends EventEmitter {
1213
1213
  variant: typeof this.options.variant === "string" && this.options.variant.trim()
1214
1214
  ? this.options.variant.trim()
1215
1215
  : undefined,
1216
+ format: jsonSchema && typeof jsonSchema === "object"
1217
+ ? { type: "json_schema", schema: jsonSchema }
1218
+ : undefined,
1216
1219
  parts: [
1217
1220
  {
1218
1221
  type: "text",
@@ -4,11 +4,17 @@ export function providerVariantForBackend(backend: any, options?: {}): Promise<a
4
4
  export function assertSupportedBackend(backend: any, options?: {}): Promise<any>;
5
5
  export function createLocalAiSession(backend: any, options?: {}): Promise<any>;
6
6
  export const DEFAULT_PROVIDER_VARIANT: "codex-app-server";
7
+ export const CODEX_EXEC_PROVIDER_VARIANT: "codex-exec";
7
8
  export const CLAUDE_PROVIDER_VARIANT: "claude-agent-sdk";
9
+ export const COPILOT_PROVIDER_VARIANT: "copilot-sdk";
8
10
  export const KIMI_PROVIDER_VARIANT: "kimi-cli-wire";
11
+ export const KIMI_PRINT_PROVIDER_VARIANT: "kimi-cli-print";
9
12
  export const OPENCODE_PROVIDER_VARIANT: "opencode-sdk";
10
13
  import { CodexAppServerSession } from "./providers/codex-app-server-session.js";
14
+ import { CodexExecSession } from "./providers/codex-exec-session.js";
11
15
  import { ClaudeAgentSdkSession } from "./providers/claude-agent-sdk-session.js";
16
+ import { CopilotSdkSession } from "./providers/copilot-sdk-session.js";
12
17
  import { KimiCliSession } from "./providers/kimi-cli-session.js";
18
+ import { KimiPrintSession } from "./providers/kimi-print-session.js";
13
19
  import { OpencodeSdkSession } from "./providers/opencode-sdk-session.js";
14
- export { CodexAppServerSession, ClaudeAgentSdkSession, KimiCliSession, OpencodeSdkSession };
20
+ export { CodexAppServerSession, CodexExecSession, ClaudeAgentSdkSession, CopilotSdkSession, KimiCliSession, KimiPrintSession, OpencodeSdkSession };