@love-moon/ai-sdk 0.13.2 → 0.14.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.
- package/CHANGELOG.md +70 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.js +22 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/providers/chat-web-session.d.ts +18 -0
- package/dist/providers/chat-web-session.js +44 -1
- package/dist/providers/claude-agent-sdk-session.d.ts +34 -0
- package/dist/providers/claude-agent-sdk-session.js +33 -0
- package/dist/providers/codex-app-server-session.d.ts +76 -6
- package/dist/providers/codex-app-server-session.js +135 -21
- package/dist/providers/codex-exec-session.d.ts +14 -0
- package/dist/providers/codex-exec-session.js +20 -1
- package/dist/providers/copilot-sdk-session.d.ts +21 -0
- package/dist/providers/copilot-sdk-session.js +55 -12
- package/dist/providers/dsh-sdk-session.d.ts +53 -3
- package/dist/providers/dsh-sdk-session.js +24 -1
- package/dist/providers/kimi-cli-session.d.ts +22 -0
- package/dist/providers/kimi-cli-session.js +23 -1
- package/dist/providers/kimi-print-session.d.ts +13 -0
- package/dist/providers/kimi-print-session.js +29 -0
- package/dist/providers/kimi-slash-commands.d.ts +1 -0
- package/dist/providers/kimi-slash-commands.js +23 -0
- package/dist/providers/opencode-sdk-session.d.ts +52 -3
- package/dist/providers/opencode-sdk-session.js +33 -1
- package/dist/shared.d.ts +46 -0
- package/dist/shared.js +18 -1
- package/dist/transports/kimi-wire-transport.d.ts +12 -0
- package/dist/transports/kimi-wire-transport.js +40 -1
- package/dist/turn-usage.d.ts +31 -0
- package/dist/turn-usage.js +44 -0
- package/dist/worker.js +1 -1
- package/package.json +3 -3
|
@@ -62,6 +62,7 @@ export class KimiPrintSession extends EventEmitter<[never]> {
|
|
|
62
62
|
currentTurnStatus: any;
|
|
63
63
|
capabilities: {
|
|
64
64
|
compact: boolean;
|
|
65
|
+
clear: boolean;
|
|
65
66
|
media: Readonly<{
|
|
66
67
|
image: "unsupported";
|
|
67
68
|
}> | Readonly<{
|
|
@@ -129,6 +130,18 @@ export class KimiPrintSession extends EventEmitter<[never]> {
|
|
|
129
130
|
runCompact(request?: import("../shared.js").CompactRequest, { onProgress }?: {
|
|
130
131
|
onProgress?: Function;
|
|
131
132
|
}): Promise<import("../shared.js").CompactResult>;
|
|
133
|
+
/**
|
|
134
|
+
* Native `/clear`: kimi-cli's built-in slash command, answered locally without
|
|
135
|
+
* a model call. Print mode spawns a child per turn, so nothing is torn down;
|
|
136
|
+
* the `--session` id stays the same and only its context file rotates.
|
|
137
|
+
*
|
|
138
|
+
* @param {Record<string, unknown>} [request]
|
|
139
|
+
* @param {{ onProgress?: Function }} [options]
|
|
140
|
+
* @returns {Promise<import("../shared.js").ClearResult>}
|
|
141
|
+
*/
|
|
142
|
+
runClear(request?: Record<string, unknown>, { onProgress }?: {
|
|
143
|
+
onProgress?: Function;
|
|
144
|
+
}): Promise<import("../shared.js").ClearResult>;
|
|
132
145
|
runTurn(promptText: any, { useInitialImages, media: mediaInput, contextFiles, onProgress, jsonSchema, suppressReply }?: {
|
|
133
146
|
useInitialImages?: boolean | undefined;
|
|
134
147
|
onProgress?: null | undefined;
|
|
@@ -6,6 +6,7 @@ import { EventEmitter } from "node:events";
|
|
|
6
6
|
import readline from "node:readline";
|
|
7
7
|
import { KIMI_CLI_PRINT_VARIANT as KIMI_PRINT_PROVIDER_VARIANT } from "../built-in-backends.js";
|
|
8
8
|
import { appendContextFilesToPrompt } from "../context-files.js";
|
|
9
|
+
import { assertKimiClearReply } from "./kimi-slash-commands.js";
|
|
9
10
|
import { PROVIDER_MEDIA_CAPABILITIES, UNSUPPORTED_MEDIA_CAPABILITIES, buildKimiContent, } from "../media-adapters.js";
|
|
10
11
|
import { assertMediaCapabilities, defaultPromptForMedia, resolveTurnMedia, } from "../media-input.js";
|
|
11
12
|
import { emitLog, getBoundedEnvInt, loadEnvConfig, normalizeLogger, noteToolFinished, noteToolStarted, parseCommandParts, proxyToEnv, sanitizeForLog, withActiveTool, } from "../shared.js";
|
|
@@ -272,6 +273,7 @@ export class KimiPrintSession extends EventEmitter {
|
|
|
272
273
|
capabilities: {
|
|
273
274
|
// Kimi Code's `--prompt` CLI is unverified for slash commands.
|
|
274
275
|
compact: this.cliMode !== "prompt",
|
|
276
|
+
clear: this.cliMode !== "prompt",
|
|
275
277
|
media: this.cliMode === "prompt"
|
|
276
278
|
? UNSUPPORTED_MEDIA_CAPABILITIES
|
|
277
279
|
: PROVIDER_MEDIA_CAPABILITIES[KIMI_PRINT_PROVIDER_VARIANT],
|
|
@@ -505,6 +507,33 @@ export class KimiPrintSession extends EventEmitter {
|
|
|
505
507
|
metadata: turnResult.metadata,
|
|
506
508
|
};
|
|
507
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Native `/clear`: kimi-cli's built-in slash command, answered locally without
|
|
512
|
+
* a model call. Print mode spawns a child per turn, so nothing is torn down;
|
|
513
|
+
* the `--session` id stays the same and only its context file rotates.
|
|
514
|
+
*
|
|
515
|
+
* @param {Record<string, unknown>} [request]
|
|
516
|
+
* @param {{ onProgress?: Function }} [options]
|
|
517
|
+
* @returns {Promise<import("../shared.js").ClearResult>}
|
|
518
|
+
*/
|
|
519
|
+
async runClear(request = {}, { onProgress = null } = {}) {
|
|
520
|
+
if (this.cliMode === "prompt") {
|
|
521
|
+
throw createTurnError("Kimi prompt mode does not support /clear", { reason: "unsupported_clear" });
|
|
522
|
+
}
|
|
523
|
+
if (this.pendingHistorySeed) {
|
|
524
|
+
return { clear: { status: "noop" }, usage: null, metadata: {} };
|
|
525
|
+
}
|
|
526
|
+
const turnResult = await this.runTurn("/clear", { onProgress, suppressReply: true });
|
|
527
|
+
// Print mode exits 0 even when the model call fails, so only kimi-cli's
|
|
528
|
+
// fixed reply is trusted.
|
|
529
|
+
assertKimiClearReply(turnResult.text);
|
|
530
|
+
this.history = [];
|
|
531
|
+
return {
|
|
532
|
+
clear: { status: "cleared", sessionId: this.sessionId || undefined },
|
|
533
|
+
usage: turnResult.usage,
|
|
534
|
+
metadata: turnResult.metadata,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
508
537
|
async runTurn(promptText, { useInitialImages = false, media: mediaInput, contextFiles, onProgress = null, jsonSchema = null, suppressReply = false } = {}) {
|
|
509
538
|
if (this.closeRequested || this.closed) {
|
|
510
539
|
throw this.createSessionClosedError();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function assertKimiClearReply(reply: any): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kimi-cli answers `/clear` itself, without calling the model, with a fixed
|
|
3
|
+
* line (see kimi_cli/soul/slash.py). Both kimi modes verify that line before
|
|
4
|
+
* reporting success: if a kimi version ever stops treating `/clear` as a
|
|
5
|
+
* built-in, the text reaches the model as an ordinary turn — which grows the
|
|
6
|
+
* context instead of clearing it — and we must fail loudly rather than tell the
|
|
7
|
+
* user their context is gone.
|
|
8
|
+
*
|
|
9
|
+
* Kept in one place so the two modes cannot drift apart, in either direction.
|
|
10
|
+
*/
|
|
11
|
+
const KIMI_CLEAR_ACK = /context has been cleared/i;
|
|
12
|
+
function createClearFailedError(message) {
|
|
13
|
+
const error = new Error(message);
|
|
14
|
+
error.reason = "clear_failed";
|
|
15
|
+
return error;
|
|
16
|
+
}
|
|
17
|
+
export function assertKimiClearReply(reply) {
|
|
18
|
+
const text = String(reply || "");
|
|
19
|
+
if (KIMI_CLEAR_ACK.test(text)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
throw createClearFailedError(`Kimi clear failed${text.trim() ? `: ${text.trim()}` : ""}`);
|
|
23
|
+
}
|
|
@@ -80,6 +80,7 @@ export class OpencodeSdkSession extends EventEmitter<[never]> {
|
|
|
80
80
|
currentTurnStatus: any;
|
|
81
81
|
capabilities: {
|
|
82
82
|
compact: boolean;
|
|
83
|
+
clear: boolean;
|
|
83
84
|
media: Readonly<{
|
|
84
85
|
image: "native";
|
|
85
86
|
}>;
|
|
@@ -208,9 +209,57 @@ export class OpencodeSdkSession extends EventEmitter<[never]> {
|
|
|
208
209
|
* @param {{ onProgress?: Function }} [options]
|
|
209
210
|
* @returns {Promise<import("../shared.js").CompactResult>}
|
|
210
211
|
*/
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Native clear: create a new session on the SAME opencode server (the server
|
|
214
|
+
* process and its event stream stay up); the old session is left untouched on
|
|
215
|
+
* disk, exactly like the boot path's `session.create`.
|
|
216
|
+
*/
|
|
217
|
+
runClear(): Promise<{
|
|
218
|
+
clear: {
|
|
219
|
+
status: string;
|
|
220
|
+
sessionId?: undefined;
|
|
221
|
+
};
|
|
222
|
+
usage: null;
|
|
223
|
+
metadata: {};
|
|
224
|
+
} | {
|
|
225
|
+
clear: {
|
|
226
|
+
status: string;
|
|
227
|
+
sessionId: any;
|
|
228
|
+
};
|
|
229
|
+
usage: null;
|
|
230
|
+
metadata: {};
|
|
231
|
+
}>;
|
|
232
|
+
runCompact(request?: {}, { onProgress }?: {
|
|
233
|
+
onProgress?: null | undefined;
|
|
234
|
+
}): Promise<{
|
|
235
|
+
compact: {
|
|
236
|
+
status: string;
|
|
237
|
+
instructionsApplied: boolean;
|
|
238
|
+
};
|
|
239
|
+
usage: null;
|
|
240
|
+
metadata: {};
|
|
241
|
+
} | {
|
|
242
|
+
compact: {
|
|
243
|
+
status: string;
|
|
244
|
+
instructionsApplied: boolean;
|
|
245
|
+
};
|
|
246
|
+
usage: {
|
|
247
|
+
total: number | undefined;
|
|
248
|
+
input: number | undefined;
|
|
249
|
+
output: number | undefined;
|
|
250
|
+
reasoning: number | undefined;
|
|
251
|
+
cache: {
|
|
252
|
+
read: number | undefined;
|
|
253
|
+
write: number | undefined;
|
|
254
|
+
};
|
|
255
|
+
} | null;
|
|
256
|
+
metadata: {
|
|
257
|
+
providerID: any;
|
|
258
|
+
modelID: any;
|
|
259
|
+
source: string;
|
|
260
|
+
sessionId: any;
|
|
261
|
+
};
|
|
262
|
+
}>;
|
|
214
263
|
runTurn(promptText: any, { useInitialImages, media: mediaInput, contextFiles, onProgress, jsonSchema }?: {
|
|
215
264
|
useInitialImages?: boolean | undefined;
|
|
216
265
|
onProgress?: null | undefined;
|
|
@@ -231,7 +231,7 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
231
231
|
resumeReady: Boolean(this.sessionId),
|
|
232
232
|
manualResume: null,
|
|
233
233
|
currentTurnStatus: this.getCurrentTurnStatus(),
|
|
234
|
-
capabilities: { compact: true, media: PROVIDER_MEDIA_CAPABILITIES[OPENCODE_PROVIDER_VARIANT] },
|
|
234
|
+
capabilities: { compact: true, clear: true, media: PROVIDER_MEDIA_CAPABILITIES[OPENCODE_PROVIDER_VARIANT] },
|
|
235
235
|
pid: this.transport.pid || undefined,
|
|
236
236
|
};
|
|
237
237
|
}
|
|
@@ -1227,6 +1227,38 @@ export class OpencodeSdkSession extends EventEmitter {
|
|
|
1227
1227
|
* @param {{ onProgress?: Function }} [options]
|
|
1228
1228
|
* @returns {Promise<import("../shared.js").CompactResult>}
|
|
1229
1229
|
*/
|
|
1230
|
+
/**
|
|
1231
|
+
* Native clear: create a new session on the SAME opencode server (the server
|
|
1232
|
+
* process and its event stream stay up); the old session is left untouched on
|
|
1233
|
+
* disk, exactly like the boot path's `session.create`.
|
|
1234
|
+
*/
|
|
1235
|
+
async runClear() {
|
|
1236
|
+
if (this.closeRequested) {
|
|
1237
|
+
throw this.createSessionClosedError();
|
|
1238
|
+
}
|
|
1239
|
+
if (this.currentTurn) {
|
|
1240
|
+
throw createTurnError("Opencode turn already running", {
|
|
1241
|
+
reason: "turn_already_running",
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
if (this.pendingHistorySeed || (!this.resumeSessionId && !this.sessionId && this.history.length === 0)) {
|
|
1245
|
+
return { clear: { status: "noop" }, usage: null, metadata: {} };
|
|
1246
|
+
}
|
|
1247
|
+
await this.boot();
|
|
1248
|
+
if (typeof this.client?.session?.create !== "function") {
|
|
1249
|
+
throw new Error("Opencode session create API is unavailable");
|
|
1250
|
+
}
|
|
1251
|
+
const session = await this.requestOrThrow(this.client.session.create({}, { throwOnError: true, responseStyle: "data" }));
|
|
1252
|
+
this.applySessionInfo(session);
|
|
1253
|
+
// A later re-boot must not reattach to the session we walked away from.
|
|
1254
|
+
this.resumeSessionId = "";
|
|
1255
|
+
this.history = [];
|
|
1256
|
+
return {
|
|
1257
|
+
clear: { status: "cleared", sessionId: this.sessionId || undefined },
|
|
1258
|
+
usage: null,
|
|
1259
|
+
metadata: {},
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1230
1262
|
async runCompact(request = {}, { onProgress = null } = {}) {
|
|
1231
1263
|
if (this.closeRequested) {
|
|
1232
1264
|
throw this.createSessionClosedError();
|
package/dist/shared.d.ts
CHANGED
|
@@ -100,6 +100,7 @@ export const TERMINAL_GOAL_STATUSES: ReadonlyArray<GoalStatus>;
|
|
|
100
100
|
* @typedef {Object} SessionCapabilities
|
|
101
101
|
* @property {boolean} [goal]
|
|
102
102
|
* @property {boolean} [compact]
|
|
103
|
+
* @property {boolean} [clear]
|
|
103
104
|
*/
|
|
104
105
|
/**
|
|
105
106
|
* Manual context compaction (`/compact`) contract for providers that
|
|
@@ -124,6 +125,22 @@ export const TERMINAL_GOAL_STATUSES: ReadonlyArray<GoalStatus>;
|
|
|
124
125
|
* @property {unknown} [usage]
|
|
125
126
|
* @property {Record<string, unknown>} [metadata]
|
|
126
127
|
*/
|
|
128
|
+
/**
|
|
129
|
+
* Native context clear (`/clear`) contract for providers that implement the
|
|
130
|
+
* optional `runClear` method and advertise `capabilities.clear === true`.
|
|
131
|
+
* Providers must NOT emit assistant messages while clearing; the caller
|
|
132
|
+
* renders the user-facing confirmation.
|
|
133
|
+
*
|
|
134
|
+
* @typedef {Object} ClearState
|
|
135
|
+
* @property {"cleared"|"noop"} status `noop` = there was nothing to clear.
|
|
136
|
+
* @property {string} [sessionId] New session id when the backend starts a new
|
|
137
|
+
* conversation; callers rebind the task to it.
|
|
138
|
+
*
|
|
139
|
+
* @typedef {Object} ClearResult
|
|
140
|
+
* @property {ClearState} clear
|
|
141
|
+
* @property {unknown} [usage]
|
|
142
|
+
* @property {Record<string, unknown>} [metadata]
|
|
143
|
+
*/
|
|
127
144
|
/**
|
|
128
145
|
* Default capabilities assigned to providers that do not opt into any optional
|
|
129
146
|
* feature. Treat the returned object as read-only; clone before mutating.
|
|
@@ -212,6 +229,7 @@ export type GoalResult = {
|
|
|
212
229
|
export type SessionCapabilities = {
|
|
213
230
|
goal?: boolean | undefined;
|
|
214
231
|
compact?: boolean | undefined;
|
|
232
|
+
clear?: boolean | undefined;
|
|
215
233
|
};
|
|
216
234
|
/**
|
|
217
235
|
* Manual context compaction (`/compact`) contract for providers that
|
|
@@ -254,3 +272,31 @@ export type CompactResult = {
|
|
|
254
272
|
usage?: unknown;
|
|
255
273
|
metadata?: Record<string, unknown> | undefined;
|
|
256
274
|
};
|
|
275
|
+
/**
|
|
276
|
+
* Native context clear (`/clear`) contract for providers that implement the
|
|
277
|
+
* optional `runClear` method and advertise `capabilities.clear === true`.
|
|
278
|
+
* Providers must NOT emit assistant messages while clearing; the caller
|
|
279
|
+
* renders the user-facing confirmation.
|
|
280
|
+
*/
|
|
281
|
+
export type ClearState = {
|
|
282
|
+
/**
|
|
283
|
+
* `noop` = there was nothing to clear.
|
|
284
|
+
*/
|
|
285
|
+
status: "cleared" | "noop";
|
|
286
|
+
/**
|
|
287
|
+
* New session id when the backend starts a new
|
|
288
|
+
* conversation; callers rebind the task to it.
|
|
289
|
+
*/
|
|
290
|
+
sessionId?: string | undefined;
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* Native context clear (`/clear`) contract for providers that implement the
|
|
294
|
+
* optional `runClear` method and advertise `capabilities.clear === true`.
|
|
295
|
+
* Providers must NOT emit assistant messages while clearing; the caller
|
|
296
|
+
* renders the user-facing confirmation.
|
|
297
|
+
*/
|
|
298
|
+
export type ClearResult = {
|
|
299
|
+
clear: ClearState;
|
|
300
|
+
usage?: unknown;
|
|
301
|
+
metadata?: Record<string, unknown> | undefined;
|
|
302
|
+
};
|
package/dist/shared.js
CHANGED
|
@@ -81,6 +81,7 @@ export function isTerminalGoalStatus(value) {
|
|
|
81
81
|
* @typedef {Object} SessionCapabilities
|
|
82
82
|
* @property {boolean} [goal]
|
|
83
83
|
* @property {boolean} [compact]
|
|
84
|
+
* @property {boolean} [clear]
|
|
84
85
|
*/
|
|
85
86
|
/**
|
|
86
87
|
* Manual context compaction (`/compact`) contract for providers that
|
|
@@ -105,13 +106,29 @@ export function isTerminalGoalStatus(value) {
|
|
|
105
106
|
* @property {unknown} [usage]
|
|
106
107
|
* @property {Record<string, unknown>} [metadata]
|
|
107
108
|
*/
|
|
109
|
+
/**
|
|
110
|
+
* Native context clear (`/clear`) contract for providers that implement the
|
|
111
|
+
* optional `runClear` method and advertise `capabilities.clear === true`.
|
|
112
|
+
* Providers must NOT emit assistant messages while clearing; the caller
|
|
113
|
+
* renders the user-facing confirmation.
|
|
114
|
+
*
|
|
115
|
+
* @typedef {Object} ClearState
|
|
116
|
+
* @property {"cleared"|"noop"} status `noop` = there was nothing to clear.
|
|
117
|
+
* @property {string} [sessionId] New session id when the backend starts a new
|
|
118
|
+
* conversation; callers rebind the task to it.
|
|
119
|
+
*
|
|
120
|
+
* @typedef {Object} ClearResult
|
|
121
|
+
* @property {ClearState} clear
|
|
122
|
+
* @property {unknown} [usage]
|
|
123
|
+
* @property {Record<string, unknown>} [metadata]
|
|
124
|
+
*/
|
|
108
125
|
/**
|
|
109
126
|
* Default capabilities assigned to providers that do not opt into any optional
|
|
110
127
|
* feature. Treat the returned object as read-only; clone before mutating.
|
|
111
128
|
*
|
|
112
129
|
* @type {Readonly<SessionCapabilities>}
|
|
113
130
|
*/
|
|
114
|
-
export const DEFAULT_SESSION_CAPABILITIES = Object.freeze({ goal: false, compact: false });
|
|
131
|
+
export const DEFAULT_SESSION_CAPABILITIES = Object.freeze({ goal: false, compact: false, clear: false });
|
|
115
132
|
/**
|
|
116
133
|
* Resolve a session's capability snapshot, falling back to
|
|
117
134
|
* {@link DEFAULT_SESSION_CAPABILITIES}.
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop resume flags from the configured command line. The session id is owned
|
|
3
|
+
* by the caller (we always append our own `--session=`), so a configured
|
|
4
|
+
* `--continue`/`--session`/`--resume` would silently reattach a fresh session —
|
|
5
|
+
* e.g. a `/clear` — to the previous conversation.
|
|
6
|
+
*
|
|
7
|
+
* Only those flags are touched. Notably `-c` is kimi's alias for
|
|
8
|
+
* `--prompt`/`--command` (it takes a value), not a continue switch, so it is
|
|
9
|
+
* passed through untouched — dropping the flag alone would leave its value
|
|
10
|
+
* behind as a stray positional argument.
|
|
11
|
+
*/
|
|
12
|
+
export function filterKimiWireBaseArgs(args: any): string[];
|
|
1
13
|
export class KimiWireTransport extends EventEmitter<[never]> {
|
|
2
14
|
constructor(options?: {});
|
|
3
15
|
options: {};
|
|
@@ -37,6 +37,45 @@ function quoteShellArg(value) {
|
|
|
37
37
|
}
|
|
38
38
|
return `'${normalized.replace(/'/g, `'\\''`)}'`;
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Drop resume flags from the configured command line. The session id is owned
|
|
42
|
+
* by the caller (we always append our own `--session=`), so a configured
|
|
43
|
+
* `--continue`/`--session`/`--resume` would silently reattach a fresh session —
|
|
44
|
+
* e.g. a `/clear` — to the previous conversation.
|
|
45
|
+
*
|
|
46
|
+
* Only those flags are touched. Notably `-c` is kimi's alias for
|
|
47
|
+
* `--prompt`/`--command` (it takes a value), not a continue switch, so it is
|
|
48
|
+
* passed through untouched — dropping the flag alone would leave its value
|
|
49
|
+
* behind as a stray positional argument.
|
|
50
|
+
*/
|
|
51
|
+
export function filterKimiWireBaseArgs(args) {
|
|
52
|
+
const filtered = [];
|
|
53
|
+
let skipNext = false;
|
|
54
|
+
for (const rawArg of Array.isArray(args) ? args : []) {
|
|
55
|
+
const arg = String(rawArg || "");
|
|
56
|
+
if (!arg) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (skipNext) {
|
|
60
|
+
skipNext = false;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (arg === "--continue" || arg === "-C") {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (arg === "--session" || arg === "-S" || arg === "--resume" || arg === "-r") {
|
|
67
|
+
skipNext = true;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (arg.startsWith("--session=") ||
|
|
71
|
+
arg.startsWith("--resume=") ||
|
|
72
|
+
["-S", "-r"].some((prefix) => arg.startsWith(prefix) && arg.length > prefix.length)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
filtered.push(arg);
|
|
76
|
+
}
|
|
77
|
+
return filtered;
|
|
78
|
+
}
|
|
40
79
|
export class KimiWireTransport extends EventEmitter {
|
|
41
80
|
constructor(options = {}) {
|
|
42
81
|
super();
|
|
@@ -55,7 +94,7 @@ export class KimiWireTransport extends EventEmitter {
|
|
|
55
94
|
throw new Error("Invalid kimi command");
|
|
56
95
|
}
|
|
57
96
|
this.command = command;
|
|
58
|
-
this.baseArgs = args;
|
|
97
|
+
this.baseArgs = filterKimiWireBaseArgs(args);
|
|
59
98
|
this.env = options.env && typeof options.env === "object" ? { ...options.env } : {};
|
|
60
99
|
this.sessionId =
|
|
61
100
|
typeof options.sessionId === "string" && options.sessionId.trim()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} TurnUsageSummary
|
|
3
|
+
* @property {number} tokens Everything the turn read and wrote.
|
|
4
|
+
* @property {number} [inputTokens] All input the model read, cached or not.
|
|
5
|
+
* @property {number} [cachedInputTokens] The part of `inputTokens` served from the prompt cache.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes one turn's provider usage; the input cache hit ratio is
|
|
9
|
+
* `cachedInputTokens / inputTokens`. Claude's `input_tokens` excludes cache
|
|
10
|
+
* reads and writes, so its input is all three; the Codex provider reports
|
|
11
|
+
* per-turn deltas whose `turnInputTokens` already include the cached ones.
|
|
12
|
+
* Unknown shapes → null.
|
|
13
|
+
*
|
|
14
|
+
* @param {unknown} usage
|
|
15
|
+
* @returns {TurnUsageSummary | null}
|
|
16
|
+
*/
|
|
17
|
+
export function summarizeTurnUsage(usage: unknown): TurnUsageSummary | null;
|
|
18
|
+
export type TurnUsageSummary = {
|
|
19
|
+
/**
|
|
20
|
+
* Everything the turn read and wrote.
|
|
21
|
+
*/
|
|
22
|
+
tokens: number;
|
|
23
|
+
/**
|
|
24
|
+
* All input the model read, cached or not.
|
|
25
|
+
*/
|
|
26
|
+
inputTokens?: number | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The part of `inputTokens` served from the prompt cache.
|
|
29
|
+
*/
|
|
30
|
+
cachedInputTokens?: number | undefined;
|
|
31
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} TurnUsageSummary
|
|
3
|
+
* @property {number} tokens Everything the turn read and wrote.
|
|
4
|
+
* @property {number} [inputTokens] All input the model read, cached or not.
|
|
5
|
+
* @property {number} [cachedInputTokens] The part of `inputTokens` served from the prompt cache.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes one turn's provider usage; the input cache hit ratio is
|
|
9
|
+
* `cachedInputTokens / inputTokens`. Claude's `input_tokens` excludes cache
|
|
10
|
+
* reads and writes, so its input is all three; the Codex provider reports
|
|
11
|
+
* per-turn deltas whose `turnInputTokens` already include the cached ones.
|
|
12
|
+
* Unknown shapes → null.
|
|
13
|
+
*
|
|
14
|
+
* @param {unknown} usage
|
|
15
|
+
* @returns {TurnUsageSummary | null}
|
|
16
|
+
*/
|
|
17
|
+
export function summarizeTurnUsage(usage) {
|
|
18
|
+
if (!usage || typeof usage !== "object") {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const read = (key) => {
|
|
22
|
+
const value = Number(/** @type {Record<string, unknown>} */ (usage)[key]);
|
|
23
|
+
return Number.isFinite(value) ? value : null;
|
|
24
|
+
};
|
|
25
|
+
const turnTotal = read("turnTotalTokens");
|
|
26
|
+
if (turnTotal !== null) {
|
|
27
|
+
const inputTokens = read("turnInputTokens");
|
|
28
|
+
const cachedInputTokens = read("turnCachedInputTokens");
|
|
29
|
+
return inputTokens !== null && cachedInputTokens !== null
|
|
30
|
+
? { tokens: turnTotal, inputTokens, cachedInputTokens }
|
|
31
|
+
: { tokens: turnTotal };
|
|
32
|
+
}
|
|
33
|
+
const [input, cacheWrite, cacheRead, output] = [
|
|
34
|
+
"input_tokens",
|
|
35
|
+
"cache_creation_input_tokens",
|
|
36
|
+
"cache_read_input_tokens",
|
|
37
|
+
"output_tokens",
|
|
38
|
+
].map(read);
|
|
39
|
+
if ([input, cacheWrite, cacheRead, output].every((value) => value === null)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const inputTokens = (input ?? 0) + (cacheWrite ?? 0) + (cacheRead ?? 0);
|
|
43
|
+
return { tokens: inputTokens + (output ?? 0), inputTokens, cachedInputTokens: cacheRead ?? 0 };
|
|
44
|
+
}
|
package/dist/worker.js
CHANGED
|
@@ -76,7 +76,7 @@ async function handleRequest(message) {
|
|
|
76
76
|
throw new Error(`Unsupported worker method: ${method}`);
|
|
77
77
|
}
|
|
78
78
|
const args = Array.isArray(message.args) ? [...message.args] : [];
|
|
79
|
-
if (method === "runTurn" || method === "runGoal" || method === "runCompact") {
|
|
79
|
+
if (method === "runTurn" || method === "runGoal" || method === "runCompact" || method === "runClear") {
|
|
80
80
|
const firstArg = args[0];
|
|
81
81
|
const options = args[1] && typeof args[1] === "object" ? { ...args[1] } : {};
|
|
82
82
|
options.onProgress = (payload) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/ai-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/lovemoon-ai/conductor.git"
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
"zod": "^4.1.5"
|
|
54
54
|
},
|
|
55
55
|
"optionalDependencies": {
|
|
56
|
-
"@love-moon/chat-web": "^0.
|
|
56
|
+
"@love-moon/chat-web": "^0.14.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/node": "^22.10.2",
|
|
60
60
|
"tsx": "^4.20.6",
|
|
61
61
|
"typescript": "^5.6.3"
|
|
62
62
|
},
|
|
63
|
-
"gitCommitId": "
|
|
63
|
+
"gitCommitId": "6def94c"
|
|
64
64
|
}
|