autopilot-ai 1.7.0 → 1.8.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
@@ -37,6 +37,411 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
37
37
  mod
38
38
  ));
39
39
 
40
+ // src/util/log-sink.ts
41
+ var log_sink_exports = {};
42
+ __export(log_sink_exports, {
43
+ flushAndCloseForTesting: () => flushAndCloseForTesting,
44
+ getLogSessionId: () => getLogSessionId,
45
+ getLogTurnId: () => getLogTurnId,
46
+ isLogSinkEnabled: () => isLogSinkEnabled,
47
+ logDir: () => logDir,
48
+ logPathFor: () => logPathFor,
49
+ pruneOldLogs: () => pruneOldLogs,
50
+ setLogDirForTesting: () => setLogDirForTesting,
51
+ setLogSessionId: () => setLogSessionId,
52
+ setLogSinkEnabled: () => setLogSinkEnabled,
53
+ setLogTurnId: () => setLogTurnId,
54
+ writeLogLine: () => writeLogLine
55
+ });
56
+ import { homedir } from "os";
57
+ import { join } from "path";
58
+ import { mkdirSync, createWriteStream, readdirSync, statSync, unlinkSync } from "fs";
59
+ function defaultLogDir() {
60
+ const xdg = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
61
+ return join(xdg, "kimiflare", "logs");
62
+ }
63
+ function isInNodeTestContext() {
64
+ if (process.env.NODE_TEST_CONTEXT) return true;
65
+ if (process.env.NODE_ENV === "test") return true;
66
+ if (process.argv.includes("--test")) return true;
67
+ return false;
68
+ }
69
+ function logDir() {
70
+ return overrideDir ?? defaultLogDir();
71
+ }
72
+ function logPathFor(date = /* @__PURE__ */ new Date()) {
73
+ return join(logDir(), `${date.toISOString().slice(0, 10)}.jsonl`);
74
+ }
75
+ function setLogDirForTesting(dir) {
76
+ if (currentStream) {
77
+ currentStream.end();
78
+ currentStream = null;
79
+ currentDate = null;
80
+ }
81
+ overrideDir = dir;
82
+ }
83
+ function setLogSinkEnabled(enabled2) {
84
+ if (!enabled2 && currentStream) {
85
+ currentStream.end();
86
+ currentStream = null;
87
+ currentDate = null;
88
+ }
89
+ sinkEnabled = enabled2;
90
+ }
91
+ async function flushAndCloseForTesting() {
92
+ if (!currentStream) return;
93
+ const s = currentStream;
94
+ currentStream = null;
95
+ currentDate = null;
96
+ await new Promise((resolve14) => {
97
+ s.end(() => resolve14());
98
+ });
99
+ }
100
+ function isLogSinkEnabled() {
101
+ return sinkEnabled;
102
+ }
103
+ function ensureStream(now2) {
104
+ if (!sinkEnabled) return null;
105
+ const dateKey = now2.toISOString().slice(0, 10);
106
+ if (currentStream && currentDate === dateKey) return currentStream;
107
+ if (currentStream) {
108
+ currentStream.end();
109
+ currentStream = null;
110
+ }
111
+ try {
112
+ mkdirSync(logDir(), { recursive: true });
113
+ currentStream = createWriteStream(logPathFor(now2), { flags: "a" });
114
+ currentDate = dateKey;
115
+ currentStream.on("error", () => {
116
+ currentStream = null;
117
+ currentDate = null;
118
+ });
119
+ return currentStream;
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+ function writeLogLine(entry, now2 = /* @__PURE__ */ new Date()) {
125
+ const stream = ensureStream(now2);
126
+ if (!stream) return;
127
+ try {
128
+ stream.write(JSON.stringify(entry) + "\n");
129
+ } catch {
130
+ }
131
+ }
132
+ function pruneOldLogs(retentionDays = RETENTION_DAYS) {
133
+ let removed = 0;
134
+ let entries;
135
+ try {
136
+ entries = readdirSync(logDir());
137
+ } catch {
138
+ return 0;
139
+ }
140
+ const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
141
+ for (const name of entries) {
142
+ if (!name.endsWith(".jsonl")) continue;
143
+ const full = join(logDir(), name);
144
+ try {
145
+ const st = statSync(full);
146
+ if (st.mtimeMs < cutoff) {
147
+ unlinkSync(full);
148
+ removed += 1;
149
+ }
150
+ } catch {
151
+ }
152
+ }
153
+ return removed;
154
+ }
155
+ function setLogSessionId(id) {
156
+ currentSessionId = id;
157
+ }
158
+ function setLogTurnId(id) {
159
+ currentTurnId = id;
160
+ }
161
+ function getLogSessionId() {
162
+ return currentSessionId;
163
+ }
164
+ function getLogTurnId() {
165
+ return currentTurnId;
166
+ }
167
+ var RETENTION_DAYS, overrideDir, currentStream, currentDate, sinkEnabled, currentSessionId, currentTurnId;
168
+ var init_log_sink = __esm({
169
+ "src/util/log-sink.ts"() {
170
+ "use strict";
171
+ RETENTION_DAYS = 7;
172
+ overrideDir = null;
173
+ currentStream = null;
174
+ currentDate = null;
175
+ sinkEnabled = process.env.KIMIFLARE_LOG_SINK !== "off" && !isInNodeTestContext();
176
+ currentSessionId = null;
177
+ currentTurnId = null;
178
+ }
179
+ });
180
+
181
+ // src/util/otel-sink.ts
182
+ var otel_sink_exports = {};
183
+ __export(otel_sink_exports, {
184
+ buildOtlpPayload: () => buildOtlpPayload,
185
+ enqueueOtelLog: () => enqueueOtelLog,
186
+ flushOtelSink: () => flushOtelSink,
187
+ getOtelDropCount: () => getOtelDropCount,
188
+ getOtelQueueSize: () => getOtelQueueSize,
189
+ initOtelSink: () => initOtelSink,
190
+ installOtelExitHook: () => installOtelExitHook,
191
+ isOtelEnabled: () => isOtelEnabled,
192
+ resetOtelSinkForTesting: () => resetOtelSinkForTesting,
193
+ setFetchForTesting: () => setFetchForTesting
194
+ });
195
+ function setFetchForTesting(f) {
196
+ fetchImpl = f ?? ((url, init) => fetch(url, init));
197
+ }
198
+ function parseHeaders(raw) {
199
+ if (!raw) return {};
200
+ const out = {};
201
+ for (const pair of raw.split(",")) {
202
+ const trimmed = pair.trim();
203
+ if (!trimmed) continue;
204
+ const eq = trimmed.indexOf("=");
205
+ if (eq <= 0) continue;
206
+ const key = trimmed.slice(0, eq).trim();
207
+ const value = trimmed.slice(eq + 1).trim();
208
+ if (key) out[key] = value;
209
+ }
210
+ return out;
211
+ }
212
+ function normalizeEndpoint(raw) {
213
+ const stripped = raw.replace(/\/+$/, "");
214
+ if (/\/v1\/(logs|traces|metrics)$/.test(stripped)) return stripped;
215
+ return `${stripped}/v1/logs`;
216
+ }
217
+ function initOtelSink(opts2 = {}) {
218
+ const endpoint = opts2.endpoint ?? process.env.KIMIFLARE_OTEL_ENDPOINT;
219
+ if (!endpoint) {
220
+ config = null;
221
+ return false;
222
+ }
223
+ config = {
224
+ endpoint: normalizeEndpoint(endpoint),
225
+ headers: parseHeaders(opts2.headers ?? process.env.KIMIFLARE_OTEL_HEADERS),
226
+ serviceName: opts2.serviceName ?? "kimiflare",
227
+ serviceVersion: opts2.serviceVersion ?? process.env.npm_package_version ?? "0.0.0"
228
+ };
229
+ return true;
230
+ }
231
+ function isOtelEnabled() {
232
+ return config !== null;
233
+ }
234
+ function resetOtelSinkForTesting() {
235
+ if (timer) {
236
+ clearTimeout(timer);
237
+ timer = null;
238
+ }
239
+ queue = [];
240
+ config = null;
241
+ dropCount = 0;
242
+ inFlightFlush = null;
243
+ }
244
+ function getOtelDropCount() {
245
+ return dropCount;
246
+ }
247
+ function getOtelQueueSize() {
248
+ return queue.length;
249
+ }
250
+ function enqueueOtelLog(entry) {
251
+ if (!config) return;
252
+ queue.push(entry);
253
+ if (queue.length >= BATCH_MAX) {
254
+ void flushOtelSink();
255
+ return;
256
+ }
257
+ scheduleFlush();
258
+ }
259
+ function scheduleFlush() {
260
+ if (timer) return;
261
+ timer = setTimeout(() => {
262
+ timer = null;
263
+ void flushOtelSink();
264
+ }, FLUSH_INTERVAL_MS);
265
+ if (typeof timer === "object" && timer && "unref" in timer) {
266
+ timer.unref();
267
+ }
268
+ }
269
+ async function flushOtelSink() {
270
+ if (!config) {
271
+ queue = [];
272
+ return;
273
+ }
274
+ if (queue.length === 0) return;
275
+ if (inFlightFlush) return inFlightFlush;
276
+ const batch = queue;
277
+ queue = [];
278
+ const payload = buildOtlpPayload(batch, config);
279
+ inFlightFlush = (async () => {
280
+ try {
281
+ const res = await fetchImpl(config.endpoint, {
282
+ method: "POST",
283
+ headers: {
284
+ "Content-Type": "application/json",
285
+ ...config.headers
286
+ },
287
+ body: JSON.stringify(payload)
288
+ });
289
+ if (!res.ok) {
290
+ dropCount += batch.length;
291
+ }
292
+ } catch {
293
+ dropCount += batch.length;
294
+ } finally {
295
+ inFlightFlush = null;
296
+ }
297
+ })();
298
+ return inFlightFlush;
299
+ }
300
+ function attr(key, value) {
301
+ if (value === null || value === void 0) return null;
302
+ if (typeof value === "string") return { key, value: { stringValue: value } };
303
+ if (typeof value === "boolean") return { key, value: { boolValue: value } };
304
+ if (typeof value === "number" && Number.isFinite(value)) {
305
+ return { key, value: { intValue: String(Math.trunc(value)) } };
306
+ }
307
+ return { key, value: { stringValue: JSON.stringify(value) } };
308
+ }
309
+ function flattenDataAttrs(data) {
310
+ if (!data) return [];
311
+ const out = [];
312
+ for (const [key, value] of Object.entries(data)) {
313
+ if (key === "request_id" || key === "requestId") continue;
314
+ const a = attr(key, value);
315
+ if (a) out.push(a);
316
+ }
317
+ return out;
318
+ }
319
+ function buildOtlpPayload(entries, cfg) {
320
+ const resourceAttrs = [
321
+ attr("service.name", cfg.serviceName),
322
+ attr("service.version", cfg.serviceVersion),
323
+ ...process.env.HOSTNAME ? [attr("host.name", process.env.HOSTNAME)] : []
324
+ ];
325
+ const logRecords = entries.map((e) => {
326
+ const attrs = [];
327
+ if (e.session_id) attrs.push(attr("session_id", e.session_id));
328
+ if (e.turn_id) attrs.push(attr("turn_id", e.turn_id));
329
+ if (e.request_id) attrs.push(attr("request_id", e.request_id));
330
+ attrs.push(attr("event", e.event));
331
+ attrs.push(...flattenDataAttrs(e.data));
332
+ const timeNs = String(BigInt(new Date(e.ts).getTime()) * 1000000n);
333
+ return {
334
+ timeUnixNano: timeNs,
335
+ observedTimeUnixNano: timeNs,
336
+ severityNumber: SEVERITY_NUMBER[e.level],
337
+ severityText: SEVERITY_TEXT[e.level],
338
+ body: { stringValue: e.event },
339
+ attributes: attrs
340
+ };
341
+ });
342
+ return {
343
+ resourceLogs: [
344
+ {
345
+ resource: { attributes: resourceAttrs },
346
+ scopeLogs: [
347
+ {
348
+ scope: { name: "kimiflare", version: cfg.serviceVersion },
349
+ logRecords
350
+ }
351
+ ]
352
+ }
353
+ ]
354
+ };
355
+ }
356
+ function installOtelExitHook() {
357
+ if (exitHookInstalled) return;
358
+ exitHookInstalled = true;
359
+ const drain = () => {
360
+ if (queue.length === 0 && !inFlightFlush) return;
361
+ void flushOtelSink();
362
+ };
363
+ process.on("beforeExit", drain);
364
+ }
365
+ var BATCH_MAX, FLUSH_INTERVAL_MS, SEVERITY_NUMBER, SEVERITY_TEXT, config, queue, timer, dropCount, inFlightFlush, fetchImpl, exitHookInstalled;
366
+ var init_otel_sink = __esm({
367
+ "src/util/otel-sink.ts"() {
368
+ "use strict";
369
+ BATCH_MAX = 100;
370
+ FLUSH_INTERVAL_MS = 5e3;
371
+ SEVERITY_NUMBER = {
372
+ debug: 5,
373
+ info: 9,
374
+ warn: 13,
375
+ error: 17,
376
+ off: 0
377
+ // never actually emitted, but maps cleanly
378
+ };
379
+ SEVERITY_TEXT = {
380
+ debug: "DEBUG",
381
+ info: "INFO",
382
+ warn: "WARN",
383
+ error: "ERROR",
384
+ off: "OFF"
385
+ };
386
+ config = null;
387
+ queue = [];
388
+ timer = null;
389
+ dropCount = 0;
390
+ inFlightFlush = null;
391
+ fetchImpl = (url, init) => fetch(url, init);
392
+ exitHookInstalled = false;
393
+ }
394
+ });
395
+
396
+ // src/util/logger.ts
397
+ function log(level, event, data) {
398
+ const sessionId = getLogSessionId();
399
+ const turnId = getLogTurnId();
400
+ const requestId = typeof data?.request_id === "string" ? data.request_id : typeof data?.requestId === "string" ? data.requestId : void 0;
401
+ const entry = {
402
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
403
+ level,
404
+ event,
405
+ ...sessionId ? { session_id: sessionId } : {},
406
+ ...turnId ? { turn_id: turnId } : {},
407
+ ...requestId ? { request_id: requestId } : {},
408
+ data
409
+ };
410
+ recentLogs.push(entry);
411
+ if (recentLogs.length > RECENT_LOGS_MAX) {
412
+ recentLogs.shift();
413
+ }
414
+ if (LEVEL_ORDER[level] >= LEVEL_ORDER[globalMinLevel]) {
415
+ console.error(JSON.stringify(entry));
416
+ }
417
+ writeLogLine(entry);
418
+ enqueueOtelLog(entry);
419
+ }
420
+ var globalMinLevel, LEVEL_ORDER, RECENT_LOGS_MAX, recentLogs, logger;
421
+ var init_logger = __esm({
422
+ "src/util/logger.ts"() {
423
+ "use strict";
424
+ init_log_sink();
425
+ init_otel_sink();
426
+ globalMinLevel = process.env.KIMIFLARE_LOG_LEVEL ?? "off";
427
+ LEVEL_ORDER = {
428
+ debug: 0,
429
+ info: 1,
430
+ warn: 2,
431
+ error: 3,
432
+ off: 4
433
+ };
434
+ RECENT_LOGS_MAX = 100;
435
+ recentLogs = [];
436
+ logger = {
437
+ debug: (event, data) => log("debug", event, data),
438
+ info: (event, data) => log("info", event, data),
439
+ warn: (event, data) => log("warn", event, data),
440
+ error: (event, data) => log("error", event, data)
441
+ };
442
+ }
443
+ });
444
+
40
445
  // src/models/registry.ts
41
446
  function registerOpenRouterModels(entries) {
42
447
  openRouterIndex = new Map(
@@ -195,11 +600,11 @@ __export(config_exports, {
195
600
  saveConfig: () => saveConfig
196
601
  });
197
602
  import { readFile, mkdir, writeFile, chmod } from "fs/promises";
198
- import { homedir } from "os";
199
- import { join } from "path";
603
+ import { homedir as homedir2 } from "os";
604
+ import { join as join2 } from "path";
200
605
  function configPath() {
201
- const xdg = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
202
- return join(xdg, "kimiflare", "config.json");
606
+ const xdg = process.env.XDG_CONFIG_HOME || join2(homedir2(), ".config");
607
+ return join2(xdg, "kimiflare", "config.json");
203
608
  }
204
609
  function readReasoningEffortEnv() {
205
610
  const raw = process.env.KIMI_REASONING_EFFORT?.toLowerCase();
@@ -270,7 +675,7 @@ async function loadConfig() {
270
675
  model: m(envModel ?? persisted.model) ?? (requesty ? REQUESTY_DEFAULT_MODEL : DEFAULT_MODEL),
271
676
  openrouterProvider: persisted.openrouterProvider,
272
677
  reasoningEffort: envEffort ?? persisted.reasoningEffort,
273
- subagentPolicy: envSubagentPolicy ?? (persisted.subagentPolicy === "off" || persisted.subagentPolicy === "suggest" || persisted.subagentPolicy === "auto" ? persisted.subagentPolicy : "suggest"),
678
+ subagentPolicy: envSubagentPolicy ?? (persisted.subagentPolicy === "suggest" && persisted.subagentPolicyExplicit !== true ? "auto" : persisted.subagentPolicy === "off" || persisted.subagentPolicy === "suggest" || persisted.subagentPolicy === "auto" ? persisted.subagentPolicy : "auto"),
274
679
  modesEnabled: persisted.modesEnabled,
275
680
  coauthor: envCoauthor?.enabled ?? persisted.coauthor ?? true,
276
681
  coauthorName: envCoauthor?.name ?? persisted.coauthorName,
@@ -380,7 +785,7 @@ async function patchPersistedConfig(patch) {
380
785
  } catch {
381
786
  }
382
787
  const merged = { ...existing, ...patch };
383
- await mkdir(join(p, ".."), { recursive: true });
788
+ await mkdir(join2(p, ".."), { recursive: true });
384
789
  await writeFile(p, JSON.stringify(merged, null, 2), "utf8");
385
790
  await chmod(p, 384);
386
791
  return p;
@@ -404,7 +809,7 @@ function resolveWorkerBudgetUsd(cfg) {
404
809
  }
405
810
  async function saveConfig(cfg) {
406
811
  const p = configPath();
407
- await mkdir(join(p, ".."), { recursive: true });
812
+ await mkdir(join2(p, ".."), { recursive: true });
408
813
  await writeFile(p, JSON.stringify(cfg, null, 2), "utf8");
409
814
  await chmod(p, 384);
410
815
  return p;
@@ -446,15 +851,15 @@ var init_config = __esm({
446
851
 
447
852
  // src/util/lsp-config.ts
448
853
  import { readFile as readFile2, mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
449
- import { join as join2, dirname } from "path";
854
+ import { join as join3, dirname } from "path";
450
855
  import { existsSync } from "fs";
451
856
  function isGitRoot(dir) {
452
- return existsSync(join2(dir, ".git"));
857
+ return existsSync(join3(dir, ".git"));
453
858
  }
454
859
  function findProjectLspConfigPath(cwd) {
455
860
  let current = cwd;
456
861
  while (true) {
457
- const candidate = join2(current, PROJECT_CONFIG_DIR, PROJECT_LSP_FILE);
862
+ const candidate = join3(current, PROJECT_CONFIG_DIR, PROJECT_LSP_FILE);
458
863
  if (existsSync(candidate)) {
459
864
  return candidate;
460
865
  }
@@ -483,7 +888,7 @@ async function loadProjectLspConfig(cwd) {
483
888
  }
484
889
  }
485
890
  async function ensureGitignore(cwd) {
486
- const gitignorePath = join2(cwd, ".gitignore");
891
+ const gitignorePath = join3(cwd, ".gitignore");
487
892
  const entry = `${PROJECT_CONFIG_DIR}/${PROJECT_LSP_FILE}`;
488
893
  try {
489
894
  const content = await readFile2(gitignorePath, "utf8");
@@ -493,9 +898,9 @@ async function ensureGitignore(cwd) {
493
898
  }
494
899
  }
495
900
  async function saveProjectLspConfig(cwd, cfg) {
496
- const dir = join2(cwd, PROJECT_CONFIG_DIR);
901
+ const dir = join3(cwd, PROJECT_CONFIG_DIR);
497
902
  await mkdir2(dir, { recursive: true });
498
- const path = join2(dir, PROJECT_LSP_FILE);
903
+ const path = join3(dir, PROJECT_LSP_FILE);
499
904
  await writeFile2(path, JSON.stringify(cfg, null, 2), "utf8");
500
905
  await ensureGitignore(cwd);
501
906
  return path;
@@ -532,11 +937,11 @@ var init_lsp_config = __esm({
532
937
  // src/util/version.ts
533
938
  import { readFileSync } from "fs";
534
939
  import { fileURLToPath } from "url";
535
- import { dirname as dirname2, join as join3 } from "path";
940
+ import { dirname as dirname2, join as join4 } from "path";
536
941
  function getAppVersion() {
537
942
  if (cachedVersion !== null) return cachedVersion;
538
943
  const here = dirname2(fileURLToPath(import.meta.url));
539
- const candidates = [join3(here, "..", "..", "package.json"), join3(here, "..", "package.json")];
944
+ const candidates = [join4(here, "..", "..", "package.json"), join4(here, "..", "package.json")];
540
945
  for (const path of candidates) {
541
946
  try {
542
947
  const pkg = JSON.parse(readFileSync(path, "utf8"));
@@ -563,17 +968,17 @@ var init_version = __esm({
563
968
 
564
969
  // src/util/update-check.ts
565
970
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
566
- import { homedir as homedir2 } from "os";
567
- import { join as join4, dirname as dirname3 } from "path";
971
+ import { homedir as homedir3 } from "os";
972
+ import { join as join5, dirname as dirname3 } from "path";
568
973
  import { fileURLToPath as fileURLToPath2 } from "url";
569
974
  function cachePath() {
570
- const xdg = process.env.XDG_CONFIG_HOME || join4(homedir2(), ".config");
571
- return join4(xdg, "kimiflare", "update-check.json");
975
+ const xdg = process.env.XDG_CONFIG_HOME || join5(homedir3(), ".config");
976
+ return join5(xdg, "kimiflare", "update-check.json");
572
977
  }
573
978
  async function findPackageJson(startDir) {
574
979
  let dir = startDir;
575
980
  while (true) {
576
- const candidate = join4(dir, "package.json");
981
+ const candidate = join5(dir, "package.json");
577
982
  try {
578
983
  const raw = await readFile3(candidate, "utf8");
579
984
  const parsed = JSON.parse(raw);
@@ -665,21 +1070,21 @@ var init_update_check = __esm({
665
1070
 
666
1071
  // src/remote/session-store.ts
667
1072
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
668
- import { homedir as homedir3 } from "os";
669
- import { join as join5 } from "path";
1073
+ import { homedir as homedir4 } from "os";
1074
+ import { join as join6 } from "path";
670
1075
  function remoteDir() {
671
- const xdg = process.env.XDG_DATA_HOME || join5(homedir3(), ".config");
672
- return join5(xdg, "kimiflare", "remote");
1076
+ const xdg = process.env.XDG_DATA_HOME || join6(homedir4(), ".config");
1077
+ return join6(xdg, "kimiflare", "remote");
673
1078
  }
674
1079
  async function saveRemoteSession(session) {
675
1080
  const dir = remoteDir();
676
1081
  await mkdir4(dir, { recursive: true });
677
- const path = join5(dir, `${session.sessionId}.json`);
1082
+ const path = join6(dir, `${session.sessionId}.json`);
678
1083
  await writeFile4(path, JSON.stringify(session, null, 2) + "\n", "utf8");
679
1084
  }
680
1085
  async function loadRemoteSession(sessionId) {
681
1086
  try {
682
- const path = join5(remoteDir(), `${sessionId}.json`);
1087
+ const path = join6(remoteDir(), `${sessionId}.json`);
683
1088
  const raw = await readFile4(path, "utf8");
684
1089
  return JSON.parse(raw);
685
1090
  } catch {
@@ -694,7 +1099,7 @@ async function listRemoteSessions() {
694
1099
  for (const file of files) {
695
1100
  if (!file.endsWith(".json")) continue;
696
1101
  try {
697
- const raw = await readFile4(join5(dir, file), "utf8");
1102
+ const raw = await readFile4(join6(dir, file), "utf8");
698
1103
  sessions.push(JSON.parse(raw));
699
1104
  } catch {
700
1105
  }
@@ -716,7 +1121,7 @@ var init_session_store = __esm({
716
1121
 
717
1122
  // src/remote/deploy.ts
718
1123
  import { execSync } from "child_process";
719
- import { join as join6, dirname as dirname4 } from "path";
1124
+ import { join as join7, dirname as dirname4 } from "path";
720
1125
  import { fileURLToPath as fileURLToPath3 } from "url";
721
1126
  import { randomBytes } from "crypto";
722
1127
  function generateSecret() {
@@ -751,7 +1156,7 @@ async function* deployForTui() {
751
1156
  yield { message: "Docker OK" };
752
1157
  yield { message: "Building remote agent bundle..." };
753
1158
  try {
754
- runCapture("npm run build:remote-agent", join6(REMOTE_DIR, ".."));
1159
+ runCapture("npm run build:remote-agent", join7(REMOTE_DIR, ".."));
755
1160
  yield { message: "Agent bundle built" };
756
1161
  } catch (err) {
757
1162
  yield { message: `Build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
@@ -881,413 +1286,8 @@ var init_deploy = __esm({
881
1286
  "use strict";
882
1287
  init_config();
883
1288
  __dirname2 = dirname4(fileURLToPath3(import.meta.url));
884
- REMOTE_DIR = join6(__dirname2, "..", "..", "..", "remote");
885
- WORKER_DIR = join6(REMOTE_DIR, "worker");
886
- }
887
- });
888
-
889
- // src/util/log-sink.ts
890
- var log_sink_exports = {};
891
- __export(log_sink_exports, {
892
- flushAndCloseForTesting: () => flushAndCloseForTesting,
893
- getLogSessionId: () => getLogSessionId,
894
- getLogTurnId: () => getLogTurnId,
895
- isLogSinkEnabled: () => isLogSinkEnabled,
896
- logDir: () => logDir,
897
- logPathFor: () => logPathFor,
898
- pruneOldLogs: () => pruneOldLogs,
899
- setLogDirForTesting: () => setLogDirForTesting,
900
- setLogSessionId: () => setLogSessionId,
901
- setLogSinkEnabled: () => setLogSinkEnabled,
902
- setLogTurnId: () => setLogTurnId,
903
- writeLogLine: () => writeLogLine
904
- });
905
- import { homedir as homedir4 } from "os";
906
- import { join as join7 } from "path";
907
- import { mkdirSync, createWriteStream, readdirSync, statSync, unlinkSync } from "fs";
908
- function defaultLogDir() {
909
- const xdg = process.env.XDG_CONFIG_HOME || join7(homedir4(), ".config");
910
- return join7(xdg, "kimiflare", "logs");
911
- }
912
- function isInNodeTestContext() {
913
- if (process.env.NODE_TEST_CONTEXT) return true;
914
- if (process.env.NODE_ENV === "test") return true;
915
- if (process.argv.includes("--test")) return true;
916
- return false;
917
- }
918
- function logDir() {
919
- return overrideDir ?? defaultLogDir();
920
- }
921
- function logPathFor(date = /* @__PURE__ */ new Date()) {
922
- return join7(logDir(), `${date.toISOString().slice(0, 10)}.jsonl`);
923
- }
924
- function setLogDirForTesting(dir) {
925
- if (currentStream) {
926
- currentStream.end();
927
- currentStream = null;
928
- currentDate = null;
929
- }
930
- overrideDir = dir;
931
- }
932
- function setLogSinkEnabled(enabled2) {
933
- if (!enabled2 && currentStream) {
934
- currentStream.end();
935
- currentStream = null;
936
- currentDate = null;
937
- }
938
- sinkEnabled = enabled2;
939
- }
940
- async function flushAndCloseForTesting() {
941
- if (!currentStream) return;
942
- const s = currentStream;
943
- currentStream = null;
944
- currentDate = null;
945
- await new Promise((resolve14) => {
946
- s.end(() => resolve14());
947
- });
948
- }
949
- function isLogSinkEnabled() {
950
- return sinkEnabled;
951
- }
952
- function ensureStream(now2) {
953
- if (!sinkEnabled) return null;
954
- const dateKey = now2.toISOString().slice(0, 10);
955
- if (currentStream && currentDate === dateKey) return currentStream;
956
- if (currentStream) {
957
- currentStream.end();
958
- currentStream = null;
959
- }
960
- try {
961
- mkdirSync(logDir(), { recursive: true });
962
- currentStream = createWriteStream(logPathFor(now2), { flags: "a" });
963
- currentDate = dateKey;
964
- currentStream.on("error", () => {
965
- currentStream = null;
966
- currentDate = null;
967
- });
968
- return currentStream;
969
- } catch {
970
- return null;
971
- }
972
- }
973
- function writeLogLine(entry, now2 = /* @__PURE__ */ new Date()) {
974
- const stream = ensureStream(now2);
975
- if (!stream) return;
976
- try {
977
- stream.write(JSON.stringify(entry) + "\n");
978
- } catch {
979
- }
980
- }
981
- function pruneOldLogs(retentionDays = RETENTION_DAYS) {
982
- let removed = 0;
983
- let entries;
984
- try {
985
- entries = readdirSync(logDir());
986
- } catch {
987
- return 0;
988
- }
989
- const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
990
- for (const name of entries) {
991
- if (!name.endsWith(".jsonl")) continue;
992
- const full = join7(logDir(), name);
993
- try {
994
- const st = statSync(full);
995
- if (st.mtimeMs < cutoff) {
996
- unlinkSync(full);
997
- removed += 1;
998
- }
999
- } catch {
1000
- }
1001
- }
1002
- return removed;
1003
- }
1004
- function setLogSessionId(id) {
1005
- currentSessionId = id;
1006
- }
1007
- function setLogTurnId(id) {
1008
- currentTurnId = id;
1009
- }
1010
- function getLogSessionId() {
1011
- return currentSessionId;
1012
- }
1013
- function getLogTurnId() {
1014
- return currentTurnId;
1015
- }
1016
- var RETENTION_DAYS, overrideDir, currentStream, currentDate, sinkEnabled, currentSessionId, currentTurnId;
1017
- var init_log_sink = __esm({
1018
- "src/util/log-sink.ts"() {
1019
- "use strict";
1020
- RETENTION_DAYS = 7;
1021
- overrideDir = null;
1022
- currentStream = null;
1023
- currentDate = null;
1024
- sinkEnabled = process.env.KIMIFLARE_LOG_SINK !== "off" && !isInNodeTestContext();
1025
- currentSessionId = null;
1026
- currentTurnId = null;
1027
- }
1028
- });
1029
-
1030
- // src/util/otel-sink.ts
1031
- var otel_sink_exports = {};
1032
- __export(otel_sink_exports, {
1033
- buildOtlpPayload: () => buildOtlpPayload,
1034
- enqueueOtelLog: () => enqueueOtelLog,
1035
- flushOtelSink: () => flushOtelSink,
1036
- getOtelDropCount: () => getOtelDropCount,
1037
- getOtelQueueSize: () => getOtelQueueSize,
1038
- initOtelSink: () => initOtelSink,
1039
- installOtelExitHook: () => installOtelExitHook,
1040
- isOtelEnabled: () => isOtelEnabled,
1041
- resetOtelSinkForTesting: () => resetOtelSinkForTesting,
1042
- setFetchForTesting: () => setFetchForTesting
1043
- });
1044
- function setFetchForTesting(f) {
1045
- fetchImpl = f ?? ((url, init) => fetch(url, init));
1046
- }
1047
- function parseHeaders(raw) {
1048
- if (!raw) return {};
1049
- const out = {};
1050
- for (const pair of raw.split(",")) {
1051
- const trimmed = pair.trim();
1052
- if (!trimmed) continue;
1053
- const eq = trimmed.indexOf("=");
1054
- if (eq <= 0) continue;
1055
- const key = trimmed.slice(0, eq).trim();
1056
- const value = trimmed.slice(eq + 1).trim();
1057
- if (key) out[key] = value;
1058
- }
1059
- return out;
1060
- }
1061
- function normalizeEndpoint(raw) {
1062
- const stripped = raw.replace(/\/+$/, "");
1063
- if (/\/v1\/(logs|traces|metrics)$/.test(stripped)) return stripped;
1064
- return `${stripped}/v1/logs`;
1065
- }
1066
- function initOtelSink(opts2 = {}) {
1067
- const endpoint = opts2.endpoint ?? process.env.KIMIFLARE_OTEL_ENDPOINT;
1068
- if (!endpoint) {
1069
- config = null;
1070
- return false;
1071
- }
1072
- config = {
1073
- endpoint: normalizeEndpoint(endpoint),
1074
- headers: parseHeaders(opts2.headers ?? process.env.KIMIFLARE_OTEL_HEADERS),
1075
- serviceName: opts2.serviceName ?? "kimiflare",
1076
- serviceVersion: opts2.serviceVersion ?? process.env.npm_package_version ?? "0.0.0"
1077
- };
1078
- return true;
1079
- }
1080
- function isOtelEnabled() {
1081
- return config !== null;
1082
- }
1083
- function resetOtelSinkForTesting() {
1084
- if (timer) {
1085
- clearTimeout(timer);
1086
- timer = null;
1087
- }
1088
- queue = [];
1089
- config = null;
1090
- dropCount = 0;
1091
- inFlightFlush = null;
1092
- }
1093
- function getOtelDropCount() {
1094
- return dropCount;
1095
- }
1096
- function getOtelQueueSize() {
1097
- return queue.length;
1098
- }
1099
- function enqueueOtelLog(entry) {
1100
- if (!config) return;
1101
- queue.push(entry);
1102
- if (queue.length >= BATCH_MAX) {
1103
- void flushOtelSink();
1104
- return;
1105
- }
1106
- scheduleFlush();
1107
- }
1108
- function scheduleFlush() {
1109
- if (timer) return;
1110
- timer = setTimeout(() => {
1111
- timer = null;
1112
- void flushOtelSink();
1113
- }, FLUSH_INTERVAL_MS);
1114
- if (typeof timer === "object" && timer && "unref" in timer) {
1115
- timer.unref();
1116
- }
1117
- }
1118
- async function flushOtelSink() {
1119
- if (!config) {
1120
- queue = [];
1121
- return;
1122
- }
1123
- if (queue.length === 0) return;
1124
- if (inFlightFlush) return inFlightFlush;
1125
- const batch = queue;
1126
- queue = [];
1127
- const payload = buildOtlpPayload(batch, config);
1128
- inFlightFlush = (async () => {
1129
- try {
1130
- const res = await fetchImpl(config.endpoint, {
1131
- method: "POST",
1132
- headers: {
1133
- "Content-Type": "application/json",
1134
- ...config.headers
1135
- },
1136
- body: JSON.stringify(payload)
1137
- });
1138
- if (!res.ok) {
1139
- dropCount += batch.length;
1140
- }
1141
- } catch {
1142
- dropCount += batch.length;
1143
- } finally {
1144
- inFlightFlush = null;
1145
- }
1146
- })();
1147
- return inFlightFlush;
1148
- }
1149
- function attr(key, value) {
1150
- if (value === null || value === void 0) return null;
1151
- if (typeof value === "string") return { key, value: { stringValue: value } };
1152
- if (typeof value === "boolean") return { key, value: { boolValue: value } };
1153
- if (typeof value === "number" && Number.isFinite(value)) {
1154
- return { key, value: { intValue: String(Math.trunc(value)) } };
1155
- }
1156
- return { key, value: { stringValue: JSON.stringify(value) } };
1157
- }
1158
- function flattenDataAttrs(data) {
1159
- if (!data) return [];
1160
- const out = [];
1161
- for (const [key, value] of Object.entries(data)) {
1162
- if (key === "request_id" || key === "requestId") continue;
1163
- const a = attr(key, value);
1164
- if (a) out.push(a);
1165
- }
1166
- return out;
1167
- }
1168
- function buildOtlpPayload(entries, cfg) {
1169
- const resourceAttrs = [
1170
- attr("service.name", cfg.serviceName),
1171
- attr("service.version", cfg.serviceVersion),
1172
- ...process.env.HOSTNAME ? [attr("host.name", process.env.HOSTNAME)] : []
1173
- ];
1174
- const logRecords = entries.map((e) => {
1175
- const attrs = [];
1176
- if (e.session_id) attrs.push(attr("session_id", e.session_id));
1177
- if (e.turn_id) attrs.push(attr("turn_id", e.turn_id));
1178
- if (e.request_id) attrs.push(attr("request_id", e.request_id));
1179
- attrs.push(attr("event", e.event));
1180
- attrs.push(...flattenDataAttrs(e.data));
1181
- const timeNs = String(BigInt(new Date(e.ts).getTime()) * 1000000n);
1182
- return {
1183
- timeUnixNano: timeNs,
1184
- observedTimeUnixNano: timeNs,
1185
- severityNumber: SEVERITY_NUMBER[e.level],
1186
- severityText: SEVERITY_TEXT[e.level],
1187
- body: { stringValue: e.event },
1188
- attributes: attrs
1189
- };
1190
- });
1191
- return {
1192
- resourceLogs: [
1193
- {
1194
- resource: { attributes: resourceAttrs },
1195
- scopeLogs: [
1196
- {
1197
- scope: { name: "kimiflare", version: cfg.serviceVersion },
1198
- logRecords
1199
- }
1200
- ]
1201
- }
1202
- ]
1203
- };
1204
- }
1205
- function installOtelExitHook() {
1206
- if (exitHookInstalled) return;
1207
- exitHookInstalled = true;
1208
- const drain = () => {
1209
- if (queue.length === 0 && !inFlightFlush) return;
1210
- void flushOtelSink();
1211
- };
1212
- process.on("beforeExit", drain);
1213
- }
1214
- var BATCH_MAX, FLUSH_INTERVAL_MS, SEVERITY_NUMBER, SEVERITY_TEXT, config, queue, timer, dropCount, inFlightFlush, fetchImpl, exitHookInstalled;
1215
- var init_otel_sink = __esm({
1216
- "src/util/otel-sink.ts"() {
1217
- "use strict";
1218
- BATCH_MAX = 100;
1219
- FLUSH_INTERVAL_MS = 5e3;
1220
- SEVERITY_NUMBER = {
1221
- debug: 5,
1222
- info: 9,
1223
- warn: 13,
1224
- error: 17,
1225
- off: 0
1226
- // never actually emitted, but maps cleanly
1227
- };
1228
- SEVERITY_TEXT = {
1229
- debug: "DEBUG",
1230
- info: "INFO",
1231
- warn: "WARN",
1232
- error: "ERROR",
1233
- off: "OFF"
1234
- };
1235
- config = null;
1236
- queue = [];
1237
- timer = null;
1238
- dropCount = 0;
1239
- inFlightFlush = null;
1240
- fetchImpl = (url, init) => fetch(url, init);
1241
- exitHookInstalled = false;
1242
- }
1243
- });
1244
-
1245
- // src/util/logger.ts
1246
- function log(level, event, data) {
1247
- const sessionId = getLogSessionId();
1248
- const turnId = getLogTurnId();
1249
- const requestId = typeof data?.request_id === "string" ? data.request_id : typeof data?.requestId === "string" ? data.requestId : void 0;
1250
- const entry = {
1251
- ts: (/* @__PURE__ */ new Date()).toISOString(),
1252
- level,
1253
- event,
1254
- ...sessionId ? { session_id: sessionId } : {},
1255
- ...turnId ? { turn_id: turnId } : {},
1256
- ...requestId ? { request_id: requestId } : {},
1257
- data
1258
- };
1259
- recentLogs.push(entry);
1260
- if (recentLogs.length > RECENT_LOGS_MAX) {
1261
- recentLogs.shift();
1262
- }
1263
- if (LEVEL_ORDER[level] >= LEVEL_ORDER[globalMinLevel]) {
1264
- console.error(JSON.stringify(entry));
1265
- }
1266
- writeLogLine(entry);
1267
- enqueueOtelLog(entry);
1268
- }
1269
- var globalMinLevel, LEVEL_ORDER, RECENT_LOGS_MAX, recentLogs, logger;
1270
- var init_logger = __esm({
1271
- "src/util/logger.ts"() {
1272
- "use strict";
1273
- init_log_sink();
1274
- init_otel_sink();
1275
- globalMinLevel = process.env.KIMIFLARE_LOG_LEVEL ?? "off";
1276
- LEVEL_ORDER = {
1277
- debug: 0,
1278
- info: 1,
1279
- warn: 2,
1280
- error: 3,
1281
- off: 4
1282
- };
1283
- RECENT_LOGS_MAX = 100;
1284
- recentLogs = [];
1285
- logger = {
1286
- debug: (event, data) => log("debug", event, data),
1287
- info: (event, data) => log("info", event, data),
1288
- warn: (event, data) => log("warn", event, data),
1289
- error: (event, data) => log("error", event, data)
1290
- };
1289
+ REMOTE_DIR = join7(__dirname2, "..", "..", "..", "remote");
1290
+ WORKER_DIR = join7(REMOTE_DIR, "worker");
1291
1291
  }
1292
1292
  });
1293
1293
 
@@ -8782,7 +8782,7 @@ async function callWorkerEndpoint(endpoint, apiKey, payload, signal, timeoutMs =
8782
8782
  ...apiKey ? { "X-Worker-Api-Key": apiKey } : {}
8783
8783
  };
8784
8784
  const body = JSON.stringify(payload);
8785
- const fetchSignal = signal ?? controller.signal;
8785
+ const fetchSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
8786
8786
  try {
8787
8787
  const res = await fetch(url, { method: "POST", headers, body, signal: fetchSignal });
8788
8788
  if (res.ok) {
@@ -8947,7 +8947,7 @@ ${finding.summary}`)
8947
8947
  if (!endpoint) {
8948
8948
  return textOutput("Worker endpoint not configured. Set KIMIFLARE_WORKER_ENDPOINT or workerEndpoint in config, or set workerBackend to hotcell.");
8949
8949
  }
8950
- const apiKey = process.env.KIMIFLARE_WORKER_API_KEY;
8950
+ const apiKey = process.env.KIMIFLARE_WORKER_API_KEY ?? cfg?.workerApiKey;
8951
8951
  const defaultModel = cfg?.model ?? DEFAULT_MODEL;
8952
8952
  const workerModel = args.model ?? ctx.model ?? defaultModel;
8953
8953
  if (cfg && usesRequesty(llmAuthFromConfig(cfg)) && !workerModel.includes("/")) {
@@ -11510,13 +11510,13 @@ function directiveFor(kind) {
11510
11510
  return "The user explicitly asked not to delegate. Do not call spawn_worker; handle the task in the main session and respect any stated dependencies.";
11511
11511
  }
11512
11512
  if (kind === "explicit-delegate") {
11513
- return "The user explicitly requested subagents. Identify bounded, independent read-only research missions and use spawn_worker where useful; keep dependent implementation in the coordinator. The tool's normal permission prompt and all configured safety limits still apply.";
11513
+ return "The user explicitly requested subagents. Before acting, split the request into bounded missions that can make progress independently; use spawn_worker in plan mode for useful parallel read-only research, wait for the workers, and synthesize their findings before dependent implementation. Do not delegate tightly coupled or sequential steps. The tool's normal permission prompt and all configured safety limits still apply.";
11514
11514
  }
11515
11515
  if (kind === "suggest") {
11516
11516
  return "The harness sees a possible parallel-work opportunity, but the user did not request delegation. Use spawn_worker only if you can define bounded, independent missions that materially help. The tool's normal permission prompt is the required user confirmation; never assume approval. Keep dependent implementation in the coordinator.";
11517
11517
  }
11518
11518
  if (kind === "auto-delegate") {
11519
- return "The opt-in automatic subagent policy found a strong parallel-work opportunity. Delegate only bounded, independent research missions with spawn_worker, then synthesize the findings and do dependent work in the coordinator. The tool's normal permission prompt, provider restrictions, spend cap, concurrency, timeout, and read-only profile still apply; never bypass them.";
11519
+ return "Proactive subagent delegation is enabled for this substantial task. First assess its structure, dependencies, and coordination cost. If two or more bounded research tracks can make meaningful progress independently, delegate them with spawn_worker in plan mode in parallel, wait for their results, and synthesize the findings before doing dependent work in the coordinator. Keep trivial, tightly coupled, and sequential work local. Worker calls still require the normal permission approval and remain subject to the configured provider/backend, spend cap, concurrency, timeout, and read-only restrictions; never bypass them. If a worker cannot run or fails, explain that plainly and continue locally without implying delegation succeeded.";
11520
11520
  }
11521
11521
  return void 0;
11522
11522
  }
@@ -11539,6 +11539,9 @@ async function resolveSubagentGuidance(options) {
11539
11539
  return result(options.policy === "auto" ? "auto-delegate" : "suggest", "independent work is explicit");
11540
11540
  }
11541
11541
  if (options.tier === "light") return result("none", "routine or sequential task");
11542
+ if (options.policy === "auto") {
11543
+ return result("auto-delegate", "substantial task; coordinator assesses independence");
11544
+ }
11542
11545
  if (!AMBIGUOUS_CANDIDATE.test(prompt)) return result("none", "no parallel-work signal");
11543
11546
  if (!options.apiKey || options.customEndpoint) return result("none", "Jev is unavailable for this provider configuration");
11544
11547
  if (options.signal?.aborted) return result("none", "turn cancelled before Jev advice");
@@ -11561,9 +11564,6 @@ async function resolveSubagentGuidance(options) {
11561
11564
  if (probability === void 0 || !Number.isFinite(probability) || probability < 0 || probability > 1) {
11562
11565
  return result("none", "Jev returned no usable delegation probability");
11563
11566
  }
11564
- if (options.policy === "auto" && probability >= AUTO_THRESHOLD) {
11565
- return result("auto-delegate", "Jev recommends independent work", probability);
11566
- }
11567
11567
  if (probability >= SUGGEST_THRESHOLD) return result("suggest", "Jev sees a possible parallel-work benefit", probability);
11568
11568
  return result("none", "Jev recommends sequential work", probability);
11569
11569
  } catch {
@@ -11573,7 +11573,7 @@ async function resolveSubagentGuidance(options) {
11573
11573
  function redactPrompt(prompt) {
11574
11574
  return prompt.replace(/\b(?:sk-or-v1-|sk-ant-|gh[pousr]_|github_pat_)[A-Za-z0-9_-]{8,}\b/gi, "[REDACTED]").replace(/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi, "Bearer [REDACTED]").replace(/\b[A-Z0-9_]*(?:API[_-]?KEY|ACCESS[_-]?TOKEN|SECRET|PASSWORD)\s*[:=]\s*[^\s,;]+/gi, "[REDACTED]");
11575
11575
  }
11576
- var EXPLICIT_NO, EXPLICIT_YES, SEQUENTIAL_DEPENDENCY, CLEAR_PARALLEL, AMBIGUOUS_CANDIDATE, SUBAGENT_JEV_TIMEOUT_MS, SUBAGENT_JEV_OPTIONS, SUGGEST_THRESHOLD, AUTO_THRESHOLD, MAX_JEV_PROMPT_CHARS;
11576
+ var EXPLICIT_NO, EXPLICIT_YES, SEQUENTIAL_DEPENDENCY, CLEAR_PARALLEL, AMBIGUOUS_CANDIDATE, SUBAGENT_JEV_TIMEOUT_MS, SUBAGENT_JEV_OPTIONS, SUGGEST_THRESHOLD, MAX_JEV_PROMPT_CHARS;
11577
11577
  var init_subagent_policy = __esm({
11578
11578
  "src/intent/subagent-policy.ts"() {
11579
11579
  "use strict";
@@ -11586,7 +11586,6 @@ var init_subagent_policy = __esm({
11586
11586
  SUBAGENT_JEV_TIMEOUT_MS = Math.min(JEV_TIMEOUT_MS, 4e3);
11587
11587
  SUBAGENT_JEV_OPTIONS = ["delegate", "sequential"];
11588
11588
  SUGGEST_THRESHOLD = 0.65;
11589
- AUTO_THRESHOLD = 0.8;
11590
11589
  MAX_JEV_PROMPT_CHARS = 1200;
11591
11590
  }
11592
11591
  });
@@ -18731,12 +18730,11 @@ var init_builtins = __esm({
18731
18730
  { name: "help", description: "Show keybindings and command list", source: "builtin" },
18732
18731
  { name: "model", argHint: "[list|<id>]", description: "Pick model (no args opens picker)", source: "builtin" },
18733
18732
  { name: "jev", argHint: "[yes|choose|score]", description: "Ask Jev for a one-shot decision with scoped chat/project context", source: "builtin" },
18734
- { name: "subagents", argHint: "[off|suggest|auto]", description: "Choose when the harness recommends or dispatches subagents", source: "builtin" },
18733
+ { name: "subagents", argHint: "[off|suggest|auto]", description: "Control proactive delegation for substantial independent work", source: "builtin" },
18735
18734
  { name: "mode", argHint: "[edit|plan|auto]", description: "Switch agent mode", source: "builtin" },
18736
18735
  { name: "multi-agent", argHint: "[enable|disable|status|setup]", description: "Configure multi-agent (endpoint, auto-implement, set up)", source: "builtin" },
18737
18736
  { name: "theme", argHint: "[<name>]", description: "Switch color theme", source: "builtin" },
18738
18737
  // Ink is the only UI engine; handler remains for manual use.
18739
- { name: "ui", argHint: "[ink|camouflage]", description: "Switch between the Ink and Camouflage UIs (takes effect on next launch)", source: "builtin" },
18740
18738
  { name: "memory", argHint: "[on|off|clear|search ...]", description: "Manage memory", source: "builtin" },
18741
18739
  { name: "cost", argHint: "[verify|on|off]", description: "Show cost report or toggle attribution", source: "builtin" },
18742
18740
  { name: "settings", argHint: "[modes on|off]", description: "Show or change feature settings (e.g. plan/edit/auto modes)", source: "builtin" },
@@ -19201,7 +19199,11 @@ var init_help_menu = __esm({
19201
19199
  { command: "/settings modes off", description: "turn modes off (always auto)" },
19202
19200
  { command: "/mode edit", description: "switch to edit mode" },
19203
19201
  { command: "/mode plan", description: "switch to plan mode" },
19204
- { command: "/mode auto", description: "switch to auto mode" }
19202
+ { command: "/mode auto", description: "switch to auto mode" },
19203
+ { command: "/subagents", description: "show delegation policy and backend setup" },
19204
+ { command: "/subagents auto", description: "let the coordinator assess substantial tasks (default)" },
19205
+ { command: "/subagents suggest", description: "use conservative delegation suggestions" },
19206
+ { command: "/subagents off", description: "disable automatic delegation suggestions" }
19205
19207
  ]
19206
19208
  },
19207
19209
  {
@@ -22253,6 +22255,7 @@ var init_hooks_wizard = __esm({
22253
22255
  // src/camouflage-view.ts
22254
22256
  var camouflage_view_exports = {};
22255
22257
  __export(camouflage_view_exports, {
22258
+ CamouflageUnavailable: () => CamouflageUnavailable,
22256
22259
  helpOptions: () => helpOptions,
22257
22260
  listMentionDir: () => listMentionDir,
22258
22261
  modelOptions: () => modelOptions,
@@ -22267,29 +22270,35 @@ import { homedir as homedir22 } from "os";
22267
22270
  import { join as join33, resolve as resolve11 } from "path";
22268
22271
  async function loadSdk() {
22269
22272
  const sdk = await import("camouflage-tui").catch((err) => {
22270
- throw new Error(`camouflage-tui is not installed (${err.message}). Reinstall autopilot, or use --ui ink.`);
22273
+ throw new CamouflageUnavailable(`camouflage-tui is not installed (${err.message})`);
22271
22274
  });
22272
22275
  if (typeof sdk.permission !== "function" || typeof sdk.mount !== "function" || typeof sdk.selectList !== "function") {
22273
- throw new Error("the installed camouflage-tui is too old for --ui camouflage. Update autopilot, or use --ui ink.");
22276
+ throw new CamouflageUnavailable("the installed camouflage-tui is too old");
22274
22277
  }
22275
22278
  return sdk;
22276
22279
  }
22277
22280
  async function runCamouflageView(opts2) {
22278
22281
  const sdk = await loadSdk();
22279
22282
  const restoreConsole = captureConsole();
22280
- const cam = await sdk.mount({
22281
- ui: "inline",
22282
- appTitle: "autopilot",
22283
- inheritStderr: false,
22284
- ...process.env.CAMOUFLAGE_BIN ? { bin: process.env.CAMOUFLAGE_BIN } : {}
22285
- });
22283
+ let cam;
22284
+ try {
22285
+ cam = await sdk.mount({
22286
+ ui: "inline",
22287
+ appTitle: "autopilot",
22288
+ inheritStderr: false,
22289
+ ...process.env.CAMOUFLAGE_BIN ? { bin: process.env.CAMOUFLAGE_BIN } : {}
22290
+ });
22291
+ } catch (err) {
22292
+ restoreConsole();
22293
+ throw new CamouflageUnavailable(err.message);
22294
+ }
22286
22295
  cam.on("stderr", (chunk) => logger.warn("camouflage.stderr", { chunk }));
22287
22296
  const cwd = process.cwd();
22288
22297
  cam.send("SessionStarted", {
22289
22298
  title: `autopilot ${opts2.version}`,
22290
22299
  detail: [
22291
22300
  [shortModel(opts2.cfg.model), shortPath(cwd), gitBranch(cwd)].filter(Boolean).join(" \xB7 "),
22292
- "/ for commands \xB7 @ to mention files \xB7 shift+tab to switch modes"
22301
+ `/ for commands \xB7 @ to mention files${opts2.cfg.modesEnabled ? " \xB7 shift+tab to switch modes" : ""}`
22293
22302
  ],
22294
22303
  accent: "orange",
22295
22304
  assistant_label: "autopilot"
@@ -22517,7 +22526,7 @@ function safeString(v) {
22517
22526
  return String(v);
22518
22527
  }
22519
22528
  }
22520
- var View;
22529
+ var CamouflageUnavailable, View;
22521
22530
  var init_camouflage_view = __esm({
22522
22531
  "src/camouflage-view.ts"() {
22523
22532
  "use strict";
@@ -22536,6 +22545,8 @@ var init_camouflage_view = __esm({
22536
22545
  init_hooks_wizard();
22537
22546
  init_mode();
22538
22547
  init_logger();
22548
+ CamouflageUnavailable = class extends Error {
22549
+ };
22539
22550
  View = class {
22540
22551
  constructor(cam, sdk) {
22541
22552
  this.cam = cam;
@@ -22680,7 +22691,8 @@ var init_camouflage_view = __esm({
22680
22691
  }
22681
22692
  syncStatus(s) {
22682
22693
  const next = {
22683
- mode: s.mode === "auto" ? "auto" : s.mode === "plan" ? "plan" : "edit",
22694
+ // With modes off (the default) every turn is auto; show no mode badge.
22695
+ mode: !s.modesEnabled ? "" : s.mode === "auto" ? "auto" : s.mode === "plan" ? "plan" : "edit",
22684
22696
  phase: s.busy ? "thinking" : "idle",
22685
22697
  model: shortModel(s.model),
22686
22698
  tokens: s.usage ? `${formatK2(s.usage.prompt_tokens)} tokens` : "",
@@ -22907,7 +22919,7 @@ var init_camouflage_view = __esm({
22907
22919
  return want(`commandWizard-${s.commandWizard.mode}-${s.commandWizard.initial?.name ?? ""}`, () => this.commandEditor(s));
22908
22920
  default:
22909
22921
  this.cam.send("RuntimeError", {
22910
- message: `${modalName(modal)} isn't available in the Camouflage UI yet. Run \`autopilot --ui ink\` for it.`,
22922
+ message: `${modalName(modal)} isn't available here yet.`,
22911
22923
  severity: "warn"
22912
22924
  });
22913
22925
  a.closeModal(modal);
@@ -26397,7 +26409,7 @@ function Welcome() {
26397
26409
  });
26398
26410
  return /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", marginBottom: 1, children: [
26399
26411
  /* @__PURE__ */ jsx24(Box22, { marginBottom: 1, children: /* @__PURE__ */ jsx24(Text23, { bold: true, color: theme.accent, children: headline }) }),
26400
- /* @__PURE__ */ jsx24(Box22, { flexDirection: "column", children: /* @__PURE__ */ jsx24(Text23, { color: theme.info.color, dimColor: true, children: "Type / for commands" }) })
26412
+ /* @__PURE__ */ jsx24(Box22, { flexDirection: "column", children: /* @__PURE__ */ jsx24(Text23, { color: theme.info.color, dimColor: true, children: "Type / for commands \xB7 try /subagents help" }) })
26401
26413
  ] });
26402
26414
  }
26403
26415
  var init_welcome = __esm({
@@ -31697,7 +31709,7 @@ function dispatchSlashCommand(ctx, cmd) {
31697
31709
  if (!handler) return false;
31698
31710
  return handler(ctx, rest, arg);
31699
31711
  }
31700
- var handleExit, handleClear, handleFresh, handleReasoning, handleCost, handleShell, handleSubagents, handleJev, handleModel, SETTINGS, handleSettings, handleKey, handleMode, handleMultiAgent, handleTheme, handleUi, handlePlan, handleAuto, handleEdit, handleSkills, handleMemory, handleResume, handleCheckpoint, handleCompact, handleInit, handleUpdate, handleMcp, handleLsp, handleHooks, handleHello, handleInbox, handleLogout, handleCommand, handleRemote, handleHelp, handleChangelogImage, handlers;
31712
+ var handleExit, handleClear, handleFresh, handleReasoning, handleCost, handleShell, handleSubagents, handleJev, handleModel, SETTINGS, handleSettings, handleKey, handleMode, handleMultiAgent, handleTheme, handlePlan, handleAuto, handleEdit, handleSkills, handleMemory, handleResume, handleCheckpoint, handleCompact, handleInit, handleUpdate, handleMcp, handleLsp, handleHooks, handleHello, handleInbox, handleLogout, handleCommand, handleRemote, handleHelp, handleChangelogImage, handlers;
31701
31713
  var init_slash_commands = __esm({
31702
31714
  "src/ui/slash-commands.ts"() {
31703
31715
  "use strict";
@@ -31928,10 +31940,10 @@ var init_slash_commands = __esm({
31928
31940
  setEvents((events) => [...events, {
31929
31941
  kind: "info",
31930
31942
  key: mkKey2(),
31931
- text: `subagent policy: ${cfg?.subagentPolicy ?? "suggest"}
31943
+ text: `subagent policy: ${cfg?.subagentPolicy ?? "auto"}
31932
31944
  usage: /subagents off|suggest|auto
31933
31945
 
31934
- off disables harness suggestions; suggest (default) asks before any worker call; auto lets the coordinator dispatch on strong evidence. Explicit user instructions still win. Every spawn_worker call remains permission-gated and budget-capped.`
31946
+ auto (default) lets the coordinator assess substantial tasks and delegate independent work; it is conditional, not a promise to spawn. suggest surfaces possible opportunities; off disables automatic suggestions. Explicit user instructions still win. Worker calls use normal permission checks and respect spend, concurrency, timeout, and read-only limits. Remote setup: KIMIFLARE_WORKER_ENDPOINT (or workerEndpoint in config). Hotcell: KIMIFLARE_WORKER_BACKEND=hotcell; plan mode only.`
31935
31947
  }]);
31936
31948
  return true;
31937
31949
  }
@@ -31947,7 +31959,7 @@ off disables harness suggestions; suggest (default) asks before any worker call;
31947
31959
  setEvents((events) => [...events, { kind: "error", key: mkKey2(), text: "configure an OpenRouter key or custom endpoint before changing subagent policy" }]);
31948
31960
  return true;
31949
31961
  }
31950
- const next = { ...cfg, subagentPolicy: policy };
31962
+ const next = { ...cfg, subagentPolicy: policy, subagentPolicyExplicit: true };
31951
31963
  setCfg(next);
31952
31964
  void saveConfig(next).catch(() => {
31953
31965
  });
@@ -32330,28 +32342,6 @@ off disables harness suggestions; suggest (default) asks before any worker call;
32330
32342
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `theme: ${next.label} \u2014 restart to apply` }]);
32331
32343
  return true;
32332
32344
  };
32333
- handleUi = (ctx, _rest, arg) => {
32334
- const { setEvents, mkKey: mkKey2 } = ctx;
32335
- const say = (text, kind = "info") => setEvents((e) => [...e, { kind, key: mkKey2(), text }]);
32336
- if (!arg) {
32337
- const current = ctx.cfg?.uiEngine ?? "ink";
32338
- say(`UI: ${current}. Use /ui ink or /ui camouflage (takes effect next launch; --ui overrides for one run).`);
32339
- return true;
32340
- }
32341
- if (arg !== "ink" && arg !== "camouflage") {
32342
- say(`unknown UI "${arg}" \u2014 use ink or camouflage`, "error");
32343
- return true;
32344
- }
32345
- ctx.setCfg((prev) => {
32346
- if (!prev) return prev;
32347
- const updated = { ...prev, uiEngine: arg };
32348
- void saveConfig(updated).catch(() => {
32349
- });
32350
- return updated;
32351
- });
32352
- say(`UI set to ${arg}. It takes effect the next time you start autopilot.`);
32353
- return true;
32354
- };
32355
32345
  handlePlan = (ctx) => {
32356
32346
  if (!modesGate(ctx)) return true;
32357
32347
  ctx.setMode("plan");
@@ -33224,7 +33214,6 @@ project: ${projectSettingsPath(cwd)}`
33224
33214
  "/mode": handleMode,
33225
33215
  "/multi-agent": handleMultiAgent,
33226
33216
  "/theme": handleTheme,
33227
- "/ui": handleUi,
33228
33217
  "/plan": handlePlan,
33229
33218
  "/auto": handleAuto,
33230
33219
  "/edit": handleEdit,
@@ -36559,14 +36548,14 @@ ${conflicts.join("\n")}` }
36559
36548
  const delegationGuidance = await resolveSubagentGuidance({
36560
36549
  prompt: display,
36561
36550
  tier: classification.tier,
36562
- policy: cfg.subagentPolicy ?? "suggest",
36551
+ policy: cfg.subagentPolicy ?? "auto",
36563
36552
  apiKey: cfg.openrouterApiKey,
36564
36553
  customEndpoint: Boolean(cfg.baseUrl),
36565
36554
  signal: turnScope.signal
36566
36555
  });
36567
36556
  if (delegationGuidance.kind !== "none") {
36568
36557
  const probability = delegationGuidance.probability === void 0 ? "" : ` (Jev estimate: ${Math.round(delegationGuidance.probability * 100)}%)`;
36569
- const text2 = delegationGuidance.kind === "explicit-delegate" ? "Subagent policy: respecting your explicit request to delegate. Worker calls still require permission." : delegationGuidance.kind === "explicit-sequential" ? "Subagent policy: respecting your instruction to work without delegation." : delegationGuidance.kind === "auto-delegate" ? `Subagent policy: auto recommends independent research${probability}; worker calls remain permission-gated.` : `Subagent suggestion: independent work may help${probability}. No worker will launch without your confirmation.`;
36558
+ const text2 = delegationGuidance.kind === "explicit-delegate" ? "Subagent policy: respecting your explicit request to delegate. Worker calls still require permission." : delegationGuidance.kind === "explicit-sequential" ? "Subagent policy: respecting your instruction to work without delegation." : delegationGuidance.kind === "auto-delegate" ? "Subagent policy: auto will assess this substantial task for independent work; worker calls remain permission-gated." : `Subagent suggestion: independent work may help${probability}. No worker will launch without your confirmation.`;
36570
36559
  setEvents((events2) => [...events2, { kind: "info", key: mkKey(), text: text2 }]);
36571
36560
  }
36572
36561
  const turnTools = [...ALL_TOOLS, ...mcpToolsRef.current, ...lspToolsRef.current].filter(
@@ -37200,6 +37189,7 @@ ${conflicts.join("\n")}` }
37200
37189
  commandWizard: modals.commandWizard,
37201
37190
  commandPickerMode: modals.commandPicker?.mode ?? null,
37202
37191
  changelogImageRepo,
37192
+ modesEnabled,
37203
37193
  multiAgent: {
37204
37194
  multiAgentEnabled: cfg?.multiAgentEnabled,
37205
37195
  workerEndpoint: cfg?.workerEndpoint,
@@ -37609,6 +37599,7 @@ var init_app = __esm({
37609
37599
  });
37610
37600
 
37611
37601
  // src/index.tsx
37602
+ init_logger();
37612
37603
  init_config();
37613
37604
  init_lsp_config();
37614
37605
  init_update_check();
@@ -37856,7 +37847,7 @@ async function runPrintMode(opts2) {
37856
37847
  const delegationGuidance = await resolveSubagentGuidance({
37857
37848
  prompt: opts2.prompt,
37858
37849
  tier: intent.tier,
37859
- policy: opts2.subagentPolicy ?? "suggest",
37850
+ policy: opts2.subagentPolicy ?? "auto",
37860
37851
  apiKey: opts2.openrouterApiKey,
37861
37852
  customEndpoint: Boolean(opts2.baseUrl)
37862
37853
  });
@@ -38089,7 +38080,7 @@ async function runPrintMode(opts2) {
38089
38080
 
38090
38081
  // src/index.tsx
38091
38082
  var program = new Command2();
38092
- program.name("autopilot").description("Terminal coding agent. Runs any OpenRouter model with your own OpenRouter key.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", `OpenRouter model id (defaults to ${DEFAULT_MODEL})`).addOption(new Option("--cloud", "(retired) KimiFlare Cloud").hideHelp()).option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").addOption(new Option("--worker-profile <profile>", "internal worker permission profile").hideHelp()).option("--reasoning", "include reasoning in stdout (print mode only)").option("--thinking", "alias for --reasoning").option("--continue-on-limit", "reset tool-call counter and continue when the 200-call limit is hit (print mode only)").option("--max-total-tool-iterations <n>", "hard ceiling on tool iterations per turn across --continue-on-limit resets (default 1000)", (v) => parseInt(v, 10)).option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").option("--ui <name>", "interface: ink (default) or camouflage (experimental inline renderer). Also KIMIFLARE_UI.").option("--mode <mode>", "run mode: interactive (default), print, rpc").option("-c, --continue", "continue the most recent session in the current working directory (print mode only)").option("-S, --session <id>", "resume a specific session by id (print mode only)").option("-f, --file <path>", "attach file(s) to the prompt; repeatable, supports globs (print mode only)", (v, prev) => (prev ?? []).concat(v)).option("--format <mode>", "output format for print mode: text (default), json, stream-json").option("--dir <path>", "run in the specified directory instead of the current one (print mode only)").option("--title <title>", "override the auto-generated session title (print mode only)").option("--attach <url>", "attach to a running autopilot serve instance (print mode only)");
38083
+ program.name("autopilot").description("Terminal coding agent. Runs any OpenRouter model with your own OpenRouter key.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", `OpenRouter model id (defaults to ${DEFAULT_MODEL})`).addOption(new Option("--cloud", "(retired) KimiFlare Cloud").hideHelp()).option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").addOption(new Option("--worker-profile <profile>", "internal worker permission profile").hideHelp()).option("--reasoning", "include reasoning in stdout (print mode only)").option("--thinking", "alias for --reasoning").option("--continue-on-limit", "reset tool-call counter and continue when the 200-call limit is hit (print mode only)").option("--max-total-tool-iterations <n>", "hard ceiling on tool iterations per turn across --continue-on-limit resets (default 1000)", (v) => parseInt(v, 10)).option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").addOption(new Option("--ui <name>", "interface: camouflage (default) or ink").hideHelp()).option("--mode <mode>", "run mode: interactive (default), print, rpc").option("-c, --continue", "continue the most recent session in the current working directory (print mode only)").option("-S, --session <id>", "resume a specific session by id (print mode only)").option("-f, --file <path>", "attach file(s) to the prompt; repeatable, supports globs (print mode only)", (v, prev) => (prev ?? []).concat(v)).option("--format <mode>", "output format for print mode: text (default), json, stream-json").option("--dir <path>", "run in the specified directory instead of the current one (print mode only)").option("--title <title>", "override the auto-generated session title (print mode only)").option("--attach <url>", "attach to a running autopilot serve instance (print mode only)");
38093
38084
  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 OpenRouter reconciliation (no network)").option("--verify", "check recorded tokens and cost against OpenRouter's generation records (latest session, or -s <id>)").action(async (cmdOpts) => {
38094
38085
  const cfg = await loadConfig();
38095
38086
  if (cmdOpts.verify) {
@@ -38348,22 +38339,22 @@ To use Requesty instead, set REQUESTY_API_KEY or run autopilot auth requesty`
38348
38339
  process.exit(2);
38349
38340
  }
38350
38341
  const logoText = renderLogo(getAppVersion(), opts.model ?? cfg?.model);
38351
- const uiEngine = (opts.ui ?? process.env.KIMIFLARE_UI ?? cfg?.uiEngine ?? "ink").toLowerCase();
38352
- if (uiEngine === "camouflage" && cfg) {
38342
+ const uiEngine = (opts.ui ?? process.env.KIMIFLARE_UI ?? "camouflage").toLowerCase();
38343
+ if (uiEngine !== "ink" && cfg) {
38353
38344
  const model = opts.model ?? cfg.model ?? DEFAULT_MODEL;
38354
- const { runCamouflageView: runCamouflageView2 } = await Promise.resolve().then(() => (init_camouflage_view(), camouflage_view_exports));
38345
+ const { runCamouflageView: runCamouflageView2, CamouflageUnavailable: CamouflageUnavailable2 } = await Promise.resolve().then(() => (init_camouflage_view(), camouflage_view_exports));
38355
38346
  const { renderAppWithBridge: renderAppWithBridge2 } = await Promise.resolve().then(() => (init_app(), app_exports));
38356
- await runCamouflageView2({
38357
- cfg: { ...cfg, model },
38358
- version: getAppVersion(),
38359
- runApp: (bridge) => renderAppWithBridge2({ ...cfg, model }, bridge, updateResult, lspScope, lspProjectPath)
38360
- });
38361
- process.exit(0);
38362
- }
38363
- if (uiEngine === "camouflage") {
38364
- console.error("autopilot: --ui camouflage needs a model provider key (OpenRouter or Requesty); run `autopilot auth openrouter` or `autopilot auth requesty` to set one up.");
38365
- } else if (uiEngine !== "ink") {
38366
- console.error(`autopilot: unknown --ui "${uiEngine}"; using ink.`);
38347
+ try {
38348
+ await runCamouflageView2({
38349
+ cfg: { ...cfg, model },
38350
+ version: getAppVersion(),
38351
+ runApp: (bridge) => renderAppWithBridge2({ ...cfg, model }, bridge, updateResult, lspScope, lspProjectPath)
38352
+ });
38353
+ process.exit(0);
38354
+ } catch (err) {
38355
+ if (!(err instanceof CamouflageUnavailable2)) throw err;
38356
+ logger.warn("camouflage.unavailable", { error: err.message });
38357
+ }
38367
38358
  }
38368
38359
  console.log(logoText);
38369
38360
  const { renderApp: renderApp2 } = await Promise.resolve().then(() => (init_app(), app_exports));