kimiflare 0.34.0 → 0.35.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/dist/index.js CHANGED
@@ -83,7 +83,7 @@ async function loadConfig() {
83
83
  const envCacheStable = process.env.KIMIFLARE_CACHE_STABLE_PROMPTS;
84
84
  const cacheStablePrompts = envCacheStable === "0" || envCacheStable === "false" ? false : true;
85
85
  const envCompiled = process.env.KIMIFLARE_COMPILED_CONTEXT;
86
- const compiledContext = envCompiled === "1" || envCompiled === "true" ? true : false;
86
+ const compiledContext = envCompiled === "0" || envCompiled === "false" ? false : true;
87
87
  const envImageTurns = process.env.KIMIFLARE_IMAGE_HISTORY_TURNS;
88
88
  const imageHistoryTurns = envImageTurns ? parseInt(envImageTurns, 10) : void 0;
89
89
  const envMemoryEnabled = readBooleanEnv("KIMIFLARE_MEMORY_ENABLED");
@@ -92,9 +92,35 @@ async function loadConfig() {
92
92
  const envMemoryMaxEntries = readNumberEnv("KIMIFLARE_MEMORY_MAX_ENTRIES");
93
93
  const envMemoryEmbeddingModel = process.env.KIMIFLARE_MEMORY_EMBEDDING_MODEL;
94
94
  const envPlumbingModel = process.env.KIMIFLARE_PLUMBING_MODEL;
95
+ const envMemoryExtractionModel = process.env.KIMIFLARE_MEMORY_EXTRACTION_MODEL;
95
96
  const envCodeMode = readBooleanEnv("KIMIFLARE_CODE_MODE");
96
97
  const envCostAttribution = readBooleanEnv("KIMI_COST_ATTRIBUTION");
97
98
  const envFilePicker = readBooleanEnv("KIMIFLARE_FILE_PICKER");
99
+ const envCloudMode = readBooleanEnv("KIMIFLARE_CLOUD");
100
+ if (envCloudMode) {
101
+ return {
102
+ accountId: "",
103
+ apiToken: "",
104
+ model: envModel,
105
+ cloudMode: true,
106
+ reasoningEffort: envEffort,
107
+ coauthor: envCoauthor?.enabled ?? true,
108
+ coauthorName: envCoauthor?.name,
109
+ coauthorEmail: envCoauthor?.email,
110
+ cacheStablePrompts,
111
+ compiledContext,
112
+ imageHistoryTurns: Number.isNaN(imageHistoryTurns) ? void 0 : imageHistoryTurns,
113
+ memoryEnabled: envMemoryEnabled,
114
+ memoryDbPath: envMemoryDbPath,
115
+ memoryMaxAgeDays: envMemoryMaxAgeDays,
116
+ memoryMaxEntries: envMemoryMaxEntries,
117
+ memoryEmbeddingModel: envMemoryEmbeddingModel,
118
+ plumbingModel: envPlumbingModel,
119
+ codeMode: envCodeMode,
120
+ costAttribution: envCostAttribution ?? false,
121
+ filePicker: envFilePicker ?? true
122
+ };
123
+ }
98
124
  if (envAccount && envToken) {
99
125
  return {
100
126
  accountId: envAccount,
@@ -112,20 +138,46 @@ async function loadConfig() {
112
138
  cacheStablePrompts,
113
139
  compiledContext,
114
140
  imageHistoryTurns: Number.isNaN(imageHistoryTurns) ? void 0 : imageHistoryTurns,
115
- memoryEnabled: envMemoryEnabled,
141
+ memoryEnabled: envMemoryEnabled ?? true,
116
142
  memoryDbPath: envMemoryDbPath,
117
143
  memoryMaxAgeDays: envMemoryMaxAgeDays,
118
144
  memoryMaxEntries: envMemoryMaxEntries,
119
145
  memoryEmbeddingModel: envMemoryEmbeddingModel,
120
146
  plumbingModel: envPlumbingModel,
121
- codeMode: envCodeMode,
122
- costAttribution: envCostAttribution ?? false,
147
+ memoryExtractionModel: envMemoryExtractionModel,
148
+ codeMode: envCodeMode ?? true,
149
+ costAttribution: envCostAttribution ?? true,
123
150
  filePicker: envFilePicker ?? true
124
151
  };
125
152
  }
126
153
  try {
127
154
  const raw = await readFile(configPath(), "utf8");
128
155
  const parsed = JSON.parse(raw);
156
+ if (parsed.cloudMode) {
157
+ return {
158
+ accountId: envAccount ?? parsed.accountId ?? "",
159
+ apiToken: envToken ?? parsed.apiToken ?? "",
160
+ model: envModel ?? parsed.model ?? DEFAULT_MODEL,
161
+ cloudMode: true,
162
+ reasoningEffort: envEffort ?? parsed.reasoningEffort,
163
+ coauthor: envCoauthor?.enabled ?? parsed.coauthor ?? true,
164
+ coauthorName: envCoauthor?.name ?? parsed.coauthorName,
165
+ coauthorEmail: envCoauthor?.email ?? parsed.coauthorEmail,
166
+ mcpServers: parsed.mcpServers,
167
+ cacheStablePrompts: parsed.cacheStablePrompts ?? cacheStablePrompts,
168
+ compiledContext: parsed.compiledContext ?? compiledContext,
169
+ imageHistoryTurns: Number.isNaN(imageHistoryTurns) ? parsed.imageHistoryTurns : imageHistoryTurns,
170
+ memoryEnabled: envMemoryEnabled ?? parsed.memoryEnabled,
171
+ memoryDbPath: envMemoryDbPath ?? parsed.memoryDbPath,
172
+ memoryMaxAgeDays: envMemoryMaxAgeDays ?? parsed.memoryMaxAgeDays,
173
+ memoryMaxEntries: envMemoryMaxEntries ?? parsed.memoryMaxEntries,
174
+ memoryEmbeddingModel: envMemoryEmbeddingModel ?? parsed.memoryEmbeddingModel,
175
+ plumbingModel: envPlumbingModel ?? parsed.plumbingModel,
176
+ codeMode: envCodeMode ?? parsed.codeMode,
177
+ costAttribution: envCostAttribution ?? parsed.costAttribution ?? false,
178
+ filePicker: envFilePicker ?? parsed.filePicker ?? true
179
+ };
180
+ }
129
181
  if (parsed.accountId && parsed.apiToken) {
130
182
  return {
131
183
  accountId: envAccount ?? parsed.accountId,
@@ -144,15 +196,17 @@ async function loadConfig() {
144
196
  cacheStablePrompts: parsed.cacheStablePrompts ?? cacheStablePrompts,
145
197
  compiledContext: parsed.compiledContext ?? compiledContext,
146
198
  imageHistoryTurns: Number.isNaN(imageHistoryTurns) ? parsed.imageHistoryTurns : imageHistoryTurns,
147
- memoryEnabled: envMemoryEnabled ?? parsed.memoryEnabled,
199
+ memoryEnabled: envMemoryEnabled ?? parsed.memoryEnabled ?? true,
148
200
  memoryDbPath: envMemoryDbPath ?? parsed.memoryDbPath,
149
201
  memoryMaxAgeDays: envMemoryMaxAgeDays ?? parsed.memoryMaxAgeDays,
150
202
  memoryMaxEntries: envMemoryMaxEntries ?? parsed.memoryMaxEntries,
151
203
  memoryEmbeddingModel: envMemoryEmbeddingModel ?? parsed.memoryEmbeddingModel,
152
204
  plumbingModel: envPlumbingModel ?? parsed.plumbingModel,
153
- codeMode: envCodeMode ?? parsed.codeMode,
154
- costAttribution: envCostAttribution ?? parsed.costAttribution ?? false,
155
- filePicker: envFilePicker ?? parsed.filePicker ?? true
205
+ memoryExtractionModel: envMemoryExtractionModel ?? parsed.memoryExtractionModel,
206
+ codeMode: envCodeMode ?? parsed.codeMode ?? true,
207
+ costAttribution: envCostAttribution ?? parsed.costAttribution ?? true,
208
+ filePicker: envFilePicker ?? parsed.filePicker ?? true,
209
+ cloudMode: envCloudMode ?? parsed.cloudMode
156
210
  };
157
211
  }
158
212
  } catch {
@@ -440,7 +494,7 @@ async function* runKimi(opts2) {
440
494
  let res;
441
495
  try {
442
496
  const headers = {
443
- Authorization: `Bearer ${opts2.apiToken}`,
497
+ Authorization: `Bearer ${opts2.cloudMode && opts2.cloudToken ? opts2.cloudToken : opts2.apiToken}`,
444
498
  "Content-Type": "application/json",
445
499
  "User-Agent": getUserAgent(),
446
500
  ...gatewayHeaders
@@ -497,6 +551,12 @@ function validateModelId(model) {
497
551
  }
498
552
  function buildKimiRequestTarget(opts2) {
499
553
  validateModelId(opts2.model);
554
+ if (opts2.cloudMode) {
555
+ return {
556
+ url: "https://api.kimiflare.com/v1/chat",
557
+ headers: opts2.cloudToken ? { Authorization: `Bearer ${opts2.cloudToken}` } : {}
558
+ };
559
+ }
500
560
  if (!opts2.gateway?.id) {
501
561
  return {
502
562
  url: `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(opts2.accountId)}/ai/run/${opts2.model}`,
@@ -553,44 +613,51 @@ async function* parseStream(body, signal) {
553
613
  lastUsage = chunk.usage;
554
614
  yield { type: "usage", usage: chunk.usage };
555
615
  }
616
+ if (typeof chunk.response === "string") {
617
+ const resp = chunk.response;
618
+ if (resp.length) {
619
+ yield { type: "text", delta: resp };
620
+ }
621
+ }
556
622
  const choice = chunk.choices?.[0];
557
- if (!choice) continue;
558
- const d = choice.delta;
559
- if (d) {
560
- if (typeof d.reasoning_content === "string" && d.reasoning_content.length) {
561
- yield { type: "reasoning", delta: d.reasoning_content };
562
- }
563
- if (typeof d.content === "string" && d.content.length) {
564
- yield { type: "text", delta: d.content };
565
- }
566
- if (Array.isArray(d.tool_calls)) {
567
- for (const tc of d.tool_calls) {
568
- const idx = typeof tc.index === "number" ? tc.index : 0;
569
- let buf = toolCalls.get(idx);
570
- const incomingName = tc.function?.name ?? null;
571
- const incomingId = tc.id ?? null;
572
- if (!buf) {
573
- buf = { id: incomingId ?? `tc_${idx}`, name: incomingName ?? "", args: "" };
574
- toolCalls.set(idx, buf);
575
- if (buf.name) {
576
- yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
623
+ if (choice) {
624
+ const d = choice.delta;
625
+ if (d) {
626
+ if (typeof d.reasoning_content === "string" && d.reasoning_content.length) {
627
+ yield { type: "reasoning", delta: d.reasoning_content };
628
+ }
629
+ if (typeof d.content === "string" && d.content.length) {
630
+ yield { type: "text", delta: d.content };
631
+ }
632
+ if (Array.isArray(d.tool_calls)) {
633
+ for (const tc of d.tool_calls) {
634
+ const idx = typeof tc.index === "number" ? tc.index : 0;
635
+ let buf = toolCalls.get(idx);
636
+ const incomingName = tc.function?.name ?? null;
637
+ const incomingId = tc.id ?? null;
638
+ if (!buf) {
639
+ buf = { id: incomingId ?? `tc_${idx}`, name: incomingName ?? "", args: "" };
640
+ toolCalls.set(idx, buf);
641
+ if (buf.name) {
642
+ yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
643
+ }
644
+ } else {
645
+ if (!buf.name && incomingName) {
646
+ buf.name = incomingName;
647
+ yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
648
+ }
649
+ if (buf.id.startsWith("tc_") && incomingId) buf.id = incomingId;
577
650
  }
578
- } else {
579
- if (!buf.name && incomingName) {
580
- buf.name = incomingName;
581
- yield { type: "tool_call_start", index: idx, id: buf.id, name: buf.name };
651
+ const argDelta = tc.function?.arguments;
652
+ if (typeof argDelta === "string" && argDelta.length) {
653
+ buf.args += argDelta;
654
+ yield { type: "tool_call_args", index: idx, argsDelta: argDelta };
582
655
  }
583
- if (buf.id.startsWith("tc_") && incomingId) buf.id = incomingId;
584
- }
585
- const argDelta = tc.function?.arguments;
586
- if (typeof argDelta === "string" && argDelta.length) {
587
- buf.args += argDelta;
588
- yield { type: "tool_call_args", index: idx, argsDelta: argDelta };
589
656
  }
590
657
  }
591
658
  }
659
+ if (choice.finish_reason) finishReason = choice.finish_reason;
592
660
  }
593
- if (choice.finish_reason) finishReason = choice.finish_reason;
594
661
  }
595
662
  for (const [idx, buf] of [...toolCalls.entries()].sort((a, b) => a[0] - b[0])) {
596
663
  if (!buf.name) continue;
@@ -965,10 +1032,129 @@ function safeJsonParse(text) {
965
1032
  return null;
966
1033
  }
967
1034
  }
968
- var EXTRACTORS;
1035
+ function truncate(str, max) {
1036
+ if (str.length <= max) return str;
1037
+ return str.slice(0, max) + "\u2026";
1038
+ }
1039
+ async function callLlm(messages, llmOpts, maxTokens = 64) {
1040
+ if (!llmOpts) return "";
1041
+ const events = runKimi({
1042
+ accountId: llmOpts.accountId,
1043
+ apiToken: llmOpts.apiToken,
1044
+ model: llmOpts.model,
1045
+ messages,
1046
+ temperature: 0.1,
1047
+ maxCompletionTokens: maxTokens,
1048
+ gateway: llmOpts.gateway,
1049
+ signal: llmOpts.signal
1050
+ });
1051
+ let text = "";
1052
+ for await (const ev of events) {
1053
+ if (ev.type === "text") text += ev.delta;
1054
+ }
1055
+ return text.trim().replace(/^["']|["']$/g, "").replace(/\s+/g, " ").toLowerCase();
1056
+ }
1057
+ async function synthesizeEditEvent(file, toolName, toolArgs, assistantMessage, llmOpts) {
1058
+ if (!llmOpts) return null;
1059
+ const oldString = typeof toolArgs.old_string === "string" ? toolArgs.old_string : "";
1060
+ const newString = typeof toolArgs.new_string === "string" ? toolArgs.new_string : "";
1061
+ const fullContent = typeof toolArgs.content === "string" ? toolArgs.content : "";
1062
+ const isWrite = toolName === "write";
1063
+ const before = isWrite ? "(new file)" : truncate(oldString, 600);
1064
+ const after = isWrite ? truncate(fullContent, 600) : truncate(newString, 600);
1065
+ const intent = assistantMessage ? assistantMessage.slice(-300).trim() : "";
1066
+ const changeContext = `File: ${file}
1067
+ Tool: ${toolName}
1068
+
1069
+ Before:
1070
+ ${before}
1071
+
1072
+ After:
1073
+ ${after}${intent ? `
1074
+
1075
+ Context: ${intent}` : ""}`;
1076
+ const summary1 = await callLlm(
1077
+ [
1078
+ { role: "system", content: EDIT_SYNTHESIS_SYSTEM },
1079
+ { role: "user", content: `${changeContext}
1080
+
1081
+ Summary:` }
1082
+ ],
1083
+ llmOpts
1084
+ );
1085
+ if (summary1.length >= 10 && summary1.length <= 200) {
1086
+ const verdict = await callLlm(
1087
+ [
1088
+ { role: "system", content: VERIFIER_SYSTEM },
1089
+ {
1090
+ role: "user",
1091
+ content: `${changeContext}
1092
+
1093
+ Proposed summary: "${summary1}"
1094
+
1095
+ Is this accurate?`
1096
+ }
1097
+ ],
1098
+ llmOpts,
1099
+ 8
1100
+ );
1101
+ if (verdict.startsWith("yes")) return summary1;
1102
+ }
1103
+ const retrySystem = `${EDIT_SYNTHESIS_SYSTEM}
1104
+
1105
+ CRITICAL: The previous summary was rejected. Be specific. Include concrete details from the After section.`;
1106
+ const summary2 = await callLlm(
1107
+ [
1108
+ { role: "system", content: retrySystem },
1109
+ { role: "user", content: `${changeContext}
1110
+
1111
+ Summary:` }
1112
+ ],
1113
+ llmOpts
1114
+ );
1115
+ if (summary2.length >= 10 && summary2.length <= 200) {
1116
+ const verdict2 = await callLlm(
1117
+ [
1118
+ { role: "system", content: VERIFIER_SYSTEM },
1119
+ {
1120
+ role: "user",
1121
+ content: `${changeContext}
1122
+
1123
+ Proposed summary: "${summary2}"
1124
+
1125
+ Is this accurate?`
1126
+ }
1127
+ ],
1128
+ llmOpts,
1129
+ 8
1130
+ );
1131
+ if (verdict2.startsWith("yes")) return summary2;
1132
+ }
1133
+ return null;
1134
+ }
1135
+ var EDIT_SYNTHESIS_SYSTEM, VERIFIER_SYSTEM, EXTRACTORS;
969
1136
  var init_extractors = __esm({
970
1137
  "src/memory/extractors.ts"() {
971
1138
  "use strict";
1139
+ init_client();
1140
+ EDIT_SYNTHESIS_SYSTEM = `You summarize a SINGLE code edit. Write ONE concise sentence (max 20 words) describing exactly what changed.
1141
+
1142
+ Rules:
1143
+ - Use ONLY the Before/After diff below.
1144
+ - For new files: describe the file's content or purpose. Never say just "added a new file".
1145
+ - For edits: describe the specific code change.
1146
+
1147
+ Examples:
1148
+ - Created test-memory.md containing the text "Memory test".
1149
+ - Fixed race condition in loop.ts by adding AbortSignal guard.
1150
+ - Added vitest dependency and removed jest from package.json.
1151
+
1152
+ Respond with only the summary sentence. No quotes, no preamble.`;
1153
+ VERIFIER_SYSTEM = `You verify whether a summary accurately describes a code edit.
1154
+ Answer exactly "yes" if the summary correctly captures what changed in the file.
1155
+ Answer exactly "no" if the summary is vague, wrong, or misses the actual change.
1156
+
1157
+ Respond with only "yes" or "no".`;
972
1158
  EXTRACTORS = [
973
1159
  {
974
1160
  id: "package_json",
@@ -1022,9 +1208,27 @@ var init_extractors = __esm({
1022
1208
  {
1023
1209
  id: "edit_event",
1024
1210
  match: (tool, file) => (tool === "edit" || tool === "write") && !!file,
1025
- extract: (_content, file) => {
1211
+ extract: async (_content, file, ctx) => {
1026
1212
  if (!file) return null;
1027
1213
  const safeKey = file.replace(/[^a-zA-Z0-9]/g, "_");
1214
+ if (ctx?.llmOpts && (ctx.toolArgs || ctx.assistantMessage)) {
1215
+ const summary = await synthesizeEditEvent(
1216
+ file,
1217
+ ctx.toolArgs?._toolName || "edit",
1218
+ ctx.toolArgs || {},
1219
+ ctx.assistantMessage,
1220
+ ctx.llmOpts
1221
+ );
1222
+ if (summary) {
1223
+ return {
1224
+ content: summary,
1225
+ category: "event",
1226
+ importance: 3,
1227
+ topicKey: `event_edit_${safeKey}`,
1228
+ relatedFiles: [file]
1229
+ };
1230
+ }
1231
+ }
1028
1232
  return {
1029
1233
  content: `File modified: ${file}.`,
1030
1234
  category: "event",
@@ -1179,6 +1383,8 @@ var init_api_generator = __esm({
1179
1383
  });
1180
1384
 
1181
1385
  // src/code-mode/sandbox.ts
1386
+ import { join as join6, dirname as dirname3 } from "path";
1387
+ import { pathToFileURL } from "url";
1182
1388
  function stripTypescript(code) {
1183
1389
  let js = code;
1184
1390
  js = js.replace(/interface\s+\w+\s*\{[\s\S]*?\n\}/g, "");
@@ -1195,6 +1401,36 @@ function stripTypescript(code) {
1195
1401
  js = js.replace(/\n{3,}/g, "\n\n");
1196
1402
  return js.trim();
1197
1403
  }
1404
+ async function loadTypescript(cwd) {
1405
+ let dir = cwd;
1406
+ while (dir !== dirname3(dir)) {
1407
+ try {
1408
+ const tsPath = join6(dir, "node_modules", "typescript", "lib", "typescript.js");
1409
+ return await import(pathToFileURL(tsPath).href);
1410
+ } catch {
1411
+ }
1412
+ dir = dirname3(dir);
1413
+ }
1414
+ return null;
1415
+ }
1416
+ async function transpileOrStrip(code, cwd) {
1417
+ const ts = await loadTypescript(cwd);
1418
+ if (ts) {
1419
+ const result = ts.transpileModule(code, {
1420
+ compilerOptions: {
1421
+ module: ts.ModuleKind.ES2022,
1422
+ target: ts.ScriptTarget.ES2022,
1423
+ esModuleInterop: true,
1424
+ isolatedModules: true
1425
+ }
1426
+ });
1427
+ return { js: result.outputText, warnings: [] };
1428
+ }
1429
+ return {
1430
+ js: stripTypescript(code),
1431
+ warnings: ["TypeScript not found in node_modules. Using fallback parser; install typescript for reliable transpilation."]
1432
+ };
1433
+ }
1198
1434
  async function runWithIsolatedVm(opts2) {
1199
1435
  const { Isolate } = await import("isolated-vm");
1200
1436
  const isolate = new Isolate({ memoryLimit: opts2.memoryLimitMB ?? 128 });
@@ -1242,7 +1478,7 @@ async function runWithIsolatedVm(opts2) {
1242
1478
  await context.eval(`var api = {
1243
1479
  ${apiMethods}
1244
1480
  };`);
1245
- const jsCode = stripTypescript(opts2.code);
1481
+ const { js: jsCode, warnings } = await transpileOrStrip(opts2.code, opts2.ctx.cwd);
1246
1482
  const wrapped = `(async function() {
1247
1483
  ${jsCode}
1248
1484
  })();`;
@@ -1253,11 +1489,11 @@ ${jsCode}
1253
1489
  await new Promise((r) => setTimeout(r, 10));
1254
1490
  } catch (err) {
1255
1491
  const message2 = err instanceof Error ? err.message : String(err);
1256
- return { output: "", logs, error: message2, toolCalls };
1492
+ return { output: "", logs, error: message2, toolCalls, warnings };
1257
1493
  } finally {
1258
1494
  isolate.dispose();
1259
1495
  }
1260
- return { output: logs.join("\n"), logs, toolCalls };
1496
+ return { output: logs.join("\n"), logs, toolCalls, warnings };
1261
1497
  }
1262
1498
  async function runWithNodeVm(opts2) {
1263
1499
  const { runInNewContext } = await import("vm");
@@ -1325,7 +1561,7 @@ async function runWithNodeVm(opts2) {
1325
1561
  return result.content;
1326
1562
  };
1327
1563
  }
1328
- const jsCode = stripTypescript(opts2.code);
1564
+ const { js: jsCode, warnings } = await transpileOrStrip(opts2.code, opts2.ctx.cwd);
1329
1565
  const wrapped = `"use strict";
1330
1566
  (async function() {
1331
1567
  ${jsCode}
@@ -1336,9 +1572,9 @@ ${jsCode}
1336
1572
  await new Promise((r) => setTimeout(r, 10));
1337
1573
  } catch (err) {
1338
1574
  const message2 = err instanceof Error ? err.message : String(err);
1339
- return { output: "", logs, error: message2, toolCalls };
1575
+ return { output: "", logs, error: message2, toolCalls, warnings };
1340
1576
  }
1341
- return { output: logs.join("\n"), logs, toolCalls };
1577
+ return { output: logs.join("\n"), logs, toolCalls, warnings };
1342
1578
  }
1343
1579
  async function runInSandbox(opts2) {
1344
1580
  try {
@@ -1499,7 +1735,9 @@ Use console.log() to return results. Only console.log output will be sent back t
1499
1735
  maxCompletionTokens: opts2.maxCompletionTokens,
1500
1736
  reasoningEffort: opts2.reasoningEffort,
1501
1737
  sessionId: opts2.sessionId,
1502
- gateway: opts2.gateway
1738
+ gateway: opts2.gateway,
1739
+ cloudMode: opts2.cloudMode,
1740
+ cloudToken: opts2.cloudToken
1503
1741
  });
1504
1742
  for await (const ev of events) {
1505
1743
  switch (ev.type) {
@@ -1678,10 +1916,13 @@ Use console.log() to return results. Only console.log output will be sent back t
1678
1916
  toolResults.push(toolResult);
1679
1917
  opts2.callbacks.onToolResult?.(toolResult);
1680
1918
  }
1681
- const resultContent = sandboxResult.error ? `Error: ${sandboxResult.error}
1919
+ const warningPrefix = sandboxResult.warnings?.length ? `Warning: ${sandboxResult.warnings.join(" ")}
1920
+
1921
+ ` : "";
1922
+ const resultContent = sandboxResult.error ? `${warningPrefix}Error: ${sandboxResult.error}
1682
1923
 
1683
1924
  Output:
1684
- ${sandboxResult.output}` : sandboxResult.output;
1925
+ ${sandboxResult.output}` : `${warningPrefix}${sandboxResult.output}`;
1685
1926
  const result = {
1686
1927
  tool_call_id: tc.id,
1687
1928
  name: "execute_code",
@@ -1715,25 +1956,44 @@ ${sandboxResult.output}` : sandboxResult.output;
1715
1956
  opts2.callbacks.onToolResult?.(result);
1716
1957
  if (opts2.memoryManager) {
1717
1958
  let filePath;
1959
+ let toolArgs = {};
1718
1960
  try {
1719
- const args = JSON.parse(tc.function.arguments || "{}");
1720
- filePath = args.path;
1961
+ toolArgs = JSON.parse(tc.function.arguments || "{}");
1962
+ filePath = toolArgs.path;
1721
1963
  } catch {
1722
1964
  }
1965
+ const lastAssistant = [...opts2.messages].reverse().find(
1966
+ (m) => m.role === "assistant" && m.tool_calls && m.tool_calls.length > 0
1967
+ );
1968
+ const assistantMessage = lastAssistant?.content ?? "";
1969
+ const llmOpts = opts2.memoryManager.getExtractionLlmOpts();
1723
1970
  for (const extractor of EXTRACTORS) {
1724
1971
  if (extractor.match(tc.function.name, filePath)) {
1725
- const memory = extractor.extract(result.content, filePath);
1726
- if (memory) {
1727
- void opts2.memoryManager.remember(
1728
- memory.content,
1729
- memory.category,
1730
- memory.importance,
1731
- opts2.cwd,
1732
- opts2.sessionId ?? "unknown",
1733
- opts2.signal
1734
- ).catch(() => {
1735
- });
1736
- }
1972
+ void (async () => {
1973
+ try {
1974
+ const memory = await extractor.extract(result.content, filePath, {
1975
+ toolArgs: { ...toolArgs, _toolName: tc.function.name },
1976
+ assistantMessage: typeof assistantMessage === "string" ? assistantMessage : "",
1977
+ llmOpts: {
1978
+ ...llmOpts,
1979
+ signal: opts2.signal
1980
+ }
1981
+ });
1982
+ if (memory) {
1983
+ await opts2.memoryManager.remember(
1984
+ memory.content,
1985
+ memory.category,
1986
+ memory.importance,
1987
+ opts2.cwd,
1988
+ opts2.sessionId ?? "unknown",
1989
+ opts2.signal,
1990
+ void 0,
1991
+ memory.topicKey
1992
+ );
1993
+ }
1994
+ } catch {
1995
+ }
1996
+ })();
1737
1997
  }
1738
1998
  }
1739
1999
  }
@@ -2014,11 +2274,11 @@ var init_mode = __esm({
2014
2274
 
2015
2275
  // src/agent/system-prompt.ts
2016
2276
  import { platform, release, homedir as homedir3 } from "os";
2017
- import { basename, join as join6 } from "path";
2277
+ import { basename, join as join7 } from "path";
2018
2278
  import { readFileSync as readFileSync2, statSync } from "fs";
2019
2279
  function loadContextFile(cwd) {
2020
2280
  for (const name of CONTEXT_FILENAMES) {
2021
- const path = join6(cwd, name);
2281
+ const path = join7(cwd, name);
2022
2282
  try {
2023
2283
  const s = statSync(path);
2024
2284
  if (!s.isFile() || s.size > MAX_CONTEXT_BYTES) continue;
@@ -2173,7 +2433,7 @@ var init_read = __esm({
2173
2433
 
2174
2434
  // src/tools/write.ts
2175
2435
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
2176
- import { dirname as dirname3 } from "path";
2436
+ import { dirname as dirname4 } from "path";
2177
2437
  var writeTool;
2178
2438
  var init_write = __esm({
2179
2439
  "src/tools/write.ts"() {
@@ -2203,7 +2463,7 @@ var init_write = __esm({
2203
2463
  before = await readFile4(abs, "utf8");
2204
2464
  } catch {
2205
2465
  }
2206
- await mkdir4(dirname3(abs), { recursive: true });
2466
+ await mkdir4(dirname4(abs), { recursive: true });
2207
2467
  await writeFile3(abs, args.content, "utf8");
2208
2468
  const verb = before ? "Overwrote" : "Created";
2209
2469
  return `${verb} ${args.path} (${args.content.length} chars).`;
@@ -2270,7 +2530,7 @@ var init_edit = __esm({
2270
2530
  // src/tools/bash.ts
2271
2531
  import { spawn } from "child_process";
2272
2532
  import { tmpdir } from "os";
2273
- import { join as join7 } from "path";
2533
+ import { join as join8 } from "path";
2274
2534
  function formatBashTitle(raw) {
2275
2535
  let cmd = (raw ?? "").trim();
2276
2536
  const m = cmd.match(/^cd\s+([^\s&;]+)\s*(?:&&|;)\s*(.*)$/);
@@ -2288,7 +2548,7 @@ function injectCoauthor(command, coauthor) {
2288
2548
  const mentionsGit = /\bgit\b/.test(trimmed);
2289
2549
  if (!createsCommit && !isRebaseContinue && !mentionsGit) return command;
2290
2550
  if (movesHeadOnly) return command;
2291
- const tmpFile = join7(tmpdir(), `kf-coauthor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
2551
+ const tmpFile = join8(tmpdir(), `kf-coauthor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
2292
2552
  const amendBlock = `
2293
2553
  if ! git log -1 --pretty=%B 2>/dev/null | grep -qF "${trailer}"; then
2294
2554
  git log -1 --pretty=%B | git interpret-trailers --trailer "${trailer}" > "${tmpFile}" && git commit --amend -F "${tmpFile}" --no-edit && rm -f "${tmpFile}"
@@ -3452,16 +3712,16 @@ var init_executor = __esm({
3452
3712
  // src/util/update-check.ts
3453
3713
  import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
3454
3714
  import { homedir as homedir5 } from "os";
3455
- import { join as join8, dirname as dirname4 } from "path";
3715
+ import { join as join9, dirname as dirname5 } from "path";
3456
3716
  import { fileURLToPath as fileURLToPath2 } from "url";
3457
3717
  function cachePath() {
3458
- const xdg = process.env.XDG_CONFIG_HOME || join8(homedir5(), ".config");
3459
- return join8(xdg, "kimiflare", "update-check.json");
3718
+ const xdg = process.env.XDG_CONFIG_HOME || join9(homedir5(), ".config");
3719
+ return join9(xdg, "kimiflare", "update-check.json");
3460
3720
  }
3461
3721
  async function findPackageJson(startDir) {
3462
3722
  let dir = startDir;
3463
3723
  while (true) {
3464
- const candidate = join8(dir, "package.json");
3724
+ const candidate = join9(dir, "package.json");
3465
3725
  try {
3466
3726
  const raw = await readFile7(candidate, "utf8");
3467
3727
  const parsed = JSON.parse(raw);
@@ -3470,14 +3730,14 @@ async function findPackageJson(startDir) {
3470
3730
  }
3471
3731
  } catch {
3472
3732
  }
3473
- const parent = dirname4(dir);
3733
+ const parent = dirname5(dir);
3474
3734
  if (parent === dir) break;
3475
3735
  dir = parent;
3476
3736
  }
3477
3737
  return null;
3478
3738
  }
3479
3739
  async function readLocalVersion() {
3480
- const here = dirname4(fileURLToPath2(import.meta.url));
3740
+ const here = dirname5(fileURLToPath2(import.meta.url));
3481
3741
  const found = await findPackageJson(here);
3482
3742
  return found?.version ?? null;
3483
3743
  }
@@ -3494,7 +3754,7 @@ async function readCache() {
3494
3754
  }
3495
3755
  async function writeCache(entry) {
3496
3756
  const p = cachePath();
3497
- await mkdir5(dirname4(p), { recursive: true });
3757
+ await mkdir5(dirname5(p), { recursive: true });
3498
3758
  await writeFile5(p, JSON.stringify(entry), "utf8");
3499
3759
  }
3500
3760
  async function fetchLatestVersion() {
@@ -3554,20 +3814,20 @@ var init_update_check = __esm({
3554
3814
  // src/remote/session-store.ts
3555
3815
  import { readFile as readFile8, writeFile as writeFile6, mkdir as mkdir6, readdir as readdir2 } from "fs/promises";
3556
3816
  import { homedir as homedir6 } from "os";
3557
- import { join as join9 } from "path";
3817
+ import { join as join10 } from "path";
3558
3818
  function remoteDir() {
3559
- const xdg = process.env.XDG_DATA_HOME || join9(homedir6(), ".config");
3560
- return join9(xdg, "kimiflare", "remote");
3819
+ const xdg = process.env.XDG_DATA_HOME || join10(homedir6(), ".config");
3820
+ return join10(xdg, "kimiflare", "remote");
3561
3821
  }
3562
3822
  async function saveRemoteSession(session) {
3563
3823
  const dir = remoteDir();
3564
3824
  await mkdir6(dir, { recursive: true });
3565
- const path = join9(dir, `${session.sessionId}.json`);
3825
+ const path = join10(dir, `${session.sessionId}.json`);
3566
3826
  await writeFile6(path, JSON.stringify(session, null, 2) + "\n", "utf8");
3567
3827
  }
3568
3828
  async function loadRemoteSession(sessionId) {
3569
3829
  try {
3570
- const path = join9(remoteDir(), `${sessionId}.json`);
3830
+ const path = join10(remoteDir(), `${sessionId}.json`);
3571
3831
  const raw = await readFile8(path, "utf8");
3572
3832
  return JSON.parse(raw);
3573
3833
  } catch {
@@ -3582,7 +3842,7 @@ async function listRemoteSessions() {
3582
3842
  for (const file of files) {
3583
3843
  if (!file.endsWith(".json")) continue;
3584
3844
  try {
3585
- const raw = await readFile8(join9(dir, file), "utf8");
3845
+ const raw = await readFile8(join10(dir, file), "utf8");
3586
3846
  sessions.push(JSON.parse(raw));
3587
3847
  } catch {
3588
3848
  }
@@ -3604,7 +3864,7 @@ var init_session_store = __esm({
3604
3864
 
3605
3865
  // src/remote/deploy.ts
3606
3866
  import { execSync } from "child_process";
3607
- import { join as join10, dirname as dirname5 } from "path";
3867
+ import { join as join11, dirname as dirname6 } from "path";
3608
3868
  import { fileURLToPath as fileURLToPath3 } from "url";
3609
3869
  import { randomBytes } from "crypto";
3610
3870
  function generateSecret() {
@@ -3639,7 +3899,7 @@ async function* deployForTui() {
3639
3899
  yield { message: "Docker OK" };
3640
3900
  yield { message: "Building remote agent bundle..." };
3641
3901
  try {
3642
- runCapture("npm run build:remote-agent", join10(REMOTE_DIR, ".."));
3902
+ runCapture("npm run build:remote-agent", join11(REMOTE_DIR, ".."));
3643
3903
  yield { message: "Agent bundle built" };
3644
3904
  } catch (err) {
3645
3905
  yield { message: `Build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
@@ -3768,9 +4028,9 @@ var init_deploy = __esm({
3768
4028
  "src/remote/deploy.ts"() {
3769
4029
  "use strict";
3770
4030
  init_config();
3771
- __dirname = dirname5(fileURLToPath3(import.meta.url));
3772
- REMOTE_DIR = join10(__dirname, "..", "..", "..", "remote");
3773
- WORKER_DIR = join10(REMOTE_DIR, "worker");
4031
+ __dirname = dirname6(fileURLToPath3(import.meta.url));
4032
+ REMOTE_DIR = join11(__dirname, "..", "..", "..", "remote");
4033
+ WORKER_DIR = join11(REMOTE_DIR, "worker");
3774
4034
  }
3775
4035
  });
3776
4036
 
@@ -4194,11 +4454,11 @@ var init_heuristic = __esm({
4194
4454
 
4195
4455
  // src/cost-attribution/classify-from-session.ts
4196
4456
  import { readFile as readFile9 } from "fs/promises";
4197
- import { join as join11 } from "path";
4457
+ import { join as join12 } from "path";
4198
4458
  import { homedir as homedir7 } from "os";
4199
4459
  function sessionsDir() {
4200
- const xdg = process.env.XDG_DATA_HOME || join11(homedir7(), ".local", "share");
4201
- return join11(xdg, "kimiflare", "sessions");
4460
+ const xdg = process.env.XDG_DATA_HOME || join12(homedir7(), ".local", "share");
4461
+ return join12(xdg, "kimiflare", "sessions");
4202
4462
  }
4203
4463
  function parseToolCalls(calls) {
4204
4464
  return calls.map((c) => {
@@ -4212,7 +4472,7 @@ function parseToolCalls(calls) {
4212
4472
  }
4213
4473
  async function classifyFromSessionFile(sessionId) {
4214
4474
  try {
4215
- const raw = await readFile9(join11(sessionsDir(), `${sessionId}.json`), "utf8");
4475
+ const raw = await readFile9(join12(sessionsDir(), `${sessionId}.json`), "utf8");
4216
4476
  const session = JSON.parse(raw);
4217
4477
  const messages = session.messages ?? [];
4218
4478
  const turns = [];
@@ -4246,14 +4506,14 @@ __export(cli_exports, {
4246
4506
  runCostCommand: () => runCostCommand
4247
4507
  });
4248
4508
  import { readFile as readFile10 } from "fs/promises";
4249
- import { join as join12 } from "path";
4509
+ import { join as join13 } from "path";
4250
4510
  import { homedir as homedir8 } from "os";
4251
4511
  function usageDir() {
4252
- const xdg = process.env.XDG_DATA_HOME || join12(homedir8(), ".local", "share");
4253
- return join12(xdg, "kimiflare");
4512
+ const xdg = process.env.XDG_DATA_HOME || join13(homedir8(), ".local", "share");
4513
+ return join13(xdg, "kimiflare");
4254
4514
  }
4255
4515
  function usagePath() {
4256
- return join12(usageDir(), "usage.json");
4516
+ return join13(usageDir(), "usage.json");
4257
4517
  }
4258
4518
  function today() {
4259
4519
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -4351,6 +4611,98 @@ var init_cli = __esm({
4351
4611
  }
4352
4612
  });
4353
4613
 
4614
+ // src/cloud/auth.ts
4615
+ var auth_exports = {};
4616
+ __export(auth_exports, {
4617
+ authenticateDevice: () => authenticateDevice,
4618
+ clearCloudCredentials: () => clearCloudCredentials,
4619
+ loadCloudCredentials: () => loadCloudCredentials,
4620
+ saveCloudCredentials: () => saveCloudCredentials
4621
+ });
4622
+ import { readFile as readFile11, writeFile as writeFile7 } from "fs/promises";
4623
+ import { homedir as homedir9 } from "os";
4624
+ import { join as join14 } from "path";
4625
+ function cloudCredPath() {
4626
+ const xdg = process.env.XDG_CONFIG_HOME || join14(homedir9(), ".config");
4627
+ return join14(xdg, "kimiflare", "cloud.json");
4628
+ }
4629
+ function generateCode() {
4630
+ const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
4631
+ let out = "";
4632
+ for (let i = 0; i < 8; i++) {
4633
+ out += chars[Math.floor(Math.random() * chars.length)];
4634
+ }
4635
+ return out;
4636
+ }
4637
+ async function loadCloudCredentials() {
4638
+ try {
4639
+ const raw = await readFile11(cloudCredPath(), "utf8");
4640
+ const parsed = JSON.parse(raw);
4641
+ if (parsed.expiresAt && parsed.expiresAt > Date.now() / 1e3) {
4642
+ return parsed;
4643
+ }
4644
+ } catch {
4645
+ }
4646
+ return null;
4647
+ }
4648
+ async function saveCloudCredentials(creds) {
4649
+ const p = cloudCredPath();
4650
+ await writeFile7(p, JSON.stringify(creds, null, 2), "utf8");
4651
+ }
4652
+ async function clearCloudCredentials() {
4653
+ try {
4654
+ const { unlink: unlink4 } = await import("fs/promises");
4655
+ await unlink4(cloudCredPath());
4656
+ } catch {
4657
+ }
4658
+ }
4659
+ async function authenticateDevice(onStatus) {
4660
+ const deviceCode = `device-${generateCode()}-${Date.now()}`;
4661
+ const userCode = `${generateCode()}-${generateCode()}`;
4662
+ const registerRes = await fetch(`${CLOUD_API_URL}/auth/device`, {
4663
+ method: "POST",
4664
+ headers: { "Content-Type": "application/json" },
4665
+ body: JSON.stringify({ device_code: deviceCode, user_code: userCode })
4666
+ });
4667
+ if (!registerRes.ok) {
4668
+ const err = await registerRes.json().catch(() => ({}));
4669
+ throw new Error(`Failed to register device: ${err.error || registerRes.statusText}`);
4670
+ }
4671
+ const authUrl = `${CLOUD_API_URL}/auth/github?code=${encodeURIComponent(userCode)}`;
4672
+ onStatus({ url: authUrl, userCode, polling: false });
4673
+ const startTime = Date.now();
4674
+ while (Date.now() - startTime < POLL_TIMEOUT_MS) {
4675
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
4676
+ onStatus({ url: authUrl, userCode, polling: true });
4677
+ const pollRes = await fetch(`${CLOUD_API_URL}/auth/poll`, {
4678
+ method: "POST",
4679
+ headers: { "Content-Type": "application/json" },
4680
+ body: JSON.stringify({ device_code: deviceCode })
4681
+ });
4682
+ if (!pollRes.ok) continue;
4683
+ const pollData = await pollRes.json();
4684
+ if (pollData.status === "approved" && pollData.access_token) {
4685
+ const creds = {
4686
+ accessToken: pollData.access_token,
4687
+ expiresAt: Math.floor(Date.now() / 1e3) + 7 * 24 * 60 * 60
4688
+ // 7 days
4689
+ };
4690
+ await saveCloudCredentials(creds);
4691
+ return creds;
4692
+ }
4693
+ }
4694
+ throw new Error("Authentication timed out. Please try again.");
4695
+ }
4696
+ var CLOUD_API_URL, POLL_INTERVAL_MS, POLL_TIMEOUT_MS;
4697
+ var init_auth = __esm({
4698
+ "src/cloud/auth.ts"() {
4699
+ "use strict";
4700
+ CLOUD_API_URL = "https://api.kimiflare.com";
4701
+ POLL_INTERVAL_MS = 5e3;
4702
+ POLL_TIMEOUT_MS = 10 * 60 * 1e3;
4703
+ }
4704
+ });
4705
+
4354
4706
  // src/remote/tui-auth.ts
4355
4707
  var tui_auth_exports = {};
4356
4708
  __export(tui_auth_exports, {
@@ -5260,9 +5612,9 @@ var init_connection = __esm({
5260
5612
  });
5261
5613
 
5262
5614
  // src/lsp/protocol.ts
5263
- import { pathToFileURL, fileURLToPath as fileURLToPath4 } from "url";
5615
+ import { pathToFileURL as pathToFileURL2, fileURLToPath as fileURLToPath4 } from "url";
5264
5616
  function toUri(path) {
5265
- return pathToFileURL(path).href;
5617
+ return pathToFileURL2(path).href;
5266
5618
  }
5267
5619
  function fromUri(uri) {
5268
5620
  return fileURLToPath4(uri);
@@ -6402,7 +6754,7 @@ import { useEffect, useState } from "react";
6402
6754
  import { Box as Box5, Text as Text5 } from "ink";
6403
6755
  import Spinner3 from "ink-spinner";
6404
6756
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
6405
- function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode, effort, contextLimit, hasUpdate, latestVersion, gatewayMeta, codeMode }) {
6757
+ function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode, effort, contextLimit, hasUpdate, latestVersion, gatewayMeta, codeMode, cloudMode }) {
6406
6758
  const theme = useTheme();
6407
6759
  const [now2, setNow] = useState(Date.now());
6408
6760
  const modeColor = mode === "plan" ? theme.modeBadge.plan : mode === "auto" ? theme.modeBadge.auto : theme.modeBadge.edit;
@@ -6414,6 +6766,7 @@ function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode,
6414
6766
  }, [thinking, turnStartedAt]);
6415
6767
  const elapsed = turnStartedAt !== null ? formatElapsed(now2 - turnStartedAt) : null;
6416
6768
  const leftParts = [`${shortModel(model)}`, effort];
6769
+ if (cloudMode) leftParts.push("CLOUD");
6417
6770
  if (codeMode) leftParts.push("CODE");
6418
6771
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
6419
6772
  /* @__PURE__ */ jsxs5(Box5, { children: [
@@ -7468,15 +7821,25 @@ var init_text_input = __esm({
7468
7821
  // src/ui/onboarding.tsx
7469
7822
  import { useState as useState5 } from "react";
7470
7823
  import { Box as Box9, Text as Text10 } from "ink";
7824
+ import SelectInput3 from "ink-select-input";
7471
7825
  import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
7472
7826
  function Onboarding({ onDone }) {
7473
7827
  const theme = useTheme();
7474
- const [step, setStep] = useState5("accountId");
7828
+ const [step, setStep] = useState5("mode");
7829
+ const [mode, setMode] = useState5("byok");
7475
7830
  const [accountId, setAccountId] = useState5("");
7476
7831
  const [apiToken, setApiToken] = useState5("");
7477
7832
  const [model, setModel] = useState5(DEFAULT_MODEL);
7478
7833
  const [savedPath, setSavedPath] = useState5(null);
7479
- const stepIndex = STEPS.indexOf(step) + 1;
7834
+ const handleModeSelect = (item) => {
7835
+ if (item.value === "cloud") {
7836
+ setMode("cloud");
7837
+ setStep("cloudDone");
7838
+ } else {
7839
+ setMode("byok");
7840
+ setStep("accountId");
7841
+ }
7842
+ };
7480
7843
  const handleAccountIdSubmit = (value) => {
7481
7844
  const trimmed = value.trim();
7482
7845
  if (!trimmed) return;
@@ -7495,7 +7858,7 @@ function Onboarding({ onDone }) {
7495
7858
  setStep("confirm");
7496
7859
  };
7497
7860
  const handleConfirm = async () => {
7498
- const cfg = { accountId, apiToken, model };
7861
+ const cfg = mode === "cloud" ? { accountId: "", apiToken: "", model, cloudMode: true } : { accountId, apiToken, model };
7499
7862
  try {
7500
7863
  const path = await saveConfig(cfg);
7501
7864
  setSavedPath(path);
@@ -7504,6 +7867,19 @@ function Onboarding({ onDone }) {
7504
7867
  setSavedPath(`error: ${e.message}`);
7505
7868
  }
7506
7869
  };
7870
+ const handleCloudSave = async () => {
7871
+ const cfg = { accountId: "", apiToken: "", model: DEFAULT_MODEL, cloudMode: true };
7872
+ try {
7873
+ const path = await saveConfig(cfg);
7874
+ setSavedPath(path);
7875
+ onDone(cfg);
7876
+ } catch (e) {
7877
+ setSavedPath(`error: ${e.message}`);
7878
+ }
7879
+ };
7880
+ const byokSteps = ["accountId", "apiToken", "model", "confirm"];
7881
+ const stepIndex = step === "mode" ? 1 : step === "cloudDone" ? 2 : byokSteps.indexOf(step) + 2;
7882
+ const totalSteps = mode === "cloud" ? 2 : byokSteps.length + 1;
7507
7883
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingY: 1, children: [
7508
7884
  /* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
7509
7885
  /* @__PURE__ */ jsx11(Text10, { bold: true, color: theme.palette.primary, children: "kimiflare" }),
@@ -7516,9 +7892,22 @@ function Onboarding({ onDone }) {
7516
7892
  "Step ",
7517
7893
  stepIndex,
7518
7894
  " of ",
7519
- STEPS.length
7895
+ totalSteps
7520
7896
  ] }),
7521
7897
  /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
7898
+ step === "mode" && /* @__PURE__ */ jsxs9(Fragment, { children: [
7899
+ /* @__PURE__ */ jsx11(Text10, { children: "How do you want to connect?" }),
7900
+ /* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx11(
7901
+ SelectInput3,
7902
+ {
7903
+ items: [
7904
+ { label: "Cloud (managed) \u2014 no API key needed", value: "cloud" },
7905
+ { label: "BYOK \u2014 bring your own Cloudflare key", value: "byok" }
7906
+ ],
7907
+ onSelect: handleModeSelect
7908
+ }
7909
+ ) })
7910
+ ] }),
7522
7911
  step === "accountId" && /* @__PURE__ */ jsxs9(Fragment, { children: [
7523
7912
  /* @__PURE__ */ jsx11(Text10, { children: "Enter your Cloudflare Account ID" }),
7524
7913
  /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
@@ -7608,6 +7997,23 @@ function Onboarding({ onDone }) {
7608
7997
  )
7609
7998
  ] })
7610
7999
  ] }),
8000
+ step === "cloudDone" && /* @__PURE__ */ jsxs9(Fragment, { children: [
8001
+ /* @__PURE__ */ jsx11(Text10, { children: "Cloud mode selected" }),
8002
+ /* @__PURE__ */ jsx11(Text10, { color: theme.info.color, children: "No API key needed. Run `kimiflare auth cloud` to sign in." }),
8003
+ /* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text10, { children: "Press Enter to save, or Ctrl+C to cancel" }) }),
8004
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
8005
+ /* @__PURE__ */ jsx11(Text10, { color: theme.palette.primary, children: "\u203A " }),
8006
+ /* @__PURE__ */ jsx11(
8007
+ CustomTextInput,
8008
+ {
8009
+ value: "",
8010
+ onChange: () => {
8011
+ },
8012
+ onSubmit: handleCloudSave
8013
+ }
8014
+ )
8015
+ ] })
8016
+ ] }),
7611
8017
  savedPath && /* @__PURE__ */ jsxs9(Text10, { color: theme.palette.success, children: [
7612
8018
  "Config saved to ",
7613
8019
  savedPath
@@ -7615,14 +8021,12 @@ function Onboarding({ onDone }) {
7615
8021
  ] })
7616
8022
  ] });
7617
8023
  }
7618
- var STEPS;
7619
8024
  var init_onboarding = __esm({
7620
8025
  "src/ui/onboarding.tsx"() {
7621
8026
  "use strict";
7622
8027
  init_text_input();
7623
8028
  init_config();
7624
8029
  init_theme_context();
7625
- STEPS = ["accountId", "apiToken", "model", "confirm"];
7626
8030
  }
7627
8031
  });
7628
8032
 
@@ -7673,9 +8077,9 @@ var init_welcome = __esm({
7673
8077
  // src/ui/help-menu.tsx
7674
8078
  import { useState as useState6 } from "react";
7675
8079
  import { Box as Box11, Text as Text12, useInput as useInput2 } from "ink";
7676
- import SelectInput3 from "ink-select-input";
8080
+ import SelectInput4 from "ink-select-input";
7677
8081
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
7678
- function HelpMenu({ customCommands, costAttributionEnabled, onDone, onCommand }) {
8082
+ function HelpMenu({ customCommands, costAttributionEnabled, cloudMode, onDone, onCommand }) {
7679
8083
  const theme = useTheme();
7680
8084
  const [page, setPage] = useState6("main");
7681
8085
  const customs = customCommands ?? [];
@@ -7692,8 +8096,9 @@ function HelpMenu({ customCommands, costAttributionEnabled, onDone, onCommand })
7692
8096
  onCommand(command);
7693
8097
  onDone();
7694
8098
  };
8099
+ const categories = cloudMode ? CATEGORIES.filter((c) => c.key !== "gateway") : CATEGORIES;
7695
8100
  if (page === "main") {
7696
- const items2 = CATEGORIES.map((cat) => ({
8101
+ const items2 = categories.map((cat) => ({
7697
8102
  label: cat.label,
7698
8103
  value: cat.key,
7699
8104
  key: cat.key
@@ -7706,7 +8111,7 @@ function HelpMenu({ customCommands, costAttributionEnabled, onDone, onCommand })
7706
8111
  /* @__PURE__ */ jsx13(Text12, { color: theme.accent, bold: true, children: "Help" }),
7707
8112
  /* @__PURE__ */ jsx13(Text12, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select, Esc to close." }),
7708
8113
  /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(
7709
- SelectInput3,
8114
+ SelectInput4,
7710
8115
  {
7711
8116
  items: items2,
7712
8117
  onSelect: (item) => {
@@ -7733,7 +8138,7 @@ function HelpMenu({ customCommands, costAttributionEnabled, onDone, onCommand })
7733
8138
  /* @__PURE__ */ jsx13(Text12, { color: theme.accent, bold: true, children: "Custom commands" }),
7734
8139
  /* @__PURE__ */ jsx13(Text12, { color: theme.info.color, dimColor: false, children: customs.length === 0 ? "no custom commands found in .kimiflare/commands/" : "Arrow keys to navigate, Enter to run, Esc to go back." }),
7735
8140
  /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(
7736
- SelectInput3,
8141
+ SelectInput4,
7737
8142
  {
7738
8143
  items: items2,
7739
8144
  onSelect: (item) => {
@@ -7747,7 +8152,7 @@ function HelpMenu({ customCommands, costAttributionEnabled, onDone, onCommand })
7747
8152
  ) })
7748
8153
  ] });
7749
8154
  }
7750
- const category = CATEGORIES.find((c) => c.key === page);
8155
+ const category = categories.find((c) => c.key === page);
7751
8156
  const selectable = category.commands.filter((cmd) => cmd.selectable !== false);
7752
8157
  const staticCmds = category.commands.filter((cmd) => cmd.selectable === false);
7753
8158
  const items = selectable.map((cmd) => ({
@@ -7760,7 +8165,7 @@ function HelpMenu({ customCommands, costAttributionEnabled, onDone, onCommand })
7760
8165
  /* @__PURE__ */ jsx13(Text12, { color: theme.accent, bold: true, children: category.label }),
7761
8166
  /* @__PURE__ */ jsx13(Text12, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to execute, Esc to go back." }),
7762
8167
  /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(
7763
- SelectInput3,
8168
+ SelectInput4,
7764
8169
  {
7765
8170
  items,
7766
8171
  onSelect: (item) => {
@@ -8006,7 +8411,7 @@ var init_tui_deploy = __esm({
8006
8411
  // src/ui/remote-dashboard.tsx
8007
8412
  import { useEffect as useEffect4, useState as useState7 } from "react";
8008
8413
  import { Box as Box12, Text as Text13, useInput as useInput3 } from "ink";
8009
- import SelectInput4 from "ink-select-input";
8414
+ import SelectInput5 from "ink-select-input";
8010
8415
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
8011
8416
  function RemoteDashboard({ onSelect, onCancel }) {
8012
8417
  const theme = useTheme();
@@ -8087,7 +8492,7 @@ function RemoteDashboard({ onSelect, onCancel }) {
8087
8492
  refreshing ? "(refreshing...)" : ""
8088
8493
  ] }),
8089
8494
  /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(
8090
- SelectInput4,
8495
+ SelectInput5,
8091
8496
  {
8092
8497
  items,
8093
8498
  onSelect: (item) => {
@@ -8258,12 +8663,12 @@ __export(sessions_exports, {
8258
8663
  pruneSessions: () => pruneSessions,
8259
8664
  saveSession: () => saveSession
8260
8665
  });
8261
- import { readFile as readFile11, writeFile as writeFile7, mkdir as mkdir7, readdir as readdir3, stat as stat4 } from "fs/promises";
8262
- import { homedir as homedir9 } from "os";
8263
- import { join as join13 } from "path";
8666
+ import { readFile as readFile12, writeFile as writeFile8, mkdir as mkdir7, readdir as readdir3, stat as stat4 } from "fs/promises";
8667
+ import { homedir as homedir10 } from "os";
8668
+ import { join as join15 } from "path";
8264
8669
  function sessionsDir2() {
8265
- const xdg = process.env.XDG_DATA_HOME || join13(homedir9(), ".local", "share");
8266
- return join13(xdg, "kimiflare", "sessions");
8670
+ const xdg = process.env.XDG_DATA_HOME || join15(homedir10(), ".local", "share");
8671
+ return join15(xdg, "kimiflare", "sessions");
8267
8672
  }
8268
8673
  function sanitize(text) {
8269
8674
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -8276,8 +8681,8 @@ function makeSessionId(firstPrompt) {
8276
8681
  async function saveSession(file) {
8277
8682
  const dir = sessionsDir2();
8278
8683
  await mkdir7(dir, { recursive: true });
8279
- const path = join13(dir, `${file.id}.json`);
8280
- await writeFile7(path, JSON.stringify(file, null, 2), "utf8");
8684
+ const path = join15(dir, `${file.id}.json`);
8685
+ await writeFile8(path, JSON.stringify(file, null, 2), "utf8");
8281
8686
  return path;
8282
8687
  }
8283
8688
  async function pruneSessions() {
@@ -8296,9 +8701,9 @@ async function listSessions(limit = 30, cwd) {
8296
8701
  const summaries = [];
8297
8702
  for (const name of entries) {
8298
8703
  if (!name.endsWith(".json")) continue;
8299
- const path = join13(dir, name);
8704
+ const path = join15(dir, name);
8300
8705
  try {
8301
- const [s, raw] = await Promise.all([stat4(path), readFile11(path, "utf8")]);
8706
+ const [s, raw] = await Promise.all([stat4(path), readFile12(path, "utf8")]);
8302
8707
  const parsed = JSON.parse(raw);
8303
8708
  if (cwd && parsed.cwd !== cwd) continue;
8304
8709
  const firstUser = parsed.messages.find((m) => m.role === "user");
@@ -8318,7 +8723,7 @@ async function listSessions(limit = 30, cwd) {
8318
8723
  return summaries.slice(0, limit);
8319
8724
  }
8320
8725
  async function loadSession(filePath) {
8321
- const raw = await readFile11(filePath, "utf8");
8726
+ const raw = await readFile12(filePath, "utf8");
8322
8727
  return JSON.parse(raw);
8323
8728
  }
8324
8729
  var init_sessions = __esm({
@@ -8329,10 +8734,10 @@ var init_sessions = __esm({
8329
8734
  });
8330
8735
 
8331
8736
  // src/util/image.ts
8332
- import { readFile as readFile12 } from "fs/promises";
8737
+ import { readFile as readFile13 } from "fs/promises";
8333
8738
  import { basename as basename3 } from "path";
8334
8739
  async function encodeImageFile(filePath) {
8335
- const buf = await readFile12(filePath);
8740
+ const buf = await readFile13(filePath);
8336
8741
  if (buf.byteLength > MAX_IMAGE_BYTES) {
8337
8742
  throw new Error(
8338
8743
  `image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
@@ -8368,15 +8773,15 @@ var init_image = __esm({
8368
8773
  });
8369
8774
 
8370
8775
  // src/usage-tracker.ts
8371
- import { readFile as readFile13, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
8372
- import { homedir as homedir10 } from "os";
8373
- import { join as join14 } from "path";
8776
+ import { readFile as readFile14, writeFile as writeFile9, mkdir as mkdir8 } from "fs/promises";
8777
+ import { homedir as homedir11 } from "os";
8778
+ import { join as join16 } from "path";
8374
8779
  function usageDir2() {
8375
- const xdg = process.env.XDG_DATA_HOME || join14(homedir10(), ".local", "share");
8376
- return join14(xdg, "kimiflare");
8780
+ const xdg = process.env.XDG_DATA_HOME || join16(homedir11(), ".local", "share");
8781
+ return join16(xdg, "kimiflare");
8377
8782
  }
8378
8783
  function usagePath2() {
8379
- return join14(usageDir2(), "usage.json");
8784
+ return join16(usageDir2(), "usage.json");
8380
8785
  }
8381
8786
  function today2() {
8382
8787
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -8387,7 +8792,7 @@ function cutoffDate(daysBack) {
8387
8792
  }
8388
8793
  async function loadLog2() {
8389
8794
  try {
8390
- const raw = await readFile13(usagePath2(), "utf8");
8795
+ const raw = await readFile14(usagePath2(), "utf8");
8391
8796
  const parsed = JSON.parse(raw);
8392
8797
  if (parsed.version === LOG_VERSION2) return parsed;
8393
8798
  } catch {
@@ -8396,7 +8801,7 @@ async function loadLog2() {
8396
8801
  }
8397
8802
  async function saveLog(log) {
8398
8803
  await mkdir8(usageDir2(), { recursive: true });
8399
- await writeFile8(usagePath2(), JSON.stringify(log, null, 2), "utf8");
8804
+ await writeFile9(usagePath2(), JSON.stringify(log, null, 2), "utf8");
8400
8805
  }
8401
8806
  function getOrCreateDay(log, date) {
8402
8807
  let day = log.days.find((d) => d.date === date);
@@ -8602,7 +9007,7 @@ __export(db_exports, {
8602
9007
  updateMemoryEmbedding: () => updateMemoryEmbedding
8603
9008
  });
8604
9009
  import Database from "better-sqlite3";
8605
- import { dirname as dirname6 } from "path";
9010
+ import { dirname as dirname7 } from "path";
8606
9011
  import { mkdirSync, statSync as statSync2 } from "fs";
8607
9012
  function initSchema(db) {
8608
9013
  db.exec(`
@@ -8687,7 +9092,7 @@ function openMemoryDb(dbPath) {
8687
9092
  if (dbInstance) {
8688
9093
  dbInstance.close();
8689
9094
  }
8690
- mkdirSync(dirname6(dbPath), { recursive: true });
9095
+ mkdirSync(dirname7(dbPath), { recursive: true });
8691
9096
  dbInstance = new Database(dbPath);
8692
9097
  dbInstance.pragma("journal_mode = WAL");
8693
9098
  dbInstance.pragma("foreign_keys = ON");
@@ -9392,6 +9797,18 @@ Return a JSON array of strings. Example:
9392
9797
  gateway: this.opts.gateway
9393
9798
  };
9394
9799
  }
9800
+ get extractionLlmOpts() {
9801
+ return {
9802
+ accountId: this.opts.accountId,
9803
+ apiToken: this.opts.apiToken,
9804
+ model: this.opts.extractionModel ?? "@cf/meta/llama-3.2-3b-instruct",
9805
+ gateway: this.opts.gateway
9806
+ };
9807
+ }
9808
+ /** Expose extraction LLM opts so the agent loop can pass them to extractors. */
9809
+ getExtractionLlmOpts() {
9810
+ return this.extractionLlmOpts;
9811
+ }
9395
9812
  shouldRedact() {
9396
9813
  return this.opts.redactSecrets !== false;
9397
9814
  }
@@ -9399,7 +9816,7 @@ Return a JSON array of strings. Example:
9399
9816
  * Store a memory with verification, topic-key normalization, hypothetical queries,
9400
9817
  * secret redaction, and supersession.
9401
9818
  */
9402
- async remember(content, category, importance, repoPath, sessionId, signal, agentRole) {
9819
+ async remember(content, category, importance, repoPath, sessionId, signal, agentRole, topicKey) {
9403
9820
  if (!this.db) throw new Error("Memory DB not open");
9404
9821
  let safeContent = this.shouldRedact() ? redactSecrets(content) : content;
9405
9822
  if (!safeContent.trim()) {
@@ -9412,10 +9829,10 @@ Return a JSON array of strings. Example:
9412
9829
  if (verified.corrected_content) {
9413
9830
  safeContent = verified.corrected_content;
9414
9831
  }
9415
- const topicKey = this.normalizeTopicKey(safeContent, repoPath);
9832
+ const resolvedTopicKey = topicKey?.trim() || this.normalizeTopicKey(safeContent, repoPath);
9416
9833
  const supersededIds = [];
9417
- if (topicKey) {
9418
- const existing = findMemoriesByTopicKey(this.db, repoPath, topicKey);
9834
+ if (resolvedTopicKey) {
9835
+ const existing = findMemoriesByTopicKey(this.db, repoPath, resolvedTopicKey);
9419
9836
  for (const old of existing) {
9420
9837
  supersedeMemory(this.db, old.id, "pending");
9421
9838
  supersededIds.push(old.id);
@@ -9436,7 +9853,7 @@ Return a JSON array of strings. Example:
9436
9853
  sourceSessionId: sessionId,
9437
9854
  repoPath,
9438
9855
  importance: Math.max(1, Math.min(5, importance)),
9439
- topicKey: topicKey ?? void 0,
9856
+ topicKey: resolvedTopicKey ?? void 0,
9440
9857
  agentRole
9441
9858
  };
9442
9859
  const memory = insertMemory(this.db, input, embeddings[0]);
@@ -9614,16 +10031,16 @@ Context: This memory was explicitly provided by the user during a conversation.`
9614
10031
  });
9615
10032
 
9616
10033
  // src/util/state.ts
9617
- import { readFile as readFile14, writeFile as writeFile9, mkdir as mkdir9 } from "fs/promises";
9618
- import { homedir as homedir11 } from "os";
9619
- import { join as join16 } from "path";
10034
+ import { readFile as readFile15, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
10035
+ import { homedir as homedir12 } from "os";
10036
+ import { join as join18 } from "path";
9620
10037
  function statePath() {
9621
- const xdg = process.env.XDG_CONFIG_HOME || join16(homedir11(), ".config");
9622
- return join16(xdg, "kimiflare", "state.json");
10038
+ const xdg = process.env.XDG_CONFIG_HOME || join18(homedir12(), ".config");
10039
+ return join18(xdg, "kimiflare", "state.json");
9623
10040
  }
9624
10041
  async function readState() {
9625
10042
  try {
9626
- const raw = await readFile14(statePath(), "utf8");
10043
+ const raw = await readFile15(statePath(), "utf8");
9627
10044
  return JSON.parse(raw);
9628
10045
  } catch {
9629
10046
  return {};
@@ -9631,8 +10048,8 @@ async function readState() {
9631
10048
  }
9632
10049
  async function writeState(state) {
9633
10050
  const path = statePath();
9634
- await mkdir9(join16(path, ".."), { recursive: true });
9635
- await writeFile9(path, JSON.stringify(state, null, 2) + "\n", "utf8");
10051
+ await mkdir9(join18(path, ".."), { recursive: true });
10052
+ await writeFile10(path, JSON.stringify(state, null, 2) + "\n", "utf8");
9636
10053
  }
9637
10054
  async function markCreatorMessageSeen(version) {
9638
10055
  const state = await readState();
@@ -9705,15 +10122,15 @@ var init_frontmatter = __esm({
9705
10122
 
9706
10123
  // src/commands/loader.ts
9707
10124
  import { open, realpath } from "fs/promises";
9708
- import { homedir as homedir12 } from "os";
9709
- import { join as join17, relative as relative4, sep as sep2 } from "path";
10125
+ import { homedir as homedir13 } from "os";
10126
+ import { join as join19, relative as relative4, sep as sep2 } from "path";
9710
10127
  import fg3 from "fast-glob";
9711
10128
  function projectCommandsDir(cwd = process.cwd()) {
9712
- return join17(cwd, ".kimiflare", "commands");
10129
+ return join19(cwd, ".kimiflare", "commands");
9713
10130
  }
9714
10131
  function globalCommandsDir() {
9715
- const xdg = process.env.XDG_CONFIG_HOME || join17(homedir12(), ".config");
9716
- return join17(xdg, "kimiflare", "commands");
10132
+ const xdg = process.env.XDG_CONFIG_HOME || join19(homedir13(), ".config");
10133
+ return join19(xdg, "kimiflare", "commands");
9717
10134
  }
9718
10135
  async function loadCustomCommands(cwd = process.cwd()) {
9719
10136
  const warnings = [];
@@ -10089,8 +10506,8 @@ var init_builtins = __esm({
10089
10506
  });
10090
10507
 
10091
10508
  // src/commands/save.ts
10092
- import { mkdir as mkdir10, writeFile as writeFile10, unlink as unlink2 } from "fs/promises";
10093
- import { dirname as dirname7 } from "path";
10509
+ import { mkdir as mkdir10, writeFile as writeFile11, unlink as unlink2 } from "fs/promises";
10510
+ import { dirname as dirname8 } from "path";
10094
10511
  async function saveCustomCommand(opts2) {
10095
10512
  const dir = opts2.source === "project" ? projectCommandsDir(opts2.cwd) : globalCommandsDir();
10096
10513
  const filepath = `${dir}/${opts2.name}.md`;
@@ -10101,8 +10518,8 @@ async function saveCustomCommand(opts2) {
10101
10518
  if (opts2.effort) data.effort = opts2.effort;
10102
10519
  const frontmatter = serializeFrontmatter(data);
10103
10520
  const content = frontmatter + opts2.template;
10104
- await mkdir10(dirname7(filepath), { recursive: true });
10105
- await writeFile10(filepath, content, "utf8");
10521
+ await mkdir10(dirname8(filepath), { recursive: true });
10522
+ await writeFile11(filepath, content, "utf8");
10106
10523
  return { filepath };
10107
10524
  }
10108
10525
  async function deleteCustomCommand(cmd) {
@@ -10119,7 +10536,7 @@ var init_save = __esm({
10119
10536
  // src/ui/command-wizard.tsx
10120
10537
  import { useState as useState8 } from "react";
10121
10538
  import { Box as Box13, Text as Text14, useInput as useInput4, useWindowSize as useWindowSize2 } from "ink";
10122
- import SelectInput5 from "ink-select-input";
10539
+ import SelectInput6 from "ink-select-input";
10123
10540
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
10124
10541
  function CommandWizard({ mode, initial, existingNames, builtinNames, onDone, onSave }) {
10125
10542
  const theme = useTheme();
@@ -10372,7 +10789,7 @@ ${template}`;
10372
10789
  ")"
10373
10790
  ] }),
10374
10791
  /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(
10375
- SelectInput5,
10792
+ SelectInput6,
10376
10793
  {
10377
10794
  items,
10378
10795
  onSelect: (item) => {
@@ -10401,7 +10818,7 @@ ${template}`;
10401
10818
  ] }),
10402
10819
  /* @__PURE__ */ jsx15(Text14, { color: theme.info.color, children: "Saved to file but not yet enforced at runtime" }),
10403
10820
  /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(
10404
- SelectInput5,
10821
+ SelectInput6,
10405
10822
  {
10406
10823
  items,
10407
10824
  onSelect: (item) => {
@@ -10429,7 +10846,7 @@ ${template}`;
10429
10846
  ")"
10430
10847
  ] }),
10431
10848
  /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(
10432
- SelectInput5,
10849
+ SelectInput6,
10433
10850
  {
10434
10851
  items,
10435
10852
  onSelect: (item) => {
@@ -10475,7 +10892,7 @@ ${template}`;
10475
10892
  ")"
10476
10893
  ] }),
10477
10894
  /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(
10478
- SelectInput5,
10895
+ SelectInput6,
10479
10896
  {
10480
10897
  items,
10481
10898
  onSelect: (item) => {
@@ -10508,7 +10925,7 @@ ${template}`;
10508
10925
  ] }),
10509
10926
  /* @__PURE__ */ jsx15(Box13, { marginTop: 1, flexDirection: "column", children: previewContent().split("\n").map((line, i) => /* @__PURE__ */ jsx15(Text14, { color: theme.info.color, children: line || " " }, i)) }),
10510
10927
  /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(
10511
- SelectInput5,
10928
+ SelectInput6,
10512
10929
  {
10513
10930
  items,
10514
10931
  onSelect: (item) => handleConfirm(item.value)
@@ -10532,7 +10949,7 @@ var init_command_wizard = __esm({
10532
10949
 
10533
10950
  // src/ui/command-picker.tsx
10534
10951
  import { Box as Box14, Text as Text15 } from "ink";
10535
- import SelectInput6 from "ink-select-input";
10952
+ import SelectInput7 from "ink-select-input";
10536
10953
  import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
10537
10954
  function CommandPicker({ commands, title, onPick }) {
10538
10955
  const theme = useTheme();
@@ -10546,7 +10963,7 @@ function CommandPicker({ commands, title, onPick }) {
10546
10963
  /* @__PURE__ */ jsx16(Text15, { color: theme.accent, bold: true, children: title }),
10547
10964
  /* @__PURE__ */ jsx16(Text15, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select." }),
10548
10965
  /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(
10549
- SelectInput6,
10966
+ SelectInput7,
10550
10967
  {
10551
10968
  items,
10552
10969
  onSelect: (item) => {
@@ -10643,7 +11060,7 @@ var init_command_list = __esm({
10643
11060
  // src/ui/lsp-wizard.tsx
10644
11061
  import { useState as useState9 } from "react";
10645
11062
  import { Box as Box16, Text as Text17 } from "ink";
10646
- import SelectInput7 from "ink-select-input";
11063
+ import SelectInput8 from "ink-select-input";
10647
11064
  import { spawn as spawn3 } from "child_process";
10648
11065
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
10649
11066
  function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
@@ -10760,7 +11177,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10760
11177
  /* @__PURE__ */ jsx18(Text17, { color: theme.accent, bold: true, children: "LSP Servers" }),
10761
11178
  /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select." }),
10762
11179
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10763
- SelectInput7,
11180
+ SelectInput8,
10764
11181
  {
10765
11182
  items: mainItems,
10766
11183
  onSelect: (item) => {
@@ -10791,7 +11208,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10791
11208
  /* @__PURE__ */ jsx18(Text17, { color: theme.accent, bold: true, children: "Add LSP Server" }),
10792
11209
  /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, dimColor: false, children: "Select a language server to configure." }),
10793
11210
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10794
- SelectInput7,
11211
+ SelectInput8,
10795
11212
  {
10796
11213
  items,
10797
11214
  onSelect: (item) => {
@@ -10830,7 +11247,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10830
11247
  ] }),
10831
11248
  installState.output && /* @__PURE__ */ jsx18(Box16, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx18(Text17, { color: isSuccess ? theme.accent : theme.error, children: installState.output.slice(-500) }) }),
10832
11249
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10833
- SelectInput7,
11250
+ SelectInput8,
10834
11251
  {
10835
11252
  items,
10836
11253
  onSelect: (item) => {
@@ -10871,7 +11288,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10871
11288
  )
10872
11289
  ] }),
10873
11290
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10874
- SelectInput7,
11291
+ SelectInput8,
10875
11292
  {
10876
11293
  items: [{ label: "\u2190 Back", value: "__back__", key: "__back__" }],
10877
11294
  onSelect: () => setPage("add")
@@ -10900,7 +11317,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10900
11317
  )
10901
11318
  ] }),
10902
11319
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10903
- SelectInput7,
11320
+ SelectInput8,
10904
11321
  {
10905
11322
  items: [{ label: "\u2190 Back", value: "__back__", key: "__back__" }],
10906
11323
  onSelect: () => setPage("custom-name")
@@ -10927,7 +11344,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10927
11344
  /* @__PURE__ */ jsx18(Text17, { color: theme.accent, bold: true, children: "Save LSP Config" }),
10928
11345
  /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, dimColor: false, children: "Where should this server configuration be saved?" }),
10929
11346
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10930
- SelectInput7,
11347
+ SelectInput8,
10931
11348
  {
10932
11349
  items,
10933
11350
  onSelect: (item) => {
@@ -10949,7 +11366,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10949
11366
  /* @__PURE__ */ jsx18(Text17, { color: theme.accent, bold: true, children: "Edit LSP Server" }),
10950
11367
  /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, children: "No servers configured." }),
10951
11368
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10952
- SelectInput7,
11369
+ SelectInput8,
10953
11370
  {
10954
11371
  items: [{ label: "\u2190 Back", value: "__back__", key: "__back__" }],
10955
11372
  onSelect: () => setPage("main")
@@ -10973,7 +11390,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10973
11390
  /* @__PURE__ */ jsx18(Text17, { color: theme.accent, bold: true, children: "Edit LSP Server" }),
10974
11391
  /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, dimColor: false, children: "Select a server to toggle enabled/disabled." }),
10975
11392
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10976
- SelectInput7,
11393
+ SelectInput8,
10977
11394
  {
10978
11395
  items,
10979
11396
  onSelect: (item) => {
@@ -10994,7 +11411,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
10994
11411
  /* @__PURE__ */ jsx18(Text17, { color: theme.accent, bold: true, children: "Delete LSP Server" }),
10995
11412
  /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, children: "No servers configured." }),
10996
11413
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
10997
- SelectInput7,
11414
+ SelectInput8,
10998
11415
  {
10999
11416
  items: [{ label: "\u2190 Back", value: "__back__", key: "__back__" }],
11000
11417
  onSelect: () => setPage("main")
@@ -11014,7 +11431,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
11014
11431
  /* @__PURE__ */ jsx18(Text17, { color: theme.accent, bold: true, children: "Delete LSP Server" }),
11015
11432
  /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, dimColor: false, children: "Select a server to remove from config." }),
11016
11433
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
11017
- SelectInput7,
11434
+ SelectInput8,
11018
11435
  {
11019
11436
  items,
11020
11437
  onSelect: (item) => {
@@ -11038,7 +11455,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
11038
11455
  return /* @__PURE__ */ jsx18(Text17, { color: theme.info.color, children: ` ${k.padEnd(16)} ${status} ${s.command.join(" ")}` }, k);
11039
11456
  }) }),
11040
11457
  /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx18(
11041
- SelectInput7,
11458
+ SelectInput8,
11042
11459
  {
11043
11460
  items: [{ label: "\u2190 Back", value: "__back__", key: "__back__" }],
11044
11461
  onSelect: () => setPage("main")
@@ -11165,7 +11582,7 @@ var init_lsp_wizard = __esm({
11165
11582
 
11166
11583
  // src/ui/theme-picker.tsx
11167
11584
  import { Box as Box17, Text as Text18 } from "ink";
11168
- import SelectInput8 from "ink-select-input";
11585
+ import SelectInput9 from "ink-select-input";
11169
11586
  import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
11170
11587
  function PaletteSwatches({ palette }) {
11171
11588
  const colors = [
@@ -11185,7 +11602,7 @@ function ThemePicker({ themes, onPick, onPreview }) {
11185
11602
  return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", borderStyle: "round", borderColor: current.accent, paddingX: 1, children: [
11186
11603
  /* @__PURE__ */ jsx19(Text18, { color: current.accent, bold: true, children: "Pick a theme" }),
11187
11604
  /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(
11188
- SelectInput8,
11605
+ SelectInput9,
11189
11606
  {
11190
11607
  items,
11191
11608
  onHighlight: (item) => {
@@ -11523,15 +11940,15 @@ var tui_report_exports = {};
11523
11940
  __export(tui_report_exports, {
11524
11941
  getCategoryReportText: () => getCategoryReportText
11525
11942
  });
11526
- import { readFile as readFile15 } from "fs/promises";
11527
- import { join as join18 } from "path";
11528
- import { homedir as homedir13 } from "os";
11943
+ import { readFile as readFile16 } from "fs/promises";
11944
+ import { join as join20 } from "path";
11945
+ import { homedir as homedir14 } from "os";
11529
11946
  function usageDir3() {
11530
- const xdg = process.env.XDG_DATA_HOME || join18(homedir13(), ".local", "share");
11531
- return join18(xdg, "kimiflare");
11947
+ const xdg = process.env.XDG_DATA_HOME || join20(homedir14(), ".local", "share");
11948
+ return join20(xdg, "kimiflare");
11532
11949
  }
11533
11950
  function usagePath3() {
11534
- return join18(usageDir3(), "usage.json");
11951
+ return join20(usageDir3(), "usage.json");
11535
11952
  }
11536
11953
  function today3() {
11537
11954
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -11543,7 +11960,7 @@ function daysAgo2(n) {
11543
11960
  }
11544
11961
  async function loadLog3() {
11545
11962
  try {
11546
- const raw = await readFile15(usagePath3(), "utf8");
11963
+ const raw = await readFile16(usagePath3(), "utf8");
11547
11964
  return JSON.parse(raw);
11548
11965
  } catch {
11549
11966
  return { version: 1, days: [], sessions: [] };
@@ -11611,9 +12028,9 @@ __export(app_exports, {
11611
12028
  });
11612
12029
  import React14, { useState as useState10, useRef as useRef3, useEffect as useEffect5, useCallback } from "react";
11613
12030
  import { Box as Box20, Text as Text21, useApp, useInput as useInput6, render } from "ink";
11614
- import SelectInput9 from "ink-select-input";
12031
+ import SelectInput10 from "ink-select-input";
11615
12032
  import { existsSync as existsSync2, statSync as statSync3 } from "fs";
11616
- import { join as join19 } from "path";
12033
+ import { join as join21 } from "path";
11617
12034
  import { unlink as unlink3 } from "fs/promises";
11618
12035
  import { execSync as execSync2 } from "child_process";
11619
12036
  import { spawn as spawn4 } from "child_process";
@@ -11691,7 +12108,7 @@ function buildFilePickerIgnoreList(cwd) {
11691
12108
  ];
11692
12109
  const gitignorePatterns = [];
11693
12110
  try {
11694
- const gitignorePath = join19(cwd, ".gitignore");
12111
+ const gitignorePath = join21(cwd, ".gitignore");
11695
12112
  const stats = statSync3(gitignorePath);
11696
12113
  if (stats.size > MAX_GITIGNORE_SIZE) {
11697
12114
  return hardcoded;
@@ -11844,7 +12261,8 @@ function App({
11844
12261
  initialCfg,
11845
12262
  initialUpdateResult,
11846
12263
  initialLspScope,
11847
- initialLspProjectPath
12264
+ initialLspProjectPath,
12265
+ initialCloudToken
11848
12266
  }) {
11849
12267
  const { exit } = useApp();
11850
12268
  const [cfg, setCfg] = useState10(initialCfg);
@@ -12108,13 +12526,14 @@ function App({
12108
12526
  }
12109
12527
  });
12110
12528
  if (cfg.memoryEnabled) {
12111
- const dbPath = cfg.memoryDbPath ?? join19(process.cwd(), ".kimiflare", "memory.db");
12529
+ const dbPath = cfg.memoryDbPath ?? join21(process.cwd(), ".kimiflare", "memory.db");
12112
12530
  const manager = new MemoryManager({
12113
12531
  dbPath,
12114
12532
  accountId: cfg.accountId,
12115
12533
  apiToken: cfg.apiToken,
12116
12534
  model: cfg.model,
12117
12535
  plumbingModel: cfg.plumbingModel,
12536
+ extractionModel: cfg.memoryExtractionModel,
12118
12537
  embeddingModel: cfg.memoryEmbeddingModel,
12119
12538
  gateway: gatewayFromConfig(cfg),
12120
12539
  maxAgeDays: cfg.memoryMaxAgeDays ?? RETENTION.memoryMaxAgeDays,
@@ -12756,7 +13175,7 @@ function App({
12756
13175
  return;
12757
13176
  }
12758
13177
  const cwd = process.cwd();
12759
- const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find((n) => existsSync2(join19(cwd, n)));
13178
+ const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find((n) => existsSync2(join21(cwd, n)));
12760
13179
  const isRefresh = existingName !== void 0;
12761
13180
  const promptParts = [
12762
13181
  isRefresh ? `Regenerate ${existingName} at the repository root to refresh project context. If the file already exists, read it first and preserve anything still accurate, updating only what has changed.` : "Generate a KIMI.md at the repository root so future agents have project context.",
@@ -12807,15 +13226,15 @@ function App({
12807
13226
  sessionId: ensureSessionId(),
12808
13227
  memoryManager: memoryManagerRef.current,
12809
13228
  codeMode: effectiveCodeMode,
12810
- maxInputTokens: effectiveCodeMode ? 2e5 : void 0,
12811
- continueOnLimit: effectiveCodeMode ? true : void 0,
13229
+ cloudMode: cfg.cloudMode,
13230
+ cloudToken: initialCloudToken,
12812
13231
  onIterationEnd,
12813
13232
  onFileChange: (path, content) => {
12814
13233
  if (content) {
12815
13234
  lspManagerRef.current.notifyChange(path, content);
12816
13235
  } else {
12817
13236
  void import("fs/promises").then(
12818
- ({ readFile: readFile16 }) => readFile16(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
13237
+ ({ readFile: readFile17 }) => readFile17(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
12819
13238
  })
12820
13239
  );
12821
13240
  }
@@ -12909,7 +13328,7 @@ function App({
12909
13328
  })
12910
13329
  }
12911
13330
  });
12912
- if (existsSync2(join19(cwd, "KIMI.md"))) {
13331
+ if (existsSync2(join21(cwd, "KIMI.md"))) {
12913
13332
  if (cacheStableRef.current) {
12914
13333
  messagesRef.current[1] = {
12915
13334
  role: "system",
@@ -13138,6 +13557,10 @@ function App({
13138
13557
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "no config loaded" }]);
13139
13558
  return true;
13140
13559
  }
13560
+ if (cfg.cloudMode) {
13561
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "AI Gateway is managed by Kimiflare Cloud" }]);
13562
+ return true;
13563
+ }
13141
13564
  const sub = rest[0]?.toLowerCase() ?? "";
13142
13565
  const subArg = rest.slice(1).join(" ").trim();
13143
13566
  if (!sub || sub === "status") {
@@ -14008,8 +14431,8 @@ ${lines.join("\n")}` }]);
14008
14431
  memoryManager: memoryManagerRef.current,
14009
14432
  keepLastImageTurns: cfg.imageHistoryTurns ?? 2,
14010
14433
  codeMode: effectiveCodeMode,
14011
- maxInputTokens: effectiveCodeMode ? 2e5 : void 0,
14012
- continueOnLimit: effectiveCodeMode ? true : void 0,
14434
+ cloudMode: cfg.cloudMode,
14435
+ cloudToken: initialCloudToken,
14013
14436
  onIterationEnd,
14014
14437
  intentClassification: classification,
14015
14438
  onFileChange: (path, content2) => {
@@ -14017,7 +14440,7 @@ ${lines.join("\n")}` }]);
14017
14440
  lspManagerRef.current.notifyChange(path, content2);
14018
14441
  } else {
14019
14442
  void import("fs/promises").then(
14020
- ({ readFile: readFile16 }) => readFile16(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
14443
+ ({ readFile: readFile17 }) => readFile17(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
14021
14444
  })
14022
14445
  );
14023
14446
  }
@@ -14198,12 +14621,28 @@ ${lines.join("\n")}` }]);
14198
14621
  return /* @__PURE__ */ jsx22(ThemeProvider, { theme, children: /* @__PURE__ */ jsx22(
14199
14622
  Onboarding,
14200
14623
  {
14201
- onDone: (newCfg) => {
14624
+ onDone: async (newCfg) => {
14202
14625
  setCfg(newCfg);
14203
- setEvents((e) => [
14204
- ...e,
14205
- { kind: "info", key: mkKey(), text: "configuration saved \u2014 welcome to kimiflare!" }
14206
- ]);
14626
+ if (newCfg.cloudMode) {
14627
+ const { loadCloudCredentials: loadCloudCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
14628
+ const creds = await loadCloudCredentials2();
14629
+ if (creds) {
14630
+ setEvents((e) => [
14631
+ ...e,
14632
+ { kind: "info", key: mkKey(), text: "configuration saved \u2014 welcome to kimiflare! (cloud mode)" }
14633
+ ]);
14634
+ } else {
14635
+ setEvents((e) => [
14636
+ ...e,
14637
+ { kind: "info", key: mkKey(), text: "cloud mode configured \u2014 run `kimiflare auth cloud` to sign in" }
14638
+ ]);
14639
+ }
14640
+ } else {
14641
+ setEvents((e) => [
14642
+ ...e,
14643
+ { kind: "info", key: mkKey(), text: "configuration saved \u2014 welcome to kimiflare!" }
14644
+ ]);
14645
+ }
14207
14646
  }
14208
14647
  }
14209
14648
  ) });
@@ -14249,6 +14688,7 @@ ${lines.join("\n")}` }]);
14249
14688
  {
14250
14689
  customCommands: customCommandsRef.current.filter((c) => !BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase())).map((c) => ({ name: c.name, description: c.description })),
14251
14690
  costAttributionEnabled: cfg?.costAttribution,
14691
+ cloudMode: cfg?.cloudMode,
14252
14692
  onDone: () => setShowHelpMenu(false),
14253
14693
  onCommand: handleHelpCommand
14254
14694
  }
@@ -14260,7 +14700,7 @@ ${lines.join("\n")}` }]);
14260
14700
  {
14261
14701
  servers: cfg?.lspServers ?? {},
14262
14702
  currentScope: lspScope,
14263
- hasProjectDir: existsSync2(join19(process.cwd(), ".kimiflare")),
14703
+ hasProjectDir: existsSync2(join21(process.cwd(), ".kimiflare")),
14264
14704
  onDone: () => setShowLspWizard(false),
14265
14705
  onSave: (servers, enabled, scope) => {
14266
14706
  setCfg((c) => c ? { ...c, lspEnabled: enabled, lspServers: servers } : c);
@@ -14331,7 +14771,7 @@ ${lines.join("\n")}` }]);
14331
14771
  ] }),
14332
14772
  /* @__PURE__ */ jsx22(Text21, { color: theme.info.color, children: commandToDelete.filepath }),
14333
14773
  /* @__PURE__ */ jsx22(Box20, { marginTop: 1, children: /* @__PURE__ */ jsx22(
14334
- SelectInput9,
14774
+ SelectInput10,
14335
14775
  {
14336
14776
  items: [
14337
14777
  { label: "Yes, delete", value: "yes", key: "yes" },
@@ -14401,7 +14841,8 @@ ${lines.join("\n")}` }]);
14401
14841
  hasUpdate,
14402
14842
  latestVersion,
14403
14843
  gatewayMeta,
14404
- codeMode
14844
+ codeMode,
14845
+ cloudMode: cfg.cloudMode
14405
14846
  }
14406
14847
  ),
14407
14848
  activePicker?.kind === "file" && /* @__PURE__ */ jsx22(
@@ -14477,7 +14918,7 @@ ${lines.join("\n")}` }]);
14477
14918
  ] })
14478
14919
  ] }) });
14479
14920
  }
14480
- async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath = null) {
14921
+ async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath = null, cloudToken) {
14481
14922
  const instance = render(
14482
14923
  /* @__PURE__ */ jsx22(
14483
14924
  App,
@@ -14485,7 +14926,8 @@ async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath
14485
14926
  initialCfg: cfg,
14486
14927
  initialUpdateResult: updateResult,
14487
14928
  initialLspScope: lspScope,
14488
- initialLspProjectPath: lspProjectPath
14929
+ initialLspProjectPath: lspProjectPath,
14930
+ initialCloudToken: cloudToken
14489
14931
  }
14490
14932
  ),
14491
14933
  {
@@ -14676,7 +15118,7 @@ function createRemoteCommand() {
14676
15118
 
14677
15119
  // src/index.tsx
14678
15120
  var program = new Command2();
14679
- program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)").option("--continue-on-limit", "reset tool-call counter and continue when the 50-call limit is hit (print mode only)").option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10));
15121
+ program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--cloud", "use Kimiflare Cloud (api.kimiflare.com) instead of direct Workers AI").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)").option("--continue-on-limit", "reset tool-call counter and continue when the 50-call limit is hit (print mode only)").option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10));
14680
15122
  program.command("cost").description("Show cost attribution by task type (requires costAttribution enabled)").option("-w, --week", "last 7 days (default)").option("-m, --month", "last 30 days").option("-d, --day", "today only").option("-s, --session <id>", "single session detail").option("-c, --category <name>", "filter by category").option("--json", "machine-readable output").option("--reclassify", "re-run classification on all sessions").option("--local-only", "skip Cloudflare reconciliation").action(async (cmdOpts) => {
14681
15123
  const cfg = await loadConfig();
14682
15124
  const enabled = cfg?.costAttribution ?? false;
@@ -14689,6 +15131,25 @@ program.command("cost").description("Show cost attribution by task type (require
14689
15131
  const { runCostCommand: runCostCommand2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
14690
15132
  await runCostCommand2({ ...cmdOpts, config: cfg });
14691
15133
  });
15134
+ program.command("usage").description("Show Kimiflare Cloud token usage (requires cloud authentication)").action(async () => {
15135
+ const { loadCloudCredentials: loadCloudCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
15136
+ const creds = await loadCloudCredentials2();
15137
+ if (!creds) {
15138
+ console.error("Not authenticated with Kimiflare Cloud. Run: kimiflare auth cloud");
15139
+ process.exit(1);
15140
+ }
15141
+ const res = await fetch("https://api.kimiflare.com/v1/usage", {
15142
+ headers: { Authorization: `Bearer ${creds.accessToken}` }
15143
+ });
15144
+ if (!res.ok) {
15145
+ console.error(`Failed to fetch usage: ${res.status} ${res.statusText}`);
15146
+ process.exit(1);
15147
+ }
15148
+ const usage = await res.json();
15149
+ console.log(`Token budget: ${usage.remaining.toLocaleString()} / ${usage.input_token_limit.toLocaleString()} remaining`);
15150
+ console.log(`Used: ${usage.input_tokens_used.toLocaleString()}`);
15151
+ console.log(`Grant expires: ${usage.expires_at}`);
15152
+ });
14692
15153
  program.addCommand(createRemoteCommand());
14693
15154
  program.command("auth").description("Authenticate with external services").addCommand(
14694
15155
  new Command2("github").description("Authenticate with GitHub via OAuth device flow").action(async () => {
@@ -14705,6 +15166,37 @@ Open: ${step.url}`);
14705
15166
  if (step.error) process.exit(1);
14706
15167
  }
14707
15168
  })
15169
+ ).addCommand(
15170
+ new Command2("cloud").description("Authenticate with Kimiflare Cloud").action(async () => {
15171
+ const { authenticateDevice: authenticateDevice2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
15172
+ try {
15173
+ const creds = await authenticateDevice2(({ url, userCode, polling }) => {
15174
+ if (!polling) {
15175
+ console.log(`
15176
+ Kimiflare Cloud Authentication`);
15177
+ console.log(`
15178
+ 1. Open this URL in your browser:`);
15179
+ console.log(` ${url}`);
15180
+ console.log(`
15181
+ 2. Sign in with GitHub
15182
+ `);
15183
+ }
15184
+ });
15185
+ console.log(`Authenticated! Token expires at ${new Date(creds.expiresAt * 1e3).toISOString()}`);
15186
+ const usageRes = await fetch("https://api.kimiflare.com/v1/usage", {
15187
+ headers: { Authorization: `Bearer ${creds.accessToken}` }
15188
+ });
15189
+ if (usageRes.ok) {
15190
+ const usage = await usageRes.json();
15191
+ console.log(`
15192
+ Token budget: ${usage.remaining.toLocaleString()} / ${usage.input_token_limit.toLocaleString()} remaining`);
15193
+ console.log(`Grant expires: ${usage.expires_at}`);
15194
+ }
15195
+ } catch (err) {
15196
+ console.error("Authentication failed:", err instanceof Error ? err.message : String(err));
15197
+ process.exit(1);
15198
+ }
15199
+ })
14708
15200
  );
14709
15201
  program.action(async () => {
14710
15202
  await main();
@@ -14727,10 +15219,25 @@ async function main() {
14727
15219
  lspScope = resolved.scope;
14728
15220
  lspProjectPath = resolved.projectPath;
14729
15221
  }
15222
+ const cloudMode = opts.cloud ?? cfg?.cloudMode ?? false;
15223
+ let cloudToken;
15224
+ if (cloudMode) {
15225
+ const { loadCloudCredentials: loadCloudCredentials2, authenticateDevice: authenticateDevice2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
15226
+ let cloudCreds = await loadCloudCredentials2();
15227
+ if (!cloudCreds) {
15228
+ console.error("kimiflare: cloud mode requires authentication.\nRun: kimiflare auth cloud\n");
15229
+ process.exit(2);
15230
+ }
15231
+ cloudToken = cloudCreds.accessToken;
15232
+ cfg = {
15233
+ ...cfg ?? { accountId: "", apiToken: "", model: DEFAULT_MODEL },
15234
+ cloudMode: true
15235
+ };
15236
+ }
14730
15237
  if (opts.print !== void 0) {
14731
15238
  if (!cfg) {
14732
15239
  console.error(
14733
- 'kimiflare: missing credentials.\nSet CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN, or write them to\n ~/.config/kimiflare/config.json (chmod 600)\n { "accountId": "...", "apiToken": "...", "model": "@cf/moonshotai/kimi-k2.6" }'
15240
+ 'kimiflare: missing credentials.\nSet CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN, or write them to\n ~/.config/kimiflare/config.json (chmod 600)\n { "accountId": "...", "apiToken": "...", "model": "@cf/moonshotai/kimi-k2.6" }\nOr use cloud mode: kimiflare --cloud -p "..."'
14734
15241
  );
14735
15242
  process.exit(2);
14736
15243
  }
@@ -14742,6 +15249,7 @@ async function main() {
14742
15249
  allowAll: !!opts.dangerouslyAllowAll,
14743
15250
  showReasoning: !!opts.reasoning,
14744
15251
  codeMode: cfg.codeMode,
15252
+ cloudToken,
14745
15253
  continueOnLimit: !!opts.continueOnLimit,
14746
15254
  maxInputTokens: opts.maxInputTokens,
14747
15255
  updateResult
@@ -14757,9 +15265,9 @@ async function main() {
14757
15265
  const { renderApp: renderApp2 } = await Promise.resolve().then(() => (init_app(), app_exports));
14758
15266
  if (cfg) {
14759
15267
  const model = opts.model ?? cfg.model ?? DEFAULT_MODEL;
14760
- await renderApp2({ ...cfg, model }, updateResult, lspScope, lspProjectPath);
15268
+ await renderApp2({ ...cfg, model }, updateResult, lspScope, lspProjectPath, cloudToken);
14761
15269
  } else {
14762
- await renderApp2(null, updateResult, lspScope, lspProjectPath);
15270
+ await renderApp2(null, updateResult, lspScope, lspProjectPath, cloudToken);
14763
15271
  }
14764
15272
  }
14765
15273
  function gatewayFromPrintOpts(opts2) {
@@ -14773,6 +15281,10 @@ function gatewayFromPrintOpts(opts2) {
14773
15281
  };
14774
15282
  }
14775
15283
  async function runPrintMode(opts2) {
15284
+ if (opts2.cloudMode) {
15285
+ process.stderr.write(`[cloud mode: api.kimiflare.com]
15286
+ `);
15287
+ }
14776
15288
  if (opts2.updateResult.hasUpdate) {
14777
15289
  process.stderr.write(
14778
15290
  `\x1B[33mkimiflare update available: ${opts2.updateResult.localVersion} \u2192 ${opts2.updateResult.latestVersion}\x1B[0m
@@ -14805,6 +15317,8 @@ async function runPrintMode(opts2) {
14805
15317
  codeMode: opts2.codeMode,
14806
15318
  continueOnLimit: opts2.continueOnLimit,
14807
15319
  maxInputTokens: opts2.maxInputTokens,
15320
+ cloudMode: opts2.cloudMode,
15321
+ cloudToken: opts2.cloudToken,
14808
15322
  coauthor: opts2.coauthor !== false ? { name: opts2.coauthorName || "kimiflare", email: opts2.coauthorEmail || "kimiflare@proton.me" } : void 0,
14809
15323
  callbacks: {
14810
15324
  onReasoningDelta: opts2.showReasoning ? (delta) => {