@love-moon/ai-sdk 0.2.38 → 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.
package/dist/client.d.ts CHANGED
@@ -55,10 +55,12 @@ export class RemoteAiSession extends EventEmitter<[never]> {
55
55
  setSessionReplyTarget(replyTo: any): void;
56
56
  ensureSessionInfo(): Promise<any>;
57
57
  getSessionUsageSummary(): Promise<any>;
58
+ interruptCurrentTurn(): Promise<any>;
58
59
  runTurn(promptText: any, options?: {}): Promise<any>;
59
60
  close(): Promise<void>;
60
- callWorker(method: any, args?: any[], { progressHandler }?: {
61
+ callWorker(method: any, args?: any[], { progressHandler, messageType }?: {
61
62
  progressHandler?: null | undefined;
63
+ messageType?: string | undefined;
62
64
  }): Promise<any>;
63
65
  handleWorkerStderr(line: any): void;
64
66
  handleWorkerLine(line: any): void;
@@ -104,6 +106,7 @@ declare class LocalAiSessionProxy extends EventEmitter<[never]> {
104
106
  setSessionReplyTarget(replyTarget: any): void;
105
107
  ensureSessionInfo(): Promise<any>;
106
108
  getSessionUsageSummary(): Promise<any>;
109
+ interruptCurrentTurn(): Promise<any>;
107
110
  runTurn(promptText: any, options?: {}): Promise<any>;
108
111
  close(): Promise<void>;
109
112
  }
package/dist/client.js CHANGED
@@ -168,6 +168,11 @@ export class RemoteAiSession extends EventEmitter {
168
168
  async getSessionUsageSummary() {
169
169
  return this.callWorker("getSessionUsageSummary", []);
170
170
  }
171
+ async interruptCurrentTurn() {
172
+ return this.callWorker("interruptCurrentTurn", [], {
173
+ messageType: "control",
174
+ });
175
+ }
171
176
  async runTurn(promptText, options = {}) {
172
177
  const { onProgress, ...restOptions } = options || {};
173
178
  return this.callWorker("runTurn", [promptText, restOptions], {
@@ -214,7 +219,7 @@ export class RemoteAiSession extends EventEmitter {
214
219
  await this.exitPromise;
215
220
  }
216
221
  }
217
- async callWorker(method, args = [], { progressHandler = null } = {}) {
222
+ async callWorker(method, args = [], { progressHandler = null, messageType = "request" } = {}) {
218
223
  if (this.closed) {
219
224
  throw createSessionClosedError();
220
225
  }
@@ -224,7 +229,7 @@ export class RemoteAiSession extends EventEmitter {
224
229
  }
225
230
  const requestId = this.nextRequestId++;
226
231
  const payload = {
227
- type: "request",
232
+ type: messageType,
228
233
  id: requestId,
229
234
  method,
230
235
  args,
@@ -566,6 +571,13 @@ class LocalAiSessionProxy extends EventEmitter {
566
571
  const session = await this.readyPromise;
567
572
  return await session.getSessionUsageSummary();
568
573
  }
574
+ async interruptCurrentTurn() {
575
+ const session = await this.readyPromise;
576
+ if (typeof session.interruptCurrentTurn === "function") {
577
+ return await session.interruptCurrentTurn();
578
+ }
579
+ return false;
580
+ }
569
581
  async runTurn(promptText, options = {}) {
570
582
  const session = await this.readyPromise;
571
583
  return await session.runTurn(promptText, options);
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { pathToFileURL } from "node:url";
3
3
  import { loadEnvConfig } from "./shared.js";
4
- const BUILT_IN_BACKENDS = new Set(["codex", "claude", "kimi", "opencode"]);
4
+ const BUILT_IN_BACKENDS = new Set(["codex", "claude", "copilot", "kimi", "opencode"]);
5
5
  const registryPromises = new Map();
6
6
  let externalProviderImportNonce = 0;
7
7
  function appendProviderModulePaths(parts, value) {
@@ -15,13 +15,49 @@ function appendProviderModulePaths(parts, value) {
15
15
  if (!raw) {
16
16
  return;
17
17
  }
18
- for (const item of raw.split(process.platform === "win32" ? ";" : ":")) {
18
+ for (const item of splitProviderModulePathString(raw)) {
19
19
  const normalized = item.trim();
20
20
  if (normalized) {
21
21
  parts.push(normalized);
22
22
  }
23
23
  }
24
24
  }
25
+ function looksLikeProviderModulePath(value) {
26
+ const normalized = String(value || "").trim();
27
+ if (!normalized) {
28
+ return false;
29
+ }
30
+ return (normalized.startsWith("/") ||
31
+ normalized.startsWith("./") ||
32
+ normalized.startsWith("../") ||
33
+ normalized.startsWith("~/") ||
34
+ normalized.startsWith("file:") ||
35
+ normalized.includes("/") ||
36
+ normalized.includes("\\") ||
37
+ /\.[cm]?[jt]sx?$/i.test(normalized) ||
38
+ /^[A-Za-z]:[\\/]/.test(normalized));
39
+ }
40
+ function splitProviderModulePathString(raw) {
41
+ const normalized = String(raw || "").trim();
42
+ if (!normalized) {
43
+ return [];
44
+ }
45
+ const platformParts = normalized
46
+ .split(path.delimiter)
47
+ .map((item) => item.trim())
48
+ .filter(Boolean);
49
+ if (platformParts.length > 1 || !normalized.includes(",")) {
50
+ return platformParts;
51
+ }
52
+ const commaParts = normalized
53
+ .split(",")
54
+ .map((item) => item.trim())
55
+ .filter(Boolean);
56
+ if (commaParts.length > 1 && commaParts.every(looksLikeProviderModulePath)) {
57
+ return commaParts;
58
+ }
59
+ return platformParts;
60
+ }
25
61
  function listProviderModulePathsFromValue(rawValue) {
26
62
  const parts = [];
27
63
  appendProviderModulePaths(parts, rawValue);
@@ -121,10 +121,11 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
121
121
  handleSdkMessage(message: any, currentTurn: any, { onProgress }: {
122
122
  onProgress: any;
123
123
  }): Promise<void>;
124
- interruptCurrentTurn(): Promise<void>;
125
- runTurn(promptText: any, { useInitialImages, onProgress }?: {
124
+ interruptCurrentTurn(): Promise<boolean>;
125
+ runTurn(promptText: any, { useInitialImages, onProgress, jsonSchema }?: {
126
126
  useInitialImages?: boolean | undefined;
127
127
  onProgress?: null | undefined;
128
+ jsonSchema?: null | undefined;
128
129
  }): Promise<{
129
130
  text: string;
130
131
  usage: null;
@@ -143,6 +144,7 @@ export class ClaudeAgentSdkSession extends EventEmitter<[never]> {
143
144
  sessionId: any;
144
145
  totalCostUsd: number | undefined;
145
146
  modelUsage: any;
147
+ structuredOutput: any;
146
148
  };
147
149
  }>;
148
150
  close(): Promise<void>;
@@ -550,6 +550,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
550
550
  "systemPrompt",
551
551
  "thinking",
552
552
  "tools",
553
+ "outputFormat",
553
554
  ];
554
555
  for (const key of passthroughKeys) {
555
556
  const value = this.options[key];
@@ -680,7 +681,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
680
681
  async interruptCurrentTurn() {
681
682
  const currentTurn = this.currentTurn;
682
683
  if (!currentTurn) {
683
- return;
684
+ return false;
684
685
  }
685
686
  try {
686
687
  await currentTurn.query?.interrupt?.();
@@ -700,8 +701,9 @@ export class ClaudeAgentSdkSession extends EventEmitter {
700
701
  catch {
701
702
  // best effort
702
703
  }
704
+ return true;
703
705
  }
704
- async runTurn(promptText, { useInitialImages = false, onProgress = null } = {}) {
706
+ async runTurn(promptText, { useInitialImages = false, onProgress = null, jsonSchema = null } = {}) {
705
707
  if (this.closeRequested) {
706
708
  throw this.createSessionClosedError();
707
709
  }
@@ -744,6 +746,10 @@ export class ClaudeAgentSdkSession extends EventEmitter {
744
746
  abortController.abort();
745
747
  currentTurn.query?.close?.();
746
748
  });
749
+ const previousOutputFormat = this.options.outputFormat;
750
+ if (jsonSchema && typeof jsonSchema === "object") {
751
+ this.options.outputFormat = { type: "json_schema", schema: jsonSchema };
752
+ }
747
753
  try {
748
754
  await this.emitWorkingStatus({
749
755
  phase: "turn_started",
@@ -786,9 +792,14 @@ export class ClaudeAgentSdkSession extends EventEmitter {
786
792
  : [],
787
793
  });
788
794
  }
789
- const responseText = normalizeText(resultMessage.result) ||
790
- currentTurn.fullText ||
791
- extractAssistantText(currentTurn.items.find((item) => item?.type === "assistant")?.message);
795
+ const structuredOutput = jsonSchema && resultMessage.structured_output && typeof resultMessage.structured_output === "object"
796
+ ? resultMessage.structured_output
797
+ : null;
798
+ const responseText = structuredOutput !== null
799
+ ? JSON.stringify(structuredOutput)
800
+ : normalizeText(resultMessage.result) ||
801
+ currentTurn.fullText ||
802
+ extractAssistantText(currentTurn.items.find((item) => item?.type === "assistant")?.message);
792
803
  if (!currentTurn.emittedAssistantMessage && responseText) {
793
804
  await this.emitAssistantMessage(responseText);
794
805
  }
@@ -814,6 +825,7 @@ export class ClaudeAgentSdkSession extends EventEmitter {
814
825
  ? Number(resultMessage.total_cost_usd)
815
826
  : undefined,
816
827
  modelUsage: resultMessage.modelUsage ? { ...resultMessage.modelUsage } : undefined,
828
+ structuredOutput: structuredOutput !== null ? structuredOutput : undefined,
817
829
  },
818
830
  };
819
831
  }
@@ -835,6 +847,14 @@ export class ClaudeAgentSdkSession extends EventEmitter {
835
847
  throw error;
836
848
  }
837
849
  finally {
850
+ if (jsonSchema && typeof jsonSchema === "object") {
851
+ if (previousOutputFormat !== undefined) {
852
+ this.options.outputFormat = previousOutputFormat;
853
+ }
854
+ else {
855
+ delete this.options.outputFormat;
856
+ }
857
+ }
838
858
  this.activeReplyTarget = "";
839
859
  if (this.currentTurn === currentTurn) {
840
860
  this.currentTurn = null;
@@ -149,9 +149,10 @@ export class CodexAppServerSession extends EventEmitter<[never]> {
149
149
  handleTransportFailure(error: any): void;
150
150
  handleTransportExit(payload: any): void;
151
151
  maybeEmitAuthRequired(error: any): void;
152
- interruptCurrentTurn(): Promise<void>;
153
- runTurn(promptText: any, { useInitialImages }?: {
152
+ interruptCurrentTurn(): Promise<boolean>;
153
+ runTurn(promptText: any, { useInitialImages, jsonSchema }?: {
154
154
  useInitialImages?: boolean | undefined;
155
+ jsonSchema?: null | undefined;
155
156
  }): Promise<{
156
157
  text: string;
157
158
  usage: null;
@@ -38,6 +38,15 @@ function buildTurnInput(promptText) {
38
38
  },
39
39
  ];
40
40
  }
41
+ function injectJsonSchemaPrompt(promptText, jsonSchema) {
42
+ const schemaText = typeof jsonSchema === "string" ? jsonSchema : JSON.stringify(jsonSchema, null, 2);
43
+ 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.
44
+
45
+ JSON Schema:
46
+ ${schemaText}
47
+
48
+ ${promptText}`;
49
+ }
41
50
  function normalizeItemId(value) {
42
51
  return typeof value === "string" ? value.trim() : "";
43
52
  }
@@ -198,7 +207,11 @@ export class CodexAppServerSession extends EventEmitter {
198
207
  const extraEnv = envConfig && typeof envConfig === "object" ? { ...envConfig, ...proxyEnv } : proxyEnv;
199
208
  this.transport = new CodexAppServerTransport({
200
209
  cwd: this.cwd,
201
- env: extraEnv,
210
+ env: {
211
+ ...extraEnv,
212
+ ...(options.env && typeof options.env === "object" ? options.env : {}),
213
+ },
214
+ ignoreCodexApiKey: options.ignoreCodexApiKey === true,
202
215
  logger: {
203
216
  log: (message) => {
204
217
  this.writeLog(message);
@@ -851,23 +864,28 @@ export class CodexAppServerSession extends EventEmitter {
851
864
  async interruptCurrentTurn() {
852
865
  const currentTurn = this.currentTurn;
853
866
  if (!currentTurn || !this.sessionId || !currentTurn.turnId) {
854
- return;
867
+ return false;
855
868
  }
856
869
  try {
857
870
  await this.transport.request("turn/interrupt", {
858
871
  threadId: this.sessionId,
859
872
  turnId: currentTurn.turnId,
860
873
  });
874
+ return true;
861
875
  }
862
876
  catch {
863
877
  // best effort
864
878
  }
879
+ return false;
865
880
  }
866
- async runTurn(promptText, { useInitialImages = false } = {}) {
881
+ async runTurn(promptText, { useInitialImages = false, jsonSchema = null } = {}) {
867
882
  if (this.closeRequested) {
868
883
  throw this.createSessionClosedError();
869
884
  }
870
- const effectivePrompt = this.buildPrompt(promptText, { useInitialImages });
885
+ let effectivePrompt = this.buildPrompt(promptText, { useInitialImages });
886
+ if (jsonSchema && typeof jsonSchema === "object" && effectivePrompt) {
887
+ effectivePrompt = injectJsonSchemaPrompt(effectivePrompt, jsonSchema);
888
+ }
871
889
  if (!effectivePrompt) {
872
890
  return {
873
891
  text: "",
@@ -0,0 +1,116 @@
1
+ export class CodexExecSession 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
+ sessionInfo: {
10
+ backend: string;
11
+ sessionId: any;
12
+ model: any;
13
+ };
14
+ history: any[];
15
+ closeRequested: boolean;
16
+ closed: boolean;
17
+ ignoreCodexApiKey: boolean;
18
+ currentTurn: {
19
+ child: null;
20
+ stdoutEvents: never[];
21
+ stderrTail: never[];
22
+ settled: boolean;
23
+ } | null;
24
+ currentTurnStatus: any;
25
+ sessionAnnounced: boolean;
26
+ sessionMessageHandler: any;
27
+ workingStatusHandler: any;
28
+ activeReplyTarget: string;
29
+ lastReplyTarget: string;
30
+ turnDeadlineMs: any;
31
+ env: any;
32
+ command: string;
33
+ baseArgs: string[];
34
+ writeLog(message: any): void;
35
+ trace(message: any): void;
36
+ get threadId(): any;
37
+ get threadOptions(): {
38
+ model: any;
39
+ modelProvider: any;
40
+ };
41
+ getSnapshot(): {
42
+ backend: string;
43
+ provider: string;
44
+ cwd: any;
45
+ sessionId: any;
46
+ sessionInfo: {
47
+ backend: string;
48
+ sessionId: any;
49
+ model: any;
50
+ } | null;
51
+ useSessionFileReplyStream: boolean;
52
+ resumeReady: boolean;
53
+ manualResume: null;
54
+ currentTurnStatus: any;
55
+ pid: any;
56
+ };
57
+ getSessionInfo(): {
58
+ backend: string;
59
+ sessionId: any;
60
+ model: any;
61
+ } | null;
62
+ getCurrentTurnStatus(): any;
63
+ ensureSessionInfo(): Promise<{
64
+ backend: string;
65
+ sessionId: any;
66
+ model: any;
67
+ } | null>;
68
+ getSessionUsageSummary(): Promise<{
69
+ sessionId: any;
70
+ sessionFilePath: undefined;
71
+ tokenUsagePercent: undefined;
72
+ contextUsagePercent: undefined;
73
+ tokenUsage: null;
74
+ rateLimits: null;
75
+ manualResume: null;
76
+ }>;
77
+ usesSessionFileReplyStream(): boolean;
78
+ setSessionMessageHandler(handler: any): void;
79
+ setWorkingStatusHandler(handler: any): void;
80
+ setSessionReplyTarget(replyTo: any): void;
81
+ getCurrentReplyTarget(): string | undefined;
82
+ announceSession(): void;
83
+ createSessionClosedError(): Error;
84
+ updateCurrentTurnStatus(payload: any): void;
85
+ emitWorkingStatus(payload: any, onProgress?: null): Promise<any>;
86
+ emitAssistantMessage(text: any): Promise<void>;
87
+ buildPrompt(promptText: any): string;
88
+ buildExecArgs({ useInitialImages, schemaFilePath, lastMessageFilePath }?: {
89
+ useInitialImages?: boolean | undefined;
90
+ schemaFilePath?: string | undefined;
91
+ lastMessageFilePath?: string | undefined;
92
+ }): string[];
93
+ maybeEmitAuthRequired(stderrTail: any): void;
94
+ runTurn(promptText: any, { useInitialImages, onProgress, jsonSchema }?: {
95
+ useInitialImages?: boolean | undefined;
96
+ onProgress?: null | undefined;
97
+ jsonSchema?: null | undefined;
98
+ }): Promise<{
99
+ text: string;
100
+ usage: null;
101
+ items: never[];
102
+ events: never[];
103
+ } | {
104
+ text: any;
105
+ usage: null;
106
+ items: any;
107
+ events: any;
108
+ provider: string;
109
+ metadata: {
110
+ source: string;
111
+ sessionId: any;
112
+ };
113
+ }>;
114
+ close(): Promise<void>;
115
+ }
116
+ import { EventEmitter } from "node:events";