@love-moon/ai-sdk 0.8.0 → 0.10.0

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/built-in-backends.d.ts +1 -0
  3. package/dist/built-in-backends.js +6 -0
  4. package/dist/context-files.d.ts +16 -0
  5. package/dist/context-files.js +86 -0
  6. package/dist/index.d.ts +4 -1
  7. package/dist/index.js +4 -1
  8. package/dist/manager/index.d.ts +1 -0
  9. package/dist/manager/index.js +1 -0
  10. package/dist/manager/install.js +29 -2
  11. package/dist/manager/manager.d.ts +5 -1
  12. package/dist/manager/manager.js +8 -0
  13. package/dist/manager/network.js +4 -2
  14. package/dist/manager/quota/dsh.d.ts +55 -0
  15. package/dist/manager/quota/dsh.js +149 -0
  16. package/dist/manager/types.d.ts +25 -1
  17. package/dist/media-adapters.d.ts +34 -0
  18. package/dist/media-adapters.js +57 -0
  19. package/dist/media-input.d.ts +21 -0
  20. package/dist/media-input.js +197 -0
  21. package/dist/providers/chat-web-session.d.ts +7 -1
  22. package/dist/providers/chat-web-session.js +12 -1
  23. package/dist/providers/claude-agent-sdk-session.d.ts +10 -1
  24. package/dist/providers/claude-agent-sdk-session.js +24 -4
  25. package/dist/providers/codex-app-server-session.d.ts +19 -21
  26. package/dist/providers/codex-app-server-session.js +124 -12
  27. package/dist/providers/codex-exec-session.d.ts +8 -3
  28. package/dist/providers/codex-exec-session.js +13 -13
  29. package/dist/providers/copilot-sdk-session.d.ts +6 -1
  30. package/dist/providers/copilot-sdk-session.js +10 -3
  31. package/dist/providers/dsh-sdk-session.d.ts +194 -0
  32. package/dist/providers/dsh-sdk-session.js +951 -0
  33. package/dist/providers/kimi-cli-session.d.ts +6 -1
  34. package/dist/providers/kimi-cli-session.js +13 -3
  35. package/dist/providers/kimi-print-session.d.ts +10 -3
  36. package/dist/providers/kimi-print-session.js +23 -53
  37. package/dist/providers/opencode-sdk-session.d.ts +6 -1
  38. package/dist/providers/opencode-sdk-session.js +11 -9
  39. package/dist/resume/dsh.d.ts +23 -0
  40. package/dist/resume/dsh.js +122 -0
  41. package/dist/resume/index.js +2 -0
  42. package/dist/session-factory.d.ts +3 -1
  43. package/dist/session-factory.js +5 -1
  44. package/dsh/cordis.yml +100 -0
  45. package/package.json +24 -3
@@ -6,6 +6,9 @@ import { randomUUID } from "node:crypto";
6
6
  import { EventEmitter } from "node:events";
7
7
  import readline from "node:readline";
8
8
  import { CODEX_EXEC_VARIANT as CODEX_EXEC_PROVIDER_VARIANT } from "../built-in-backends.js";
9
+ import { appendContextFilesToPrompt } from "../context-files.js";
10
+ import { PROVIDER_MEDIA_CAPABILITIES } from "../media-adapters.js";
11
+ import { assertMediaCapabilities, defaultPromptForMedia, resolveTurnMedia, } from "../media-input.js";
9
12
  import { emitLog, getBoundedEnvInt, loadEnvConfig, normalizeLogger, parseCommandParts, proxyToEnv, sanitizeForLog, } from "../shared.js";
10
13
  const DEFAULT_TURN_DEADLINE_MS = 12 * 60 * 1000;
11
14
  const MIN_TURN_DEADLINE_MS = 30 * 1000;
@@ -239,6 +242,7 @@ export class CodexExecSession extends EventEmitter {
239
242
  resumeReady: false,
240
243
  manualResume: null,
241
244
  currentTurnStatus: this.getCurrentTurnStatus(),
245
+ capabilities: { media: PROVIDER_MEDIA_CAPABILITIES[CODEX_EXEC_PROVIDER_VARIANT] },
242
246
  pid: this.currentTurn?.child?.pid || undefined,
243
247
  };
244
248
  }
@@ -338,7 +342,7 @@ export class CodexExecSession extends EventEmitter {
338
342
  buildPrompt(promptText) {
339
343
  return buildHistoryPrompt(this.history, promptText);
340
344
  }
341
- buildExecArgs({ useInitialImages = false, schemaFilePath = "", lastMessageFilePath = "" } = {}) {
345
+ buildExecArgs({ media = [], schemaFilePath = "", lastMessageFilePath = "" } = {}) {
342
346
  const args = [...this.baseArgs];
343
347
  args.push("exec");
344
348
  args.push("--json");
@@ -354,11 +358,8 @@ export class CodexExecSession extends EventEmitter {
354
358
  if (typeof this.options.model === "string" && this.options.model.trim()) {
355
359
  args.push("--model", this.options.model.trim());
356
360
  }
357
- const images = useInitialImages && Array.isArray(this.options.initialImages)
358
- ? this.options.initialImages.filter((item) => typeof item === "string" && item.trim())
359
- : [];
360
- for (const imagePath of images) {
361
- args.push("--image", imagePath);
361
+ for (const image of media.filter((item) => item.kind === "image")) {
362
+ args.push("--image", image.path);
362
363
  }
363
364
  return args;
364
365
  }
@@ -376,7 +377,7 @@ export class CodexExecSession extends EventEmitter {
376
377
  message: lastMessage || "Codex authentication required",
377
378
  });
378
379
  }
379
- async runTurn(promptText, { useInitialImages = false, onProgress = null, jsonSchema = null } = {}) {
380
+ async runTurn(promptText, { useInitialImages = false, media: mediaInput, contextFiles, onProgress = null, jsonSchema = null } = {}) {
380
381
  if (this.closeRequested || this.closed) {
381
382
  throw this.createSessionClosedError();
382
383
  }
@@ -385,12 +386,11 @@ export class CodexExecSession extends EventEmitter {
385
386
  reason: "turn_already_running",
386
387
  });
387
388
  }
389
+ const media = resolveTurnMedia(this.options, { useInitialImages, media: mediaInput });
390
+ assertMediaCapabilities(media, this.backend, PROVIDER_MEDIA_CAPABILITIES[CODEX_EXEC_PROVIDER_VARIANT]);
388
391
  const effectivePrompt = this.buildPrompt(promptText);
389
- const imagePaths = useInitialImages && Array.isArray(this.options.initialImages)
390
- ? this.options.initialImages.filter((item) => typeof item === "string" && item.trim())
391
- : [];
392
- const stdinPrompt = effectivePrompt || (imagePaths.length > 0 ? "Analyze the attached image." : "");
393
- if (!stdinPrompt && imagePaths.length === 0) {
392
+ const stdinPrompt = appendContextFilesToPrompt(effectivePrompt || (media.length > 0 ? defaultPromptForMedia(media) : ""), contextFiles).prompt;
393
+ if (!stdinPrompt && media.length === 0) {
394
394
  return buildEmptyTurnResult();
395
395
  }
396
396
  const normalizedSchema = normalizeJsonSchema(jsonSchema);
@@ -416,7 +416,7 @@ export class CodexExecSession extends EventEmitter {
416
416
  status_line: statusLineForPhase("turn_started"),
417
417
  }, onProgress);
418
418
  const args = this.buildExecArgs({
419
- useInitialImages,
419
+ media,
420
420
  schemaFilePath,
421
421
  lastMessageFilePath,
422
422
  });
@@ -83,6 +83,11 @@ export class CopilotSdkSession extends EventEmitter<[never]> {
83
83
  command: string;
84
84
  } | null;
85
85
  currentTurnStatus: any;
86
+ capabilities: {
87
+ media: Readonly<{
88
+ image: "native";
89
+ }>;
90
+ };
86
91
  };
87
92
  getSessionInfo(): {
88
93
  backend: string;
@@ -165,7 +170,7 @@ export class CopilotSdkSession extends EventEmitter<[never]> {
165
170
  handleCopilotEventFailure(error: any): void;
166
171
  handleCopilotEvent(event: any): Promise<void>;
167
172
  interruptCurrentTurn(): Promise<void>;
168
- runTurn(promptText: any, { useInitialImages, onProgress }?: {
173
+ runTurn(promptText: any, { useInitialImages, media: mediaInput, contextFiles, onProgress }?: {
169
174
  useInitialImages?: boolean | undefined;
170
175
  onProgress?: null | undefined;
171
176
  }): Promise<{
@@ -3,6 +3,9 @@ import { existsSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
4
  import path from "node:path";
5
5
  import { COPILOT_SDK_VARIANT as COPILOT_PROVIDER_VARIANT } from "../built-in-backends.js";
6
+ import { appendContextFilesToPrompt } from "../context-files.js";
7
+ import { PROVIDER_MEDIA_CAPABILITIES, buildCopilotAttachments } from "../media-adapters.js";
8
+ import { assertMediaCapabilities, defaultPromptForMedia, resolveTurnMedia, } from "../media-input.js";
6
9
  import { emitLog, getBoundedEnvInt, loadEnvConfig, normalizeLogger, parseCommandParts, proxyToEnv, sanitizeForLog, withoutCopilotGithubTokenEnv, } from "../shared.js";
7
10
  const DEFAULT_TURN_DEADLINE_MS = 12 * 60 * 1000;
8
11
  const MIN_TURN_DEADLINE_MS = 30 * 1000;
@@ -591,6 +594,7 @@ export class CopilotSdkSession extends EventEmitter {
591
594
  }
592
595
  : null,
593
596
  currentTurnStatus: this.getCurrentTurnStatus(),
597
+ capabilities: { media: PROVIDER_MEDIA_CAPABILITIES[COPILOT_PROVIDER_VARIANT] },
594
598
  };
595
599
  }
596
600
  getSessionInfo() {
@@ -1368,12 +1372,14 @@ export class CopilotSdkSession extends EventEmitter {
1368
1372
  }
1369
1373
  }
1370
1374
  }
1371
- async runTurn(promptText, { useInitialImages = false, onProgress = null } = {}) {
1375
+ async runTurn(promptText, { useInitialImages = false, media: mediaInput, contextFiles, onProgress = null } = {}) {
1372
1376
  if (this.currentTurn) {
1373
1377
  throw this.createTurnAlreadyRunningError();
1374
1378
  }
1375
- const prompt = this.buildPrompt(promptText, { useInitialImages });
1376
- if (!prompt) {
1379
+ const media = resolveTurnMedia(this.options, { useInitialImages, media: mediaInput });
1380
+ assertMediaCapabilities(media, this.backend, PROVIDER_MEDIA_CAPABILITIES[COPILOT_PROVIDER_VARIANT]);
1381
+ const prompt = appendContextFilesToPrompt(this.buildPrompt(promptText, { useInitialImages: false }) || (media.length ? defaultPromptForMedia(media) : ""), contextFiles).prompt;
1382
+ if (!prompt && media.length === 0) {
1377
1383
  return {
1378
1384
  text: "",
1379
1385
  usage: this.lastUsage ? { ...this.lastUsage } : null,
@@ -1419,6 +1425,7 @@ export class CopilotSdkSession extends EventEmitter {
1419
1425
  const completion = await Promise.race([
1420
1426
  this.requestOrThrow(this.session.sendAndWait({
1421
1427
  prompt,
1428
+ attachments: buildCopilotAttachments(media),
1422
1429
  mode: "immediate",
1423
1430
  }, this.turnDeadlineMs + SDK_SEND_AND_WAIT_TIMEOUT_GRACE_MS)),
1424
1431
  closeGuard.promise,
@@ -0,0 +1,194 @@
1
+ export function defaultDshSessionRoot(): string;
2
+ /**
3
+ * DeepSeek Harness (dsh) backend session.
4
+ *
5
+ * Drives a dsh runtime subprocess through `@deepseek-ai/dsh-sdk-client`
6
+ * (stdio JSON-RPC). The runtime composition is the package-owned
7
+ * `dsh/cordis.yml`; the pinned runtime bin comes from
8
+ * `@deepseek-ai/dsh-sdk-jsonrpc-demo`. One DshSdkSession owns at most one
9
+ * runtime subprocess across turns; the dsh-side session id is minted by us
10
+ * (or taken from resumeSessionId) so the same conversation resumes across
11
+ * runtime restarts through dsh's JSONL session persistence.
12
+ *
13
+ * Interrupt semantics: the dsh SDK wire has NO mid-turn cancel. Interrupting
14
+ * closes the runtime subprocess (SIGTERM ladder inside the SDK client) and
15
+ * the next runTurn spawns a fresh runtime resuming the same session id.
16
+ */
17
+ export class DshSdkSession extends EventEmitter<[never]> {
18
+ static capabilities: Readonly<{
19
+ goal: false;
20
+ }>;
21
+ constructor(backend: any, options?: {});
22
+ getCapabilities(): {
23
+ goal: false;
24
+ };
25
+ backend: string;
26
+ options: {};
27
+ logger: any;
28
+ cwd: any;
29
+ resumeSessionId: any;
30
+ pendingResumeFromSessionId: any;
31
+ sessionId: string;
32
+ sessionIdUsed: boolean;
33
+ model: any;
34
+ dshProvider: any;
35
+ maxTokens: number | undefined;
36
+ sessionRoot: any;
37
+ sessionInfo: {
38
+ backend: string;
39
+ sessionId: string;
40
+ model: any;
41
+ modelProvider: any;
42
+ };
43
+ history: any[];
44
+ pendingHistorySeed: boolean;
45
+ closeRequested: boolean;
46
+ closed: boolean;
47
+ closeWaiters: Set<any>;
48
+ sessionMessageHandler: any;
49
+ workingStatusHandler: any;
50
+ activeReplyTarget: string;
51
+ lastReplyTarget: string;
52
+ manualResumeReady: boolean;
53
+ sessionAnnounced: boolean;
54
+ currentTurn: {
55
+ emittedAssistantMessage: boolean;
56
+ fullText: string;
57
+ interrupted: boolean;
58
+ items: never[];
59
+ terminalWorkingStatusEmitted: boolean;
60
+ turnEndReason: null;
61
+ usage: null;
62
+ } | null;
63
+ harness: any;
64
+ lastUsage: any;
65
+ currentTurnStatus: any;
66
+ currentTurnActivityAt: number;
67
+ now: any;
68
+ turnDeadlineMs: any;
69
+ sdkModulePromise: Promise<any> | Promise<typeof import("@deepseek-ai/dsh-sdk-client")> | null;
70
+ env: any;
71
+ writeLog(message: any): void;
72
+ trace(message: any): void;
73
+ getSnapshot(): {
74
+ backend: string;
75
+ provider: string;
76
+ cwd: any;
77
+ sessionId: string;
78
+ sessionInfo: {
79
+ backend: string;
80
+ sessionId: string;
81
+ model: any;
82
+ modelProvider: any;
83
+ } | null;
84
+ useSessionFileReplyStream: boolean;
85
+ resumeReady: boolean;
86
+ manualResume: null;
87
+ currentTurnStatus: any;
88
+ capabilities: {
89
+ goal: false;
90
+ };
91
+ };
92
+ getSessionInfo(): {
93
+ backend: string;
94
+ sessionId: string;
95
+ model: any;
96
+ modelProvider: any;
97
+ } | null;
98
+ getCurrentTurnStatus(): any;
99
+ ensureSessionInfo(): Promise<{
100
+ backend: string;
101
+ sessionId: string;
102
+ model: any;
103
+ modelProvider: any;
104
+ } | null>;
105
+ getSessionUsageSummary(): Promise<{
106
+ sessionId: string;
107
+ sessionFilePath: undefined;
108
+ totalCostUsd: undefined;
109
+ usage: any;
110
+ modelUsage: null;
111
+ rateLimits: null;
112
+ manualResume: null;
113
+ }>;
114
+ usesSessionFileReplyStream(): boolean;
115
+ setSessionMessageHandler(handler: any): void;
116
+ setWorkingStatusHandler(handler: any): void;
117
+ setSessionReplyTarget(replyTo: any): void;
118
+ getCurrentReplyTarget(): string | undefined;
119
+ touchTurnActivity(): void;
120
+ updateCurrentTurnStatus(payload: any): void;
121
+ emitWorkingStatus(payload: any, onProgress?: null): Promise<void>;
122
+ emitAssistantMessage(text: any): Promise<void>;
123
+ emitTerminalWorkingStatus(currentTurn: any, payload: any, onProgress?: null): Promise<void>;
124
+ announceSession(): void;
125
+ /**
126
+ * Mint a fresh wire session id after the previous one's runtime was torn
127
+ * down (interrupt/timeout). The persisted log of the old id would collide
128
+ * on the next create, so the conversation continues on a new id seeded
129
+ * with the in-memory history; the new id is re-announced on success.
130
+ */
131
+ rotateSessionId(): void;
132
+ findPersistedSessionLog(sessionId: any): string | null;
133
+ /**
134
+ * Recover the user/assistant conversation from a persisted dsh JSONL log
135
+ * so a cross-process resume can seed it into the fresh wire session.
136
+ */
137
+ loadPersistedHistory(sessionId: any): {
138
+ role: string;
139
+ content: string;
140
+ }[];
141
+ createSessionClosedError(): Error;
142
+ createTurnTimeoutError(timeoutMs: any): Error;
143
+ createCloseGuard(onClose: any): {
144
+ promise: Promise<any>;
145
+ cleanup: () => void;
146
+ };
147
+ createTurnTimeoutGuard(onTimeout: any): {
148
+ promise: Promise<any>;
149
+ cleanup: () => void;
150
+ };
151
+ flushCloseWaiters(): void;
152
+ buildPrompt(promptText: any, { useInitialImages }?: {
153
+ useInitialImages?: boolean | undefined;
154
+ }): string;
155
+ resolveLaunchSpec(): {
156
+ command: any;
157
+ args: any;
158
+ };
159
+ buildRuntimeEnv(): any;
160
+ getSdkModule(): Promise<any>;
161
+ getHarness(): Promise<any>;
162
+ disposeHarness(): Promise<void>;
163
+ maybeEmitAuthRequired(message: any): void;
164
+ handleNotification(notification: any, currentTurn: any, { onProgress }: {
165
+ onProgress: any;
166
+ }): Promise<void>;
167
+ interruptCurrentTurn(): Promise<boolean>;
168
+ runTurn(promptText: any, { useInitialImages, onProgress }?: {
169
+ useInitialImages?: boolean | undefined;
170
+ onProgress?: null | undefined;
171
+ }): Promise<{
172
+ text: string;
173
+ usage: null;
174
+ items: never[];
175
+ events: never[];
176
+ provider?: undefined;
177
+ metadata?: undefined;
178
+ } | {
179
+ text: any;
180
+ usage: any;
181
+ items: never[];
182
+ events: never[];
183
+ provider: string;
184
+ metadata: {
185
+ source: string;
186
+ sessionId: string;
187
+ model: any;
188
+ modelProvider: any;
189
+ turnEndReason: undefined;
190
+ };
191
+ }>;
192
+ close(): Promise<void>;
193
+ }
194
+ import { EventEmitter } from "node:events";