autopilot-ai 1.19.2 → 1.20.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 +1912 -428
- package/dist/index.js.map +1 -1
- package/dist/sdk/index.d.ts +279 -0
- package/dist/sdk/index.js +1176 -187
- package/dist/sdk/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -635,6 +635,15 @@ function readNumberEnv(name) {
|
|
|
635
635
|
const parsed = parseInt(raw, 10);
|
|
636
636
|
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
637
637
|
}
|
|
638
|
+
function resolveTraceConfig(persisted) {
|
|
639
|
+
const envEnabled = readBooleanEnv("KIMIFLARE_TRACE");
|
|
640
|
+
const envRoot = process.env.KIMIFLARE_TRACE_DIR?.trim() || void 0;
|
|
641
|
+
if (envEnabled === void 0 && envRoot === void 0 && persisted === void 0) return void 0;
|
|
642
|
+
const trace = { enabled: envEnabled ?? persisted?.enabled ?? false };
|
|
643
|
+
const root = envRoot ?? persisted?.root;
|
|
644
|
+
if (root) trace.root = root;
|
|
645
|
+
return trace;
|
|
646
|
+
}
|
|
638
647
|
async function loadConfig() {
|
|
639
648
|
let persisted = {};
|
|
640
649
|
let hasFile = false;
|
|
@@ -726,7 +735,8 @@ async function loadConfig() {
|
|
|
726
735
|
workerPreReadMaxChars: readNumberEnv("KIMIFLARE_WORKER_PRE_READ_MAX_CHARS") ?? persisted.workerPreReadMaxChars,
|
|
727
736
|
permissions: persisted.permissions,
|
|
728
737
|
preferPullRequests: readBooleanEnv("KIMIFLARE_PREFER_PULL_REQUESTS") ?? persisted.preferPullRequests ?? true,
|
|
729
|
-
allowDirectPush: readBooleanEnv("KIMIFLARE_ALLOW_DIRECT_PUSH") ?? persisted.allowDirectPush ?? false
|
|
738
|
+
allowDirectPush: readBooleanEnv("KIMIFLARE_ALLOW_DIRECT_PUSH") ?? persisted.allowDirectPush ?? false,
|
|
739
|
+
trace: resolveTraceConfig(persisted.trace)
|
|
730
740
|
};
|
|
731
741
|
if (hasFile && hasLegacyFields(persisted)) {
|
|
732
742
|
await rewriteLegacyConfig(persisted, openrouterApiKey).catch(() => void 0);
|
|
@@ -1474,6 +1484,68 @@ var init_messages = __esm({
|
|
|
1474
1484
|
}
|
|
1475
1485
|
});
|
|
1476
1486
|
|
|
1487
|
+
// src/trace/blobs.ts
|
|
1488
|
+
import { createHash } from "crypto";
|
|
1489
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
1490
|
+
import { join as join8 } from "path";
|
|
1491
|
+
function sha256Hex(content) {
|
|
1492
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1493
|
+
}
|
|
1494
|
+
function truncateUtf8(content, maxBytes) {
|
|
1495
|
+
const buf = Buffer.from(content, "utf8");
|
|
1496
|
+
if (buf.length <= maxBytes) return content;
|
|
1497
|
+
let end = maxBytes;
|
|
1498
|
+
while (end > 0 && (buf[end] & 192) === 128) end--;
|
|
1499
|
+
return buf.subarray(0, end).toString("utf8");
|
|
1500
|
+
}
|
|
1501
|
+
var DEFAULT_MAX_BLOB_BYTES, BlobStore;
|
|
1502
|
+
var init_blobs = __esm({
|
|
1503
|
+
"src/trace/blobs.ts"() {
|
|
1504
|
+
"use strict";
|
|
1505
|
+
DEFAULT_MAX_BLOB_BYTES = 2 * 1024 * 1024;
|
|
1506
|
+
BlobStore = class {
|
|
1507
|
+
constructor(dir, opts2 = {}) {
|
|
1508
|
+
this.dir = dir;
|
|
1509
|
+
this.maxBlobBytes = opts2.maxBlobBytes ?? DEFAULT_MAX_BLOB_BYTES;
|
|
1510
|
+
}
|
|
1511
|
+
dir;
|
|
1512
|
+
seen = /* @__PURE__ */ new Set();
|
|
1513
|
+
dirReady = false;
|
|
1514
|
+
maxBlobBytes;
|
|
1515
|
+
put(content) {
|
|
1516
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
1517
|
+
let stored = content;
|
|
1518
|
+
let truncated = false;
|
|
1519
|
+
if (bytes > this.maxBlobBytes) {
|
|
1520
|
+
stored = `${truncateUtf8(content, this.maxBlobBytes)}
|
|
1521
|
+
[truncated: ${bytes} bytes total]`;
|
|
1522
|
+
truncated = true;
|
|
1523
|
+
}
|
|
1524
|
+
const sha2563 = sha256Hex(stored);
|
|
1525
|
+
if (this.seen.has(sha2563)) return { sha256: sha2563, bytes, truncated, written: 0 };
|
|
1526
|
+
if (!this.dirReady) {
|
|
1527
|
+
mkdirSync2(this.dir, { recursive: true });
|
|
1528
|
+
this.dirReady = true;
|
|
1529
|
+
}
|
|
1530
|
+
const path = join8(this.dir, sha2563);
|
|
1531
|
+
let written = 0;
|
|
1532
|
+
if (!existsSync2(path)) {
|
|
1533
|
+
writeFileSync(path, stored, "utf8");
|
|
1534
|
+
written = Buffer.byteLength(stored, "utf8");
|
|
1535
|
+
}
|
|
1536
|
+
this.seen.add(sha2563);
|
|
1537
|
+
return { sha256: sha2563, bytes, truncated, written };
|
|
1538
|
+
}
|
|
1539
|
+
has(sha2563) {
|
|
1540
|
+
return this.seen.has(sha2563) || existsSync2(join8(this.dir, sha2563));
|
|
1541
|
+
}
|
|
1542
|
+
pathFor(sha2563) {
|
|
1543
|
+
return join8(this.dir, sha2563);
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1477
1549
|
// src/agent/session-state.ts
|
|
1478
1550
|
function emptySessionState(task = "") {
|
|
1479
1551
|
return {
|
|
@@ -1927,8 +1999,8 @@ var init_artifact_compaction = __esm({
|
|
|
1927
1999
|
|
|
1928
2000
|
// src/util/llm-dump.ts
|
|
1929
2001
|
import { homedir as homedir5 } from "os";
|
|
1930
|
-
import { join as
|
|
1931
|
-
import { mkdirSync as
|
|
2002
|
+
import { join as join9 } from "path";
|
|
2003
|
+
import { mkdirSync as mkdirSync3, appendFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1932
2004
|
function isLlmDumpEnabled() {
|
|
1933
2005
|
const raw = process.env.KIMIFLARE_DUMP_LLM;
|
|
1934
2006
|
if (raw === void 0) return false;
|
|
@@ -1938,11 +2010,11 @@ function isLlmDumpEnabled() {
|
|
|
1938
2010
|
function defaultDumpRoot() {
|
|
1939
2011
|
const override = process.env.KIMIFLARE_DUMP_LLM_DIR;
|
|
1940
2012
|
if (override && override.trim()) return override;
|
|
1941
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
1942
|
-
return
|
|
2013
|
+
const xdg = process.env.XDG_CONFIG_HOME || join9(homedir5(), ".config");
|
|
2014
|
+
return join9(xdg, "kimiflare", "llm-dumps");
|
|
1943
2015
|
}
|
|
1944
2016
|
function dumpDir(sessionId) {
|
|
1945
|
-
return
|
|
2017
|
+
return join9(defaultDumpRoot(), sessionId && sessionId.trim() ? sessionId : "nosession");
|
|
1946
2018
|
}
|
|
1947
2019
|
function messageChars(m) {
|
|
1948
2020
|
let chars = 0;
|
|
@@ -1999,12 +2071,12 @@ function writeLlmDump(record) {
|
|
|
1999
2071
|
if (!isLlmDumpEnabled()) return;
|
|
2000
2072
|
try {
|
|
2001
2073
|
const dir = dumpDir(record.meta.sessionId);
|
|
2002
|
-
|
|
2074
|
+
mkdirSync3(dir, { recursive: true });
|
|
2003
2075
|
const tsPart = safeSegment(record.meta.ts);
|
|
2004
2076
|
const turnPart = safeSegment(record.meta.turnId ?? "noturn");
|
|
2005
2077
|
const reqPart = safeSegment(record.meta.requestId);
|
|
2006
|
-
const file =
|
|
2007
|
-
|
|
2078
|
+
const file = join9(dir, `${tsPart}-${turnPart}-${reqPart}.json`);
|
|
2079
|
+
writeFileSync2(file, JSON.stringify(record, null, 2));
|
|
2008
2080
|
const summary = {
|
|
2009
2081
|
...record.meta,
|
|
2010
2082
|
...record.breakdown,
|
|
@@ -2014,7 +2086,7 @@ function writeLlmDump(record) {
|
|
|
2014
2086
|
usage: record.response.usage,
|
|
2015
2087
|
file
|
|
2016
2088
|
};
|
|
2017
|
-
appendFileSync(
|
|
2089
|
+
appendFileSync(join9(dir, "index.jsonl"), JSON.stringify(summary) + "\n");
|
|
2018
2090
|
} catch {
|
|
2019
2091
|
}
|
|
2020
2092
|
}
|
|
@@ -2257,41 +2329,89 @@ async function* runKimi(opts2) {
|
|
|
2257
2329
|
if (Object.keys(provider).length > 0) body.provider = provider;
|
|
2258
2330
|
if (vendorOf(opts2.model) === "anthropic") body.cache_control = { type: "ephemeral" };
|
|
2259
2331
|
}
|
|
2332
|
+
const dumpEnabled = isLlmDumpEnabled();
|
|
2333
|
+
const trace = opts2.trace?.enabled ? opts2.trace : null;
|
|
2334
|
+
const captured = dumpEnabled || trace ? captureRequest(body) : null;
|
|
2335
|
+
const response = {
|
|
2336
|
+
text: "",
|
|
2337
|
+
reasoning: "",
|
|
2338
|
+
reasoningDetails: [],
|
|
2339
|
+
toolCalls: [],
|
|
2340
|
+
finishReason: null,
|
|
2341
|
+
usage: null
|
|
2342
|
+
};
|
|
2260
2343
|
let dumpRecord = null;
|
|
2261
|
-
if (
|
|
2262
|
-
const dumpMessages = body.messages;
|
|
2263
|
-
const dumpTools = body.tools ?? [];
|
|
2264
|
-
const { messages: _m, tools: _t, ...params } = body;
|
|
2265
|
-
const dumpResponse = {
|
|
2266
|
-
text: "",
|
|
2267
|
-
reasoning: "",
|
|
2268
|
-
toolCalls: [],
|
|
2269
|
-
finishReason: null,
|
|
2270
|
-
usage: null
|
|
2271
|
-
};
|
|
2344
|
+
if (dumpEnabled && captured) {
|
|
2272
2345
|
dumpRecord = {
|
|
2273
2346
|
meta: {
|
|
2274
2347
|
requestId,
|
|
2275
2348
|
sessionId: opts2.sessionId ?? getLogSessionId(),
|
|
2276
|
-
turnId: getLogTurnId(),
|
|
2349
|
+
turnId: opts2.turnId ?? getLogTurnId(),
|
|
2277
2350
|
model: opts2.model,
|
|
2278
2351
|
url,
|
|
2279
2352
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
2280
2353
|
},
|
|
2281
2354
|
request: {
|
|
2282
|
-
system:
|
|
2283
|
-
messages:
|
|
2284
|
-
tools:
|
|
2285
|
-
params,
|
|
2286
|
-
rawSerialized:
|
|
2355
|
+
system: captured.messages.filter((m) => m.role === "system"),
|
|
2356
|
+
messages: captured.messages,
|
|
2357
|
+
tools: captured.tools,
|
|
2358
|
+
params: captured.params,
|
|
2359
|
+
rawSerialized: captured.rawSerialized
|
|
2287
2360
|
},
|
|
2288
|
-
breakdown:
|
|
2289
|
-
response
|
|
2361
|
+
breakdown: captured.breakdown,
|
|
2362
|
+
response
|
|
2290
2363
|
};
|
|
2291
2364
|
}
|
|
2365
|
+
const traceRequest = (attempt) => {
|
|
2366
|
+
if (!trace || !captured) return;
|
|
2367
|
+
trace.emit(
|
|
2368
|
+
"llm_request",
|
|
2369
|
+
{
|
|
2370
|
+
request_id: requestId,
|
|
2371
|
+
attempt,
|
|
2372
|
+
model: opts2.model,
|
|
2373
|
+
params: captured.params,
|
|
2374
|
+
request_blob: trace.blob(captured.rawSerialized) ?? "",
|
|
2375
|
+
messages_sha: captured.messagesSha,
|
|
2376
|
+
tools_sha: captured.toolsSha,
|
|
2377
|
+
estimated_tokens: captured.breakdown.estTokens
|
|
2378
|
+
},
|
|
2379
|
+
opts2.turnId ?? null
|
|
2380
|
+
);
|
|
2381
|
+
};
|
|
2382
|
+
const traceResponse = (startedAt2, error) => {
|
|
2383
|
+
if (!trace) return;
|
|
2384
|
+
const usage = response.usage;
|
|
2385
|
+
trace.emit(
|
|
2386
|
+
"llm_response",
|
|
2387
|
+
{
|
|
2388
|
+
request_id: requestId,
|
|
2389
|
+
text_blob: trace.blob(response.text) ?? "",
|
|
2390
|
+
...response.reasoning ? { reasoning_blob: trace.blob(response.reasoning) ?? "" } : {},
|
|
2391
|
+
...response.reasoningDetails.length ? { reasoning_details: response.reasoningDetails } : {},
|
|
2392
|
+
tool_calls: response.toolCalls.map((tc) => ({ id: tc.id, name: tc.name, arguments: tc.arguments })),
|
|
2393
|
+
finish_reason: response.finishReason,
|
|
2394
|
+
...usage ? {
|
|
2395
|
+
usage: {
|
|
2396
|
+
prompt_tokens: usage.prompt_tokens,
|
|
2397
|
+
completion_tokens: usage.completion_tokens,
|
|
2398
|
+
...usage.prompt_tokens_details?.cached_tokens !== void 0 ? { cached_tokens: usage.prompt_tokens_details.cached_tokens ?? void 0 } : {},
|
|
2399
|
+
...usage.cost !== void 0 ? { cost: usage.cost } : {}
|
|
2400
|
+
}
|
|
2401
|
+
} : {},
|
|
2402
|
+
...response.generationId ? { generation_id: response.generationId } : {},
|
|
2403
|
+
latency_ms: Math.round(performance.now() - startedAt2),
|
|
2404
|
+
aborted: opts2.signal?.aborted === true,
|
|
2405
|
+
...error !== void 0 ? { error } : {}
|
|
2406
|
+
},
|
|
2407
|
+
opts2.turnId ?? null
|
|
2408
|
+
);
|
|
2409
|
+
};
|
|
2292
2410
|
logger.debug("runKimi:request", { requestId, attempt: 0, model: opts2.model });
|
|
2293
2411
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
2294
2412
|
let res;
|
|
2413
|
+
const attemptStartedAt = performance.now();
|
|
2414
|
+
traceRequest(attempt);
|
|
2295
2415
|
try {
|
|
2296
2416
|
const headers = {
|
|
2297
2417
|
"Content-Type": "application/json",
|
|
@@ -2307,8 +2427,9 @@ async function* runKimi(opts2) {
|
|
|
2307
2427
|
signal: opts2.signal
|
|
2308
2428
|
});
|
|
2309
2429
|
} catch (fetchErr) {
|
|
2310
|
-
if (isAbortError(fetchErr)) throw fetchErr;
|
|
2311
2430
|
const msg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
|
|
2431
|
+
traceResponse(attemptStartedAt, msg);
|
|
2432
|
+
if (isAbortError(fetchErr)) throw fetchErr;
|
|
2312
2433
|
logger.warn("runKimi:fetch_error", { requestId, attempt, error: msg });
|
|
2313
2434
|
if (attempt < MAX_ATTEMPTS - 1) {
|
|
2314
2435
|
const delay = Math.random() * (500 * 2 ** attempt);
|
|
@@ -2340,6 +2461,7 @@ async function* runKimi(opts2) {
|
|
|
2340
2461
|
err?.code,
|
|
2341
2462
|
status
|
|
2342
2463
|
);
|
|
2464
|
+
traceResponse(attemptStartedAt, apiErr.message);
|
|
2343
2465
|
if (isRetryable(apiErr, attempt)) {
|
|
2344
2466
|
const isRateLimit = apiErr.httpStatus === 429;
|
|
2345
2467
|
const baseDelay = isRateLimit ? 2e3 : 500;
|
|
@@ -2351,18 +2473,27 @@ async function* runKimi(opts2) {
|
|
|
2351
2473
|
}
|
|
2352
2474
|
throw apiErr;
|
|
2353
2475
|
}
|
|
2354
|
-
if (!res.body)
|
|
2476
|
+
if (!res.body) {
|
|
2477
|
+
const emptyErr = new KimiApiError("kimiflare: empty response body", void 0, res.status);
|
|
2478
|
+
traceResponse(attemptStartedAt, emptyErr.message);
|
|
2479
|
+
throw emptyErr;
|
|
2480
|
+
}
|
|
2355
2481
|
logger.debug("runKimi:stream_start", { requestId });
|
|
2482
|
+
let streamError;
|
|
2356
2483
|
try {
|
|
2357
2484
|
for await (const ev of parseStream(res.body, opts2.signal, opts2.idleTimeoutMs, opts2.postFirstByteIdleTimeoutMs)) {
|
|
2358
|
-
if (
|
|
2485
|
+
if (captured) accumulateDumpResponse(response, ev);
|
|
2359
2486
|
yield ev;
|
|
2360
2487
|
}
|
|
2488
|
+
} catch (streamErr) {
|
|
2489
|
+
streamError = streamErr instanceof Error ? streamErr.message : String(streamErr);
|
|
2490
|
+
throw streamErr;
|
|
2361
2491
|
} finally {
|
|
2362
2492
|
if (dumpRecord) {
|
|
2363
2493
|
dumpRecord.meta.attempt = attempt;
|
|
2364
2494
|
writeLlmDump(dumpRecord);
|
|
2365
2495
|
}
|
|
2496
|
+
traceResponse(attemptStartedAt, streamError);
|
|
2366
2497
|
}
|
|
2367
2498
|
logger.debug("runKimi:stream_end", { requestId });
|
|
2368
2499
|
return;
|
|
@@ -2439,16 +2570,36 @@ function describeRequestyHttpError(model, status, msg) {
|
|
|
2439
2570
|
}
|
|
2440
2571
|
return msg;
|
|
2441
2572
|
}
|
|
2573
|
+
function captureRequest(body) {
|
|
2574
|
+
const messages = body.messages;
|
|
2575
|
+
const tools = body.tools ?? [];
|
|
2576
|
+
const { messages: _m, tools: _t, ...params } = body;
|
|
2577
|
+
return {
|
|
2578
|
+
messages,
|
|
2579
|
+
tools,
|
|
2580
|
+
params,
|
|
2581
|
+
rawSerialized: stableStringify(body, jsonReplacer),
|
|
2582
|
+
messagesSha: sha256Hex(stableStringify(messages, jsonReplacer)),
|
|
2583
|
+
toolsSha: sha256Hex(stableStringify(tools, jsonReplacer)),
|
|
2584
|
+
breakdown: computeBreakdown(messages, tools)
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2442
2587
|
function accumulateDumpResponse(resp, ev) {
|
|
2443
2588
|
switch (ev.type) {
|
|
2589
|
+
case "response_meta":
|
|
2590
|
+
if (ev.meta.generationId) resp.generationId = ev.meta.generationId;
|
|
2591
|
+
break;
|
|
2444
2592
|
case "text":
|
|
2445
2593
|
resp.text += ev.delta;
|
|
2446
2594
|
break;
|
|
2447
2595
|
case "reasoning":
|
|
2448
2596
|
resp.reasoning += ev.delta;
|
|
2449
2597
|
break;
|
|
2598
|
+
case "reasoning_details":
|
|
2599
|
+
resp.reasoningDetails = mergeReasoningDetails(resp.reasoningDetails, ev.details);
|
|
2600
|
+
break;
|
|
2450
2601
|
case "tool_call_complete":
|
|
2451
|
-
resp.toolCalls.push({ name: ev.name, arguments: ev.arguments });
|
|
2602
|
+
resp.toolCalls.push({ id: ev.id, name: ev.name, arguments: ev.arguments });
|
|
2452
2603
|
break;
|
|
2453
2604
|
case "usage":
|
|
2454
2605
|
resp.usage = ev.usage;
|
|
@@ -2674,6 +2825,7 @@ var init_client = __esm({
|
|
|
2674
2825
|
init_errors();
|
|
2675
2826
|
init_version();
|
|
2676
2827
|
init_messages();
|
|
2828
|
+
init_blobs();
|
|
2677
2829
|
init_logger();
|
|
2678
2830
|
init_log_sink();
|
|
2679
2831
|
init_llm_dump();
|
|
@@ -2746,7 +2898,7 @@ var init_recall_inject = __esm({
|
|
|
2746
2898
|
|
|
2747
2899
|
// src/storage-limits.ts
|
|
2748
2900
|
import { readdir as readdir2, stat as stat2, unlink } from "fs/promises";
|
|
2749
|
-
import { join as
|
|
2901
|
+
import { join as join10 } from "path";
|
|
2750
2902
|
async function listFilesByMtime(dir, pattern = /.*/) {
|
|
2751
2903
|
let entries;
|
|
2752
2904
|
try {
|
|
@@ -2757,7 +2909,7 @@ async function listFilesByMtime(dir, pattern = /.*/) {
|
|
|
2757
2909
|
const files = [];
|
|
2758
2910
|
for (const name of entries) {
|
|
2759
2911
|
if (!pattern.test(name)) continue;
|
|
2760
|
-
const p =
|
|
2912
|
+
const p = join10(dir, name);
|
|
2761
2913
|
try {
|
|
2762
2914
|
const s = await stat2(p);
|
|
2763
2915
|
if (s.isFile()) files.push({ path: p, mtime: s.mtime });
|
|
@@ -2834,7 +2986,13 @@ var init_storage_limits = __esm({
|
|
|
2834
2986
|
/** Memories older than this (days) are pruned. */
|
|
2835
2987
|
memoryMaxAgeDays: 90,
|
|
2836
2988
|
/** Max memories per repo. */
|
|
2837
|
-
memoryMaxEntries: 1e3
|
|
2989
|
+
memoryMaxEntries: 1e3,
|
|
2990
|
+
/** Session trace directories older than this (days) are pruned. */
|
|
2991
|
+
traceMaxAgeDays: 90,
|
|
2992
|
+
/** Max total size of all session traces (bytes). Oldest sessions go first. */
|
|
2993
|
+
traceMaxTotalBytes: 2 * 1024 * 1024 * 1024,
|
|
2994
|
+
/** Max size of a single session trace (bytes); recording stops at the cap. */
|
|
2995
|
+
traceMaxSessionBytes: 200 * 1024 * 1024
|
|
2838
2996
|
};
|
|
2839
2997
|
}
|
|
2840
2998
|
});
|
|
@@ -2842,13 +3000,13 @@ var init_storage_limits = __esm({
|
|
|
2842
3000
|
// src/cost-debug.ts
|
|
2843
3001
|
import { appendFile, mkdir as mkdir5 } from "fs/promises";
|
|
2844
3002
|
import { homedir as homedir6 } from "os";
|
|
2845
|
-
import { join as
|
|
3003
|
+
import { join as join11 } from "path";
|
|
2846
3004
|
function debugDir() {
|
|
2847
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
2848
|
-
return
|
|
3005
|
+
const xdg = process.env.XDG_DATA_HOME || join11(homedir6(), ".local", "share");
|
|
3006
|
+
return join11(xdg, "kimiflare");
|
|
2849
3007
|
}
|
|
2850
3008
|
function debugPath() {
|
|
2851
|
-
return
|
|
3009
|
+
return join11(debugDir(), "cost-debug.jsonl");
|
|
2852
3010
|
}
|
|
2853
3011
|
function now() {
|
|
2854
3012
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3562,7 +3720,7 @@ var require_isolated_vm = __commonJS({
|
|
|
3562
3720
|
});
|
|
3563
3721
|
|
|
3564
3722
|
// src/code-mode/sandbox.ts
|
|
3565
|
-
import { join as
|
|
3723
|
+
import { join as join12, dirname as dirname5 } from "path";
|
|
3566
3724
|
import { pathToFileURL } from "url";
|
|
3567
3725
|
function stripTypescript(code) {
|
|
3568
3726
|
let js = code;
|
|
@@ -3589,7 +3747,7 @@ async function loadTypescript(cwd) {
|
|
|
3589
3747
|
let dir = cwd;
|
|
3590
3748
|
while (dir !== dirname5(dir)) {
|
|
3591
3749
|
try {
|
|
3592
|
-
const tsPath =
|
|
3750
|
+
const tsPath = join12(dir, "node_modules", "typescript", "lib", "typescript.js");
|
|
3593
3751
|
return await import(pathToFileURL(tsPath).href);
|
|
3594
3752
|
} catch {
|
|
3595
3753
|
}
|
|
@@ -4433,11 +4591,11 @@ Prefer non-interactive flags when they exist (\`--yes\`, \`-m\`, \`--with-token\
|
|
|
4433
4591
|
|
|
4434
4592
|
// src/agent/system-prompt.ts
|
|
4435
4593
|
import { platform, release, homedir as homedir7 } from "os";
|
|
4436
|
-
import { basename, join as
|
|
4594
|
+
import { basename, join as join13 } from "path";
|
|
4437
4595
|
import { readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
4438
4596
|
function loadContextFile(cwd) {
|
|
4439
4597
|
for (const name of CONTEXT_FILENAMES) {
|
|
4440
|
-
const path =
|
|
4598
|
+
const path = join13(cwd, name);
|
|
4441
4599
|
try {
|
|
4442
4600
|
const s = statSync2(path);
|
|
4443
4601
|
if (!s.isFile() || s.size > MAX_CONTEXT_BYTES) continue;
|
|
@@ -4964,6 +5122,403 @@ var init_completion_check = __esm({
|
|
|
4964
5122
|
}
|
|
4965
5123
|
});
|
|
4966
5124
|
|
|
5125
|
+
// src/trace/paths.ts
|
|
5126
|
+
import { homedir as homedir8 } from "os";
|
|
5127
|
+
import { join as join14 } from "path";
|
|
5128
|
+
function defaultTraceRoot() {
|
|
5129
|
+
const xdg = process.env.XDG_DATA_HOME || join14(homedir8(), ".local", "share");
|
|
5130
|
+
return join14(xdg, "kimiflare", "traces");
|
|
5131
|
+
}
|
|
5132
|
+
function traceRoot(configRoot) {
|
|
5133
|
+
const env2 = process.env.KIMIFLARE_TRACE_DIR;
|
|
5134
|
+
if (env2 && env2.trim()) return env2.trim();
|
|
5135
|
+
if (configRoot && configRoot.trim()) return configRoot.trim();
|
|
5136
|
+
return defaultTraceRoot();
|
|
5137
|
+
}
|
|
5138
|
+
function safeSessionSegment(sessionId) {
|
|
5139
|
+
const cleaned = sessionId.replace(/[\\/\0]/g, "_").replace(/^\.+$/, "_");
|
|
5140
|
+
return cleaned || "session";
|
|
5141
|
+
}
|
|
5142
|
+
function sessionTraceDir(sessionId, root) {
|
|
5143
|
+
return join14(traceRoot(root), safeSessionSegment(sessionId));
|
|
5144
|
+
}
|
|
5145
|
+
function traceFilePath(sessionDir) {
|
|
5146
|
+
return join14(sessionDir, TRACE_FILE_NAME);
|
|
5147
|
+
}
|
|
5148
|
+
function blobsDir(sessionDir) {
|
|
5149
|
+
return join14(sessionDir, BLOBS_DIR_NAME);
|
|
5150
|
+
}
|
|
5151
|
+
var TRACE_FILE_NAME, BLOBS_DIR_NAME;
|
|
5152
|
+
var init_paths = __esm({
|
|
5153
|
+
"src/trace/paths.ts"() {
|
|
5154
|
+
"use strict";
|
|
5155
|
+
TRACE_FILE_NAME = "trace.jsonl";
|
|
5156
|
+
BLOBS_DIR_NAME = "blobs";
|
|
5157
|
+
}
|
|
5158
|
+
});
|
|
5159
|
+
|
|
5160
|
+
// src/trace/redact.ts
|
|
5161
|
+
function redactLight(text) {
|
|
5162
|
+
if (!text) return text;
|
|
5163
|
+
let out = text.replace(PEM_BLOCK, "<redacted-key>");
|
|
5164
|
+
for (const re of TOKEN_SHAPES) out = out.replace(re, "<redacted>");
|
|
5165
|
+
out = out.replace(KEY_VALUE, "$1$2<redacted>");
|
|
5166
|
+
return out;
|
|
5167
|
+
}
|
|
5168
|
+
function isSecretConfigKey(key) {
|
|
5169
|
+
const lower = key.toLowerCase();
|
|
5170
|
+
return SECRET_KEY_SUFFIXES.some((suffix) => lower.endsWith(suffix));
|
|
5171
|
+
}
|
|
5172
|
+
function redactConfig(value) {
|
|
5173
|
+
return redactValue(value, true);
|
|
5174
|
+
}
|
|
5175
|
+
function redactDeep(value) {
|
|
5176
|
+
return redactValue(value, false);
|
|
5177
|
+
}
|
|
5178
|
+
function redactValue(value, redactSecretKeys) {
|
|
5179
|
+
if (typeof value === "string") return redactLight(value);
|
|
5180
|
+
if (Array.isArray(value)) return value.map((v) => redactValue(v, redactSecretKeys));
|
|
5181
|
+
if (value && typeof value === "object") {
|
|
5182
|
+
const out = {};
|
|
5183
|
+
for (const [k, v] of Object.entries(value)) {
|
|
5184
|
+
if (v === void 0) continue;
|
|
5185
|
+
if (redactSecretKeys && isSecretConfigKey(k) && v !== null && v !== "") {
|
|
5186
|
+
out[k] = "<redacted>";
|
|
5187
|
+
} else {
|
|
5188
|
+
out[k] = redactValue(v, redactSecretKeys);
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
return out;
|
|
5192
|
+
}
|
|
5193
|
+
return value;
|
|
5194
|
+
}
|
|
5195
|
+
var PEM_BLOCK, TOKEN_SHAPES, KEY_VALUE, SECRET_KEY_SUFFIXES;
|
|
5196
|
+
var init_redact = __esm({
|
|
5197
|
+
"src/trace/redact.ts"() {
|
|
5198
|
+
"use strict";
|
|
5199
|
+
PEM_BLOCK = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
|
|
5200
|
+
TOKEN_SHAPES = [
|
|
5201
|
+
/sk-or-v1-[A-Za-z0-9]+/g,
|
|
5202
|
+
/ghp_[A-Za-z0-9]+/g,
|
|
5203
|
+
/github_pat_[A-Za-z0-9_]+/g,
|
|
5204
|
+
/Bearer [A-Za-z0-9._-]+/g,
|
|
5205
|
+
/Basic [A-Za-z0-9+/=]+/g
|
|
5206
|
+
];
|
|
5207
|
+
KEY_VALUE = /(api[_-]?key|secret|token|password|authorization)(["']?\s*[:=]\s*["']?)[^\s"']+/gi;
|
|
5208
|
+
SECRET_KEY_SUFFIXES = ["apikey", "token", "secret", "password"];
|
|
5209
|
+
}
|
|
5210
|
+
});
|
|
5211
|
+
|
|
5212
|
+
// src/trace/types.ts
|
|
5213
|
+
var TRACE_SCHEMA_VERSION;
|
|
5214
|
+
var init_types = __esm({
|
|
5215
|
+
"src/trace/types.ts"() {
|
|
5216
|
+
"use strict";
|
|
5217
|
+
TRACE_SCHEMA_VERSION = 1;
|
|
5218
|
+
}
|
|
5219
|
+
});
|
|
5220
|
+
|
|
5221
|
+
// src/trace/recorder.ts
|
|
5222
|
+
import { randomUUID } from "crypto";
|
|
5223
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3 } from "fs";
|
|
5224
|
+
function createNoopRecorder(sessionId = "") {
|
|
5225
|
+
return new NoopRecorder(sessionId);
|
|
5226
|
+
}
|
|
5227
|
+
function makeTurnId() {
|
|
5228
|
+
return randomUUID();
|
|
5229
|
+
}
|
|
5230
|
+
function createRecorder(opts2) {
|
|
5231
|
+
if (!opts2.enabled) return new NoopRecorder(opts2.sessionId);
|
|
5232
|
+
const dir = sessionTraceDir(opts2.sessionId, opts2.root);
|
|
5233
|
+
return new FileTraceRecorder(opts2.sessionId, dir, {
|
|
5234
|
+
maxBlobBytes: opts2.maxBlobBytes,
|
|
5235
|
+
maxSessionBytes: opts2.maxSessionBytes
|
|
5236
|
+
});
|
|
5237
|
+
}
|
|
5238
|
+
var NoopRecorder, FileTraceRecorder;
|
|
5239
|
+
var init_recorder = __esm({
|
|
5240
|
+
"src/trace/recorder.ts"() {
|
|
5241
|
+
"use strict";
|
|
5242
|
+
init_logger();
|
|
5243
|
+
init_log_sink();
|
|
5244
|
+
init_storage_limits();
|
|
5245
|
+
init_blobs();
|
|
5246
|
+
init_paths();
|
|
5247
|
+
init_redact();
|
|
5248
|
+
init_types();
|
|
5249
|
+
NoopRecorder = class {
|
|
5250
|
+
constructor(sessionId) {
|
|
5251
|
+
this.sessionId = sessionId;
|
|
5252
|
+
}
|
|
5253
|
+
sessionId;
|
|
5254
|
+
enabled = false;
|
|
5255
|
+
dir = null;
|
|
5256
|
+
emit() {
|
|
5257
|
+
}
|
|
5258
|
+
blob() {
|
|
5259
|
+
return null;
|
|
5260
|
+
}
|
|
5261
|
+
flush() {
|
|
5262
|
+
return Promise.resolve();
|
|
5263
|
+
}
|
|
5264
|
+
};
|
|
5265
|
+
FileTraceRecorder = class {
|
|
5266
|
+
constructor(sessionId, dir, opts2) {
|
|
5267
|
+
this.sessionId = sessionId;
|
|
5268
|
+
this.dir = dir;
|
|
5269
|
+
this.file = traceFilePath(dir);
|
|
5270
|
+
this.blobs = new BlobStore(blobsDir(dir), { maxBlobBytes: opts2.maxBlobBytes });
|
|
5271
|
+
this.maxSessionBytes = opts2.maxSessionBytes ?? RETENTION.traceMaxSessionBytes;
|
|
5272
|
+
}
|
|
5273
|
+
sessionId;
|
|
5274
|
+
enabled = true;
|
|
5275
|
+
dir;
|
|
5276
|
+
file;
|
|
5277
|
+
blobs;
|
|
5278
|
+
maxSessionBytes;
|
|
5279
|
+
seq = 0;
|
|
5280
|
+
bytesWritten = 0;
|
|
5281
|
+
dirReady = false;
|
|
5282
|
+
emit(type, payload, turnId) {
|
|
5283
|
+
if (!this.enabled) return;
|
|
5284
|
+
try {
|
|
5285
|
+
const event = {
|
|
5286
|
+
v: TRACE_SCHEMA_VERSION,
|
|
5287
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5288
|
+
seq: this.seq,
|
|
5289
|
+
session_id: this.sessionId,
|
|
5290
|
+
turn_id: turnId === void 0 ? getLogTurnId() : turnId,
|
|
5291
|
+
type,
|
|
5292
|
+
payload: redactDeep(payload)
|
|
5293
|
+
};
|
|
5294
|
+
if (!this.dirReady) {
|
|
5295
|
+
mkdirSync4(this.dir, { recursive: true });
|
|
5296
|
+
this.dirReady = true;
|
|
5297
|
+
this.resumeSeq();
|
|
5298
|
+
event.seq = this.seq;
|
|
5299
|
+
}
|
|
5300
|
+
const line = JSON.stringify(event) + "\n";
|
|
5301
|
+
if (!this.ensureBudget(Buffer.byteLength(line, "utf8"))) return;
|
|
5302
|
+
appendFileSync2(this.file, line, "utf8");
|
|
5303
|
+
this.seq++;
|
|
5304
|
+
} catch (err) {
|
|
5305
|
+
this.fail(err);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
blob(content) {
|
|
5309
|
+
if (!this.enabled) return null;
|
|
5310
|
+
try {
|
|
5311
|
+
const result2 = this.blobs.put(redactLight(content));
|
|
5312
|
+
if (result2.written > 0) this.ensureBudget(result2.written);
|
|
5313
|
+
return result2.sha256;
|
|
5314
|
+
} catch (err) {
|
|
5315
|
+
this.fail(err);
|
|
5316
|
+
return null;
|
|
5317
|
+
}
|
|
5318
|
+
}
|
|
5319
|
+
flush() {
|
|
5320
|
+
return Promise.resolve();
|
|
5321
|
+
}
|
|
5322
|
+
/** Continue `seq` after the lines already in an existing trace file. */
|
|
5323
|
+
resumeSeq() {
|
|
5324
|
+
if (!existsSync3(this.file)) return;
|
|
5325
|
+
const existing = readFileSync3(this.file, "utf8");
|
|
5326
|
+
let lines = 0;
|
|
5327
|
+
for (let i = 0; i < existing.length; i++) if (existing.charCodeAt(i) === 10) lines++;
|
|
5328
|
+
this.seq = lines;
|
|
5329
|
+
this.bytesWritten += Buffer.byteLength(existing, "utf8");
|
|
5330
|
+
}
|
|
5331
|
+
/** Account for `bytes`; returns false (and disables) once the session cap is exceeded. */
|
|
5332
|
+
ensureBudget(bytes) {
|
|
5333
|
+
this.bytesWritten += bytes;
|
|
5334
|
+
if (this.bytesWritten > this.maxSessionBytes) {
|
|
5335
|
+
this.enabled = false;
|
|
5336
|
+
logger.warn("trace:session_cap", {
|
|
5337
|
+
session_id: this.sessionId,
|
|
5338
|
+
dir: this.dir,
|
|
5339
|
+
max_bytes: this.maxSessionBytes
|
|
5340
|
+
});
|
|
5341
|
+
return false;
|
|
5342
|
+
}
|
|
5343
|
+
return true;
|
|
5344
|
+
}
|
|
5345
|
+
fail(err) {
|
|
5346
|
+
this.enabled = false;
|
|
5347
|
+
logger.warn("trace:write_failed", {
|
|
5348
|
+
session_id: this.sessionId,
|
|
5349
|
+
dir: this.dir,
|
|
5350
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5351
|
+
});
|
|
5352
|
+
}
|
|
5353
|
+
};
|
|
5354
|
+
}
|
|
5355
|
+
});
|
|
5356
|
+
|
|
5357
|
+
// src/cost-attribution/git-diff.ts
|
|
5358
|
+
import { execFile } from "child_process";
|
|
5359
|
+
import { promisify } from "util";
|
|
5360
|
+
function parseNumstat(raw) {
|
|
5361
|
+
const entries = [];
|
|
5362
|
+
for (const line of raw.split("\n")) {
|
|
5363
|
+
if (!line.trim()) continue;
|
|
5364
|
+
const parts = line.split(" ");
|
|
5365
|
+
if (parts.length < 3) continue;
|
|
5366
|
+
const ins = parseInt(parts[0], 10);
|
|
5367
|
+
const del = parseInt(parts[1], 10);
|
|
5368
|
+
entries.push({
|
|
5369
|
+
file: parts.slice(2).join(" "),
|
|
5370
|
+
insertions: Number.isNaN(ins) ? 0 : ins,
|
|
5371
|
+
deletions: Number.isNaN(del) ? 0 : del
|
|
5372
|
+
});
|
|
5373
|
+
}
|
|
5374
|
+
return entries;
|
|
5375
|
+
}
|
|
5376
|
+
async function gitNumstat(cwd, opts2 = {}) {
|
|
5377
|
+
let raw;
|
|
5378
|
+
try {
|
|
5379
|
+
const args = ["diff", "--numstat"];
|
|
5380
|
+
if (opts2.againstHead) args.push("HEAD");
|
|
5381
|
+
args.push("--", ".");
|
|
5382
|
+
try {
|
|
5383
|
+
raw = (await execFileAsync("git", args, { cwd, timeout: 5e3 })).stdout;
|
|
5384
|
+
} catch (err) {
|
|
5385
|
+
if (!opts2.againstHead) throw err;
|
|
5386
|
+
raw = (await execFileAsync("git", ["diff", "--numstat", "--", "."], { cwd, timeout: 5e3 })).stdout;
|
|
5387
|
+
}
|
|
5388
|
+
} catch {
|
|
5389
|
+
return null;
|
|
5390
|
+
}
|
|
5391
|
+
const entries = parseNumstat(raw);
|
|
5392
|
+
if (opts2.includeUntracked) {
|
|
5393
|
+
try {
|
|
5394
|
+
const { stdout } = await execFileAsync("git", ["ls-files", "--others", "--exclude-standard"], { cwd, timeout: 5e3 });
|
|
5395
|
+
const { readFile: readFile32, stat: stat11 } = await import("fs/promises");
|
|
5396
|
+
const { join: join54 } = await import("path");
|
|
5397
|
+
for (const file of stdout.split("\n").filter(Boolean)) {
|
|
5398
|
+
let lines = 0;
|
|
5399
|
+
try {
|
|
5400
|
+
const path = join54(cwd, file);
|
|
5401
|
+
const size = (await stat11(path)).size;
|
|
5402
|
+
if (size > 0 && size <= 1024 * 1024) {
|
|
5403
|
+
const text = await readFile32(path, "utf8");
|
|
5404
|
+
lines = text.split("\n").length - (text.endsWith("\n") ? 1 : 0);
|
|
5405
|
+
}
|
|
5406
|
+
} catch {
|
|
5407
|
+
}
|
|
5408
|
+
entries.push({ file, insertions: lines, deletions: 0 });
|
|
5409
|
+
raw += `${lines} 0 ${file}
|
|
5410
|
+
`;
|
|
5411
|
+
}
|
|
5412
|
+
} catch {
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
return { raw, entries };
|
|
5416
|
+
}
|
|
5417
|
+
async function gitPatch(cwd, maxBytes) {
|
|
5418
|
+
try {
|
|
5419
|
+
let stdout;
|
|
5420
|
+
try {
|
|
5421
|
+
stdout = (await execFileAsync("git", ["diff", "HEAD", "--", "."], { cwd, timeout: 1e4, maxBuffer: maxBytes * 4 })).stdout;
|
|
5422
|
+
} catch {
|
|
5423
|
+
stdout = (await execFileAsync("git", ["diff", "--", "."], { cwd, timeout: 1e4, maxBuffer: maxBytes * 4 })).stdout;
|
|
5424
|
+
}
|
|
5425
|
+
return Buffer.byteLength(stdout, "utf8") <= maxBytes ? stdout : null;
|
|
5426
|
+
} catch {
|
|
5427
|
+
return null;
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
var execFileAsync;
|
|
5431
|
+
var init_git_diff = __esm({
|
|
5432
|
+
"src/cost-attribution/git-diff.ts"() {
|
|
5433
|
+
"use strict";
|
|
5434
|
+
execFileAsync = promisify(execFile);
|
|
5435
|
+
}
|
|
5436
|
+
});
|
|
5437
|
+
|
|
5438
|
+
// src/trace/git-delta.ts
|
|
5439
|
+
function toMap(entries) {
|
|
5440
|
+
return new Map(entries.map((e) => [e.file, e]));
|
|
5441
|
+
}
|
|
5442
|
+
function diffNumstat(before, after) {
|
|
5443
|
+
const b = toMap(before);
|
|
5444
|
+
const a = toMap(after);
|
|
5445
|
+
const files = /* @__PURE__ */ new Set([...b.keys(), ...a.keys()]);
|
|
5446
|
+
let insertions = 0;
|
|
5447
|
+
let deletions = 0;
|
|
5448
|
+
const changed = [];
|
|
5449
|
+
for (const file of files) {
|
|
5450
|
+
const x = b.get(file) ?? { file, insertions: 0, deletions: 0 };
|
|
5451
|
+
const y = a.get(file) ?? { file, insertions: 0, deletions: 0 };
|
|
5452
|
+
const di = y.insertions - x.insertions;
|
|
5453
|
+
const dd = y.deletions - x.deletions;
|
|
5454
|
+
if (di === 0 && dd === 0) continue;
|
|
5455
|
+
changed.push(file);
|
|
5456
|
+
insertions += Math.max(0, di);
|
|
5457
|
+
deletions += Math.max(0, dd);
|
|
5458
|
+
}
|
|
5459
|
+
changed.sort();
|
|
5460
|
+
return { files_changed: changed.length, insertions, deletions, files: changed };
|
|
5461
|
+
}
|
|
5462
|
+
function rememberGitDelta(sessionId, turnId, files) {
|
|
5463
|
+
if (files.length === 0) return;
|
|
5464
|
+
lastDeltaBySession.set(sessionId, { turnId, files });
|
|
5465
|
+
}
|
|
5466
|
+
function detectGitRevert(sessionId, command) {
|
|
5467
|
+
const last = lastDeltaBySession.get(sessionId);
|
|
5468
|
+
if (!last) return null;
|
|
5469
|
+
if (!REVERT_COMMAND.test(command)) return null;
|
|
5470
|
+
const isStash = /git\s+stash\b/.test(command) && !/git\s+stash\s+(pop|apply|list|show|drop)\b/.test(command);
|
|
5471
|
+
if (isStash) return { turnId: last.turnId };
|
|
5472
|
+
if (/git\s+(checkout\s+--|restore)\s*$/.test(command.trim())) return null;
|
|
5473
|
+
const hit = last.files.some((file) => {
|
|
5474
|
+
const base = file.slice(file.lastIndexOf("/") + 1);
|
|
5475
|
+
return command.includes(file) || base && new RegExp(`(^|[\\s/'"])${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(['"\\s]|$)`).test(command);
|
|
5476
|
+
});
|
|
5477
|
+
if (hit) return { turnId: last.turnId };
|
|
5478
|
+
if (/git\s+restore\s+(\.|--staged\s+\.)(\s|$)/.test(command) || /git\s+checkout\s+--\s+\.(\s|$)/.test(command)) return { turnId: last.turnId };
|
|
5479
|
+
return null;
|
|
5480
|
+
}
|
|
5481
|
+
async function beginGitDelta(opts2) {
|
|
5482
|
+
if (!opts2.trace.enabled) return null;
|
|
5483
|
+
const before = await gitNumstat(opts2.cwd, { againstHead: true, includeUntracked: true });
|
|
5484
|
+
if (!before) return null;
|
|
5485
|
+
let done = false;
|
|
5486
|
+
return {
|
|
5487
|
+
async finish() {
|
|
5488
|
+
if (done) return;
|
|
5489
|
+
done = true;
|
|
5490
|
+
const after = await gitNumstat(opts2.cwd, { againstHead: true, includeUntracked: true });
|
|
5491
|
+
if (!after || !opts2.trace.enabled) return;
|
|
5492
|
+
const delta = diffNumstat(before.entries, after.entries);
|
|
5493
|
+
const patch = delta.files_changed > 0 ? await gitPatch(opts2.cwd, MAX_PATCH_BYTES) : null;
|
|
5494
|
+
opts2.trace.emit(
|
|
5495
|
+
"outcome",
|
|
5496
|
+
{
|
|
5497
|
+
kind: "git_delta",
|
|
5498
|
+
turn_id: opts2.turnId,
|
|
5499
|
+
files_changed: delta.files_changed,
|
|
5500
|
+
insertions: delta.insertions,
|
|
5501
|
+
deletions: delta.deletions,
|
|
5502
|
+
numstat_blob: opts2.trace.blob(after.raw) ?? "",
|
|
5503
|
+
...patch ? { patch_blob: opts2.trace.blob(patch) ?? "" } : {}
|
|
5504
|
+
},
|
|
5505
|
+
opts2.turnId
|
|
5506
|
+
);
|
|
5507
|
+
if (opts2.sessionId) rememberGitDelta(opts2.sessionId, opts2.turnId, delta.files);
|
|
5508
|
+
}
|
|
5509
|
+
};
|
|
5510
|
+
}
|
|
5511
|
+
var MAX_PATCH_BYTES, lastDeltaBySession, REVERT_COMMAND;
|
|
5512
|
+
var init_git_delta = __esm({
|
|
5513
|
+
"src/trace/git-delta.ts"() {
|
|
5514
|
+
"use strict";
|
|
5515
|
+
init_git_diff();
|
|
5516
|
+
MAX_PATCH_BYTES = 512 * 1024;
|
|
5517
|
+
lastDeltaBySession = /* @__PURE__ */ new Map();
|
|
5518
|
+
REVERT_COMMAND = /(^|[\s;&|(])git\s+(checkout\s+--|restore\b|stash\b)/;
|
|
5519
|
+
}
|
|
5520
|
+
});
|
|
5521
|
+
|
|
4967
5522
|
// src/agent/loop.ts
|
|
4968
5523
|
function getSessionWebFetchHistory(sessionId) {
|
|
4969
5524
|
const key = sessionId ?? "default";
|
|
@@ -5002,7 +5557,90 @@ function raceWithSignal(promise, signal) {
|
|
|
5002
5557
|
]);
|
|
5003
5558
|
}
|
|
5004
5559
|
async function runAgentTurn(opts2) {
|
|
5005
|
-
const
|
|
5560
|
+
const tr = {
|
|
5561
|
+
trace: opts2.trace ?? createNoopRecorder(opts2.sessionId ?? ""),
|
|
5562
|
+
turnId: opts2.turnId ?? makeTurnId(),
|
|
5563
|
+
startedAt: performance.now(),
|
|
5564
|
+
toolsUsed: [],
|
|
5565
|
+
partialText: "",
|
|
5566
|
+
finalText: null,
|
|
5567
|
+
costUsd: 0,
|
|
5568
|
+
endReason: null
|
|
5569
|
+
};
|
|
5570
|
+
const endTurn = (reason, finalText) => {
|
|
5571
|
+
if (!tr.trace.enabled) return;
|
|
5572
|
+
const finalBlob = finalText ? tr.trace.blob(finalText) : null;
|
|
5573
|
+
tr.trace.emit(
|
|
5574
|
+
"turn_end",
|
|
5575
|
+
{
|
|
5576
|
+
reason,
|
|
5577
|
+
tools_used: tr.toolsUsed,
|
|
5578
|
+
...tr.costUsd > 0 ? { cost_usd: tr.costUsd } : {},
|
|
5579
|
+
duration_ms: Math.round(performance.now() - tr.startedAt),
|
|
5580
|
+
...finalBlob ? { final_text_blob: finalBlob } : {}
|
|
5581
|
+
},
|
|
5582
|
+
tr.turnId
|
|
5583
|
+
);
|
|
5584
|
+
};
|
|
5585
|
+
const gitDelta = await beginGitDelta({ trace: tr.trace, turnId: tr.turnId, sessionId: opts2.sessionId, cwd: opts2.cwd }).catch(
|
|
5586
|
+
() => null
|
|
5587
|
+
);
|
|
5588
|
+
try {
|
|
5589
|
+
await runAgentTurnInner(opts2, tr);
|
|
5590
|
+
endTurn(tr.endReason ?? "complete", tr.finalText);
|
|
5591
|
+
} catch (err) {
|
|
5592
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
5593
|
+
endTurn("aborted", tr.partialText || tr.finalText);
|
|
5594
|
+
} else if (err instanceof AgentLoopError || err instanceof BudgetExhaustedError) {
|
|
5595
|
+
endTurn("guardrail", tr.finalText);
|
|
5596
|
+
} else {
|
|
5597
|
+
endTurn("error", tr.partialText || tr.finalText);
|
|
5598
|
+
}
|
|
5599
|
+
throw err;
|
|
5600
|
+
} finally {
|
|
5601
|
+
await gitDelta?.finish().catch(() => void 0);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
async function runAgentTurnInner(opts2, tr) {
|
|
5605
|
+
const turnStart = tr.startedAt;
|
|
5606
|
+
const { trace, turnId } = tr;
|
|
5607
|
+
const notifyGuardrail = (ev) => {
|
|
5608
|
+
if (ev.kind !== "completion_nudge") trace.emit("guardrail", { kind: ev.kind, message: ev.message }, turnId);
|
|
5609
|
+
opts2.callbacks.onGuardrail?.(ev);
|
|
5610
|
+
};
|
|
5611
|
+
const traceBlocked = (tc, result2, errorCode, redirected = false) => {
|
|
5612
|
+
if (!trace.enabled) return;
|
|
5613
|
+
const argsBlob = trace.blob(tc.function.arguments) ?? "";
|
|
5614
|
+
trace.emit(
|
|
5615
|
+
"tool_call",
|
|
5616
|
+
{
|
|
5617
|
+
tool_call_id: tc.id,
|
|
5618
|
+
name: tc.function.name,
|
|
5619
|
+
args_blob: argsBlob,
|
|
5620
|
+
...tc.function.arguments.length <= 2048 ? { args_inline: parseArgsObject(tc.function.arguments) } : {},
|
|
5621
|
+
needs_permission: false,
|
|
5622
|
+
code_mode_redirected: redirected
|
|
5623
|
+
},
|
|
5624
|
+
turnId
|
|
5625
|
+
);
|
|
5626
|
+
const blob = trace.blob(result2.content) ?? "";
|
|
5627
|
+
const bytes = Buffer.byteLength(result2.content, "utf8");
|
|
5628
|
+
trace.emit(
|
|
5629
|
+
"tool_result",
|
|
5630
|
+
{
|
|
5631
|
+
tool_call_id: tc.id,
|
|
5632
|
+
name: tc.function.name,
|
|
5633
|
+
ok: false,
|
|
5634
|
+
error_code: errorCode,
|
|
5635
|
+
raw_blob: blob,
|
|
5636
|
+
raw_bytes: bytes,
|
|
5637
|
+
reduced_blob: blob,
|
|
5638
|
+
reduced_bytes: bytes,
|
|
5639
|
+
duration_ms: 0
|
|
5640
|
+
},
|
|
5641
|
+
turnId
|
|
5642
|
+
);
|
|
5643
|
+
};
|
|
5006
5644
|
logger.info("turn:start", { sessionId: opts2.sessionId, codeMode: opts2.codeMode ?? false });
|
|
5007
5645
|
const max = opts2.maxToolIterations ?? 200;
|
|
5008
5646
|
const limitBehavior = opts2.toolLimitBehavior ?? (opts2.continueOnLimit ? "continue" : "throw");
|
|
@@ -5164,6 +5802,18 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5164
5802
|
const webFetchHistory = getSessionWebFetchHistory(opts2.sessionId);
|
|
5165
5803
|
let webFetchesThisTurn = 0;
|
|
5166
5804
|
let codeModeRedirects = 0;
|
|
5805
|
+
const noteCodeModeRedirect = () => {
|
|
5806
|
+
if (codeModeRedirects === MAX_CODE_MODE_REDIRECTS) {
|
|
5807
|
+
trace.emit(
|
|
5808
|
+
"guardrail",
|
|
5809
|
+
{
|
|
5810
|
+
kind: "code_mode_redirect_cap",
|
|
5811
|
+
message: `Code Mode redirect cap (${MAX_CODE_MODE_REDIRECTS}) reached \u2014 direct tool calls now execute normally.`
|
|
5812
|
+
},
|
|
5813
|
+
turnId
|
|
5814
|
+
);
|
|
5815
|
+
}
|
|
5816
|
+
};
|
|
5167
5817
|
const MAX_WEB_FETCH_PER_TURN = 5;
|
|
5168
5818
|
let cumulativePromptTokens = 0;
|
|
5169
5819
|
let iter = 0;
|
|
@@ -5201,7 +5851,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5201
5851
|
role: "system",
|
|
5202
5852
|
content: `You have reached the hard safety ceiling of ${hardCeiling} tool iterations for this turn. Do not call any more tools. Summarize what you accomplished, what remains unfinished, and the concrete next steps needed to complete the task.`
|
|
5203
5853
|
});
|
|
5204
|
-
|
|
5854
|
+
notifyGuardrail({
|
|
5205
5855
|
kind: "limit_ceiling",
|
|
5206
5856
|
message: `Tool-iteration ceiling (${hardCeiling}) reached \u2014 wrapping up the turn with a summary.`,
|
|
5207
5857
|
totalIterations: totalIter
|
|
@@ -5213,17 +5863,18 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5213
5863
|
content: "You have reached the tool-call limit for this session. The counter has been reset so you can continue working. Please proceed with your task."
|
|
5214
5864
|
});
|
|
5215
5865
|
iter = 0;
|
|
5216
|
-
|
|
5866
|
+
notifyGuardrail({
|
|
5217
5867
|
kind: "limit_reset",
|
|
5218
5868
|
message: `Tool-call limit (${max}) reached \u2014 counter reset automatically (${totalIter}/${hardCeiling} total).`,
|
|
5219
5869
|
totalIterations: totalIter
|
|
5220
5870
|
});
|
|
5221
5871
|
} else if (limitBehavior === "stop") {
|
|
5222
|
-
|
|
5872
|
+
notifyGuardrail({
|
|
5223
5873
|
kind: "limit_stopped",
|
|
5224
5874
|
message: `Tool-call limit (${max}) reached \u2014 turn stopped.`,
|
|
5225
5875
|
totalIterations: totalIter
|
|
5226
5876
|
});
|
|
5877
|
+
tr.endReason = "guardrail";
|
|
5227
5878
|
await fireStopHook();
|
|
5228
5879
|
return;
|
|
5229
5880
|
} else {
|
|
@@ -5240,6 +5891,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5240
5891
|
let reasoning = "";
|
|
5241
5892
|
let responseMeta;
|
|
5242
5893
|
let reasoningDetails = [];
|
|
5894
|
+
tr.partialText = "";
|
|
5243
5895
|
opts2.callbacks.onAssistantStart?.();
|
|
5244
5896
|
const stripReasoning = process.env.KIMIFLARE_STRIP_REASONING === "1";
|
|
5245
5897
|
const shadowStrip = process.env.KIMIFLARE_SHADOW_STRIP === "1";
|
|
@@ -5286,6 +5938,17 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5286
5938
|
budgetTokens: maxPromptTokens
|
|
5287
5939
|
};
|
|
5288
5940
|
logger.info("context:compacted", { sessionId: opts2.sessionId, ...info });
|
|
5941
|
+
trace.emit(
|
|
5942
|
+
"compaction",
|
|
5943
|
+
{
|
|
5944
|
+
method: "preflight",
|
|
5945
|
+
tokens_before: info.tokensBefore,
|
|
5946
|
+
tokens_after: info.tokensAfter,
|
|
5947
|
+
turns_removed: info.turnsRemoved,
|
|
5948
|
+
artifacts_archived: info.artifactsArchived
|
|
5949
|
+
},
|
|
5950
|
+
turnId
|
|
5951
|
+
);
|
|
5289
5952
|
opts2.callbacks.onCompacted?.(info);
|
|
5290
5953
|
}
|
|
5291
5954
|
}
|
|
@@ -5305,7 +5968,9 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5305
5968
|
reasoningEffort: opts2.reasoningEffort,
|
|
5306
5969
|
sessionId: opts2.sessionId,
|
|
5307
5970
|
idleTimeoutMs: opts2.idleTimeoutMs ?? 6e4,
|
|
5308
|
-
postFirstByteIdleTimeoutMs: opts2.postFirstByteIdleTimeoutMs
|
|
5971
|
+
postFirstByteIdleTimeoutMs: opts2.postFirstByteIdleTimeoutMs,
|
|
5972
|
+
trace,
|
|
5973
|
+
turnId
|
|
5309
5974
|
});
|
|
5310
5975
|
let gotFirstChunk = false;
|
|
5311
5976
|
for await (const ev of events) {
|
|
@@ -5327,6 +5992,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5327
5992
|
break;
|
|
5328
5993
|
case "text":
|
|
5329
5994
|
content += ev.delta;
|
|
5995
|
+
tr.partialText = content;
|
|
5330
5996
|
opts2.callbacks.onTextDelta?.(ev.delta);
|
|
5331
5997
|
break;
|
|
5332
5998
|
case "tool_call_start":
|
|
@@ -5343,6 +6009,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5343
6009
|
function: { name: normalizeToolName(ev.name, opts2.tools), arguments: safeArgs }
|
|
5344
6010
|
};
|
|
5345
6011
|
toolCalls.push(call);
|
|
6012
|
+
tr.toolsUsed.push(call.function.name);
|
|
5346
6013
|
opts2.callbacks.onToolCallFinalized?.(call);
|
|
5347
6014
|
break;
|
|
5348
6015
|
}
|
|
@@ -5356,6 +6023,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5356
6023
|
}
|
|
5357
6024
|
if (opts2.signal.aborted) throw new DOMException("aborted", "AbortError");
|
|
5358
6025
|
if (lastUsage) {
|
|
6026
|
+
if (typeof lastUsage.cost === "number") tr.costUsd += lastUsage.cost;
|
|
5359
6027
|
opts2.callbacks.onUsageFinal?.(lastUsage, responseMeta);
|
|
5360
6028
|
cumulativePromptTokens += lastUsage.prompt_tokens;
|
|
5361
6029
|
if (!budgetExhausted && opts2.maxInputTokens !== void 0 && opts2.maxInputTokens > 0 && cumulativePromptTokens >= opts2.maxInputTokens) {
|
|
@@ -5384,8 +6052,10 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5384
6052
|
}
|
|
5385
6053
|
}
|
|
5386
6054
|
opts2.messages.push(assistantMsg);
|
|
6055
|
+
tr.finalText = typeof assistantMsg.content === "string" ? assistantMsg.content : null;
|
|
5387
6056
|
opts2.callbacks.onAssistantFinal?.(assistantMsg);
|
|
5388
6057
|
if (finalizeReason !== null) {
|
|
6058
|
+
tr.endReason = "guardrail";
|
|
5389
6059
|
logger.info("turn:finalized", { sessionId: opts2.sessionId, reason: finalizeReason, totalIter });
|
|
5390
6060
|
if (budgetExhausted) throw new BudgetExhaustedError();
|
|
5391
6061
|
await fireStopHook();
|
|
@@ -5424,11 +6094,12 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5424
6094
|
if (shouldCheckCompletion(input, opts2.intentClassification?.tier)) {
|
|
5425
6095
|
const p = await opts2.completionCheck(input, opts2.signal).catch(() => null);
|
|
5426
6096
|
if (opts2.signal.aborted) throw new DOMException("aborted", "AbortError");
|
|
6097
|
+
if (p !== null) trace.emit("completion_check", { probability: p, nudged: p < INCOMPLETE_THRESHOLD }, turnId);
|
|
5427
6098
|
if (p !== null && p < INCOMPLETE_THRESHOLD) {
|
|
5428
6099
|
completionNudges++;
|
|
5429
6100
|
logger.info("turn:completion_nudge", { sessionId: opts2.sessionId, probability: p });
|
|
5430
6101
|
opts2.messages.push({ role: "system", content: CONTINUE_NUDGE });
|
|
5431
|
-
|
|
6102
|
+
notifyGuardrail({
|
|
5432
6103
|
kind: "completion_nudge",
|
|
5433
6104
|
message: "The agent stopped before finishing \u2014 asked it to continue.",
|
|
5434
6105
|
probability: p
|
|
@@ -5468,6 +6139,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5468
6139
|
kind: "blocked",
|
|
5469
6140
|
tc,
|
|
5470
6141
|
loopSignature,
|
|
6142
|
+
errorCode: "loop_guardrail",
|
|
5471
6143
|
result: {
|
|
5472
6144
|
tool_call_id: tc.id,
|
|
5473
6145
|
name: tc.function.name,
|
|
@@ -5480,10 +6152,13 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5480
6152
|
}
|
|
5481
6153
|
if (codeMode && CODE_MODE_REDIRECT_TOOLS.has(tc.function.name) && codeModeRedirects < MAX_CODE_MODE_REDIRECTS) {
|
|
5482
6154
|
codeModeRedirects++;
|
|
6155
|
+
noteCodeModeRedirect();
|
|
5483
6156
|
items.push({
|
|
5484
6157
|
kind: "blocked",
|
|
5485
6158
|
tc,
|
|
5486
6159
|
loopSignature,
|
|
6160
|
+
errorCode: "code_mode_redirect",
|
|
6161
|
+
redirected: true,
|
|
5487
6162
|
result: { tool_call_id: tc.id, name: tc.function.name, content: codeModeRedirectMessage(tc.function.name), ok: false }
|
|
5488
6163
|
});
|
|
5489
6164
|
continue;
|
|
@@ -5500,6 +6175,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5500
6175
|
kind: "blocked",
|
|
5501
6176
|
tc,
|
|
5502
6177
|
loopSignature,
|
|
6178
|
+
errorCode: "web_fetch_budget",
|
|
5503
6179
|
result: { tool_call_id: tc.id, name: "web_fetch", content: warning, ok: false, guardrail: true }
|
|
5504
6180
|
});
|
|
5505
6181
|
continue;
|
|
@@ -5520,6 +6196,8 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5520
6196
|
{ id: it.tc.id, name: it.tc.function.name, arguments: it.tc.function.arguments },
|
|
5521
6197
|
batchAskPermission,
|
|
5522
6198
|
{
|
|
6199
|
+
trace,
|
|
6200
|
+
turnId,
|
|
5523
6201
|
cwd: opts2.cwd,
|
|
5524
6202
|
signal: opts2.signal,
|
|
5525
6203
|
onTasks: wrappedOnTasks,
|
|
@@ -5562,6 +6240,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5562
6240
|
}
|
|
5563
6241
|
for (const it of items) {
|
|
5564
6242
|
if (it.kind === "blocked") {
|
|
6243
|
+
traceBlocked(it.tc, it.result, it.errorCode, it.redirected === true);
|
|
5565
6244
|
toolResults.push(it.result);
|
|
5566
6245
|
opts2.messages.push({
|
|
5567
6246
|
role: "tool",
|
|
@@ -5697,6 +6376,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5697
6376
|
content: `Tool ${tc.function.name} is not available under this turn's policy. Continue without it or ask the user to enable it.`,
|
|
5698
6377
|
ok: false
|
|
5699
6378
|
};
|
|
6379
|
+
traceBlocked(tc, unavailableResult, "unavailable");
|
|
5700
6380
|
toolResults.push(unavailableResult);
|
|
5701
6381
|
opts2.messages.push({
|
|
5702
6382
|
role: "tool",
|
|
@@ -5720,6 +6400,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5720
6400
|
ok: false,
|
|
5721
6401
|
guardrail: true
|
|
5722
6402
|
};
|
|
6403
|
+
traceBlocked(tc, loopResult, "loop_guardrail");
|
|
5723
6404
|
toolResults.push(loopResult);
|
|
5724
6405
|
opts2.messages.push({
|
|
5725
6406
|
role: "tool",
|
|
@@ -5748,6 +6429,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5748
6429
|
ok: false,
|
|
5749
6430
|
guardrail: true
|
|
5750
6431
|
};
|
|
6432
|
+
traceBlocked(tc, budgetResult, "web_fetch_budget");
|
|
5751
6433
|
toolResults.push(budgetResult);
|
|
5752
6434
|
opts2.messages.push({
|
|
5753
6435
|
role: "tool",
|
|
@@ -5770,6 +6452,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5770
6452
|
ok: false,
|
|
5771
6453
|
guardrail: true
|
|
5772
6454
|
};
|
|
6455
|
+
traceBlocked(tc, sessionCapResult, "web_fetch_budget");
|
|
5773
6456
|
toolResults.push(sessionCapResult);
|
|
5774
6457
|
opts2.messages.push({
|
|
5775
6458
|
role: "tool",
|
|
@@ -5791,12 +6474,39 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5791
6474
|
if (codeMode && tc.function.name === "execute_code") {
|
|
5792
6475
|
const args = JSON.parse(tc.function.arguments || "{}");
|
|
5793
6476
|
const code = args.code || "";
|
|
6477
|
+
const sandboxStartedAt = performance.now();
|
|
6478
|
+
if (trace.enabled) {
|
|
6479
|
+
trace.emit(
|
|
6480
|
+
"tool_call",
|
|
6481
|
+
{
|
|
6482
|
+
tool_call_id: tc.id,
|
|
6483
|
+
name: "execute_code",
|
|
6484
|
+
args_blob: trace.blob(tc.function.arguments) ?? "",
|
|
6485
|
+
...tc.function.arguments.length <= 2048 ? { args_inline: parseArgsObject(tc.function.arguments) } : {},
|
|
6486
|
+
needs_permission: false,
|
|
6487
|
+
code_mode_redirected: false
|
|
6488
|
+
},
|
|
6489
|
+
turnId
|
|
6490
|
+
);
|
|
6491
|
+
}
|
|
5794
6492
|
const sandboxResult = await runInSandbox({
|
|
5795
6493
|
code,
|
|
5796
6494
|
tools: sandboxTools,
|
|
5797
6495
|
executor: opts2.executor,
|
|
5798
6496
|
askPermission: opts2.callbacks.askPermission,
|
|
5799
|
-
ctx: {
|
|
6497
|
+
ctx: {
|
|
6498
|
+
cwd: opts2.cwd,
|
|
6499
|
+
signal: opts2.signal,
|
|
6500
|
+
onTasks: wrappedOnTasks,
|
|
6501
|
+
onPlanOptions: opts2.callbacks.onPlanOptions,
|
|
6502
|
+
coauthor: opts2.coauthor,
|
|
6503
|
+
memoryManager: opts2.memoryManager,
|
|
6504
|
+
sessionId: opts2.sessionId,
|
|
6505
|
+
githubToken: opts2.githubToken,
|
|
6506
|
+
trace,
|
|
6507
|
+
turnId,
|
|
6508
|
+
codeModeParentToolCallId: tc.id
|
|
6509
|
+
},
|
|
5800
6510
|
timeoutMs: 3e4,
|
|
5801
6511
|
memoryLimitMB: 128
|
|
5802
6512
|
});
|
|
@@ -5819,6 +6529,7 @@ Not in this API \u2014 call these as separate tools instead: ${directTools.map((
|
|
|
5819
6529
|
|
|
5820
6530
|
Output:
|
|
5821
6531
|
${sandboxResult.output}` : sandboxResult.output;
|
|
6532
|
+
const sandboxRawContent = resultContent;
|
|
5822
6533
|
if (resultContent.length > MAX_TOOL_CONTENT_CHARS) {
|
|
5823
6534
|
const rawBytes = resultContent.length;
|
|
5824
6535
|
resultContent = resultContent.slice(0, MAX_TOOL_CONTENT_CHARS) + `
|
|
@@ -5837,6 +6548,24 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
5837
6548
|
content: resultContent,
|
|
5838
6549
|
ok: !sandboxResult.error
|
|
5839
6550
|
};
|
|
6551
|
+
if (trace.enabled) {
|
|
6552
|
+
const rawBlob = trace.blob(sandboxRawContent) ?? "";
|
|
6553
|
+
trace.emit(
|
|
6554
|
+
"tool_result",
|
|
6555
|
+
{
|
|
6556
|
+
tool_call_id: tc.id,
|
|
6557
|
+
name: "execute_code",
|
|
6558
|
+
ok: result2.ok,
|
|
6559
|
+
...sandboxResult.error ? { error_code: "sandbox_error" } : {},
|
|
6560
|
+
raw_blob: rawBlob,
|
|
6561
|
+
raw_bytes: Buffer.byteLength(sandboxRawContent, "utf8"),
|
|
6562
|
+
reduced_blob: sandboxRawContent === resultContent ? rawBlob : trace.blob(resultContent) ?? "",
|
|
6563
|
+
reduced_bytes: Buffer.byteLength(resultContent, "utf8"),
|
|
6564
|
+
duration_ms: Math.round(performance.now() - sandboxStartedAt)
|
|
6565
|
+
},
|
|
6566
|
+
turnId
|
|
6567
|
+
);
|
|
6568
|
+
}
|
|
5840
6569
|
toolResults.push(result2);
|
|
5841
6570
|
opts2.messages.push({
|
|
5842
6571
|
role: "tool",
|
|
@@ -5849,8 +6578,10 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
5849
6578
|
if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
|
|
5850
6579
|
} else if (codeMode && CODE_MODE_REDIRECT_TOOLS.has(tc.function.name) && codeModeRedirects < MAX_CODE_MODE_REDIRECTS) {
|
|
5851
6580
|
codeModeRedirects++;
|
|
6581
|
+
noteCodeModeRedirect();
|
|
5852
6582
|
const msg = codeModeRedirectMessage(tc.function.name);
|
|
5853
6583
|
const redirectResult = { tool_call_id: tc.id, name: tc.function.name, content: msg, ok: false };
|
|
6584
|
+
traceBlocked(tc, redirectResult, "code_mode_redirect", true);
|
|
5854
6585
|
toolResults.push(redirectResult);
|
|
5855
6586
|
opts2.messages.push({ role: "tool", tool_call_id: tc.id, content: msg, name: tc.function.name });
|
|
5856
6587
|
opts2.callbacks.onToolResult?.(redirectResult);
|
|
@@ -5861,6 +6592,8 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
5861
6592
|
{ id: tc.id, name: tc.function.name, arguments: tc.function.arguments },
|
|
5862
6593
|
opts2.callbacks.askPermission,
|
|
5863
6594
|
{
|
|
6595
|
+
trace,
|
|
6596
|
+
turnId,
|
|
5864
6597
|
cwd: opts2.cwd,
|
|
5865
6598
|
signal: opts2.signal,
|
|
5866
6599
|
onTasks: wrappedOnTasks,
|
|
@@ -5929,6 +6662,7 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
5929
6662
|
condition: result2.waitRequest.condition,
|
|
5930
6663
|
wakeAt: result2.waitRequest.wakeAt
|
|
5931
6664
|
});
|
|
6665
|
+
tr.endReason = "needs_input";
|
|
5932
6666
|
try {
|
|
5933
6667
|
opts2.callbacks.onRunYield?.(result2.waitRequest);
|
|
5934
6668
|
} catch (error) {
|
|
@@ -6087,7 +6821,7 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
6087
6821
|
role: "system",
|
|
6088
6822
|
content: "Every tool call in your last step was blocked because you kept repeating the same calls with identical arguments. Do not repeat them. Either take a genuinely different approach (different tools, arguments, or files), or, if you already have enough information, stop calling tools and give your final answer."
|
|
6089
6823
|
});
|
|
6090
|
-
|
|
6824
|
+
notifyGuardrail({
|
|
6091
6825
|
kind: "loop_recovery",
|
|
6092
6826
|
message: "Loop guardrail: repeated tool calls were blocked \u2014 asked the agent to change approach."
|
|
6093
6827
|
});
|
|
@@ -6098,7 +6832,7 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
6098
6832
|
role: "system",
|
|
6099
6833
|
content: "You are still repeating blocked tool calls after being asked to change approach, so this turn is ending. Do not call any more tools. Summarize what you accomplished, what is blocking you, and what the user could do next."
|
|
6100
6834
|
});
|
|
6101
|
-
|
|
6835
|
+
notifyGuardrail({
|
|
6102
6836
|
kind: "loop_stopped",
|
|
6103
6837
|
message: "Loop guardrail: the agent kept repeating blocked tool calls \u2014 ending the turn with a summary."
|
|
6104
6838
|
});
|
|
@@ -6159,6 +6893,8 @@ var init_loop = __esm({
|
|
|
6159
6893
|
init_context_budget();
|
|
6160
6894
|
init_turn_directive();
|
|
6161
6895
|
init_completion_check();
|
|
6896
|
+
init_recorder();
|
|
6897
|
+
init_git_delta();
|
|
6162
6898
|
MAX_LOOP_RECOVERIES = 1;
|
|
6163
6899
|
BudgetExhaustedError = class extends Error {
|
|
6164
6900
|
constructor(message2 = "Cumulative input token budget exhausted") {
|
|
@@ -6214,10 +6950,10 @@ var init_pricing = __esm({
|
|
|
6214
6950
|
// src/util/atomic-file.ts
|
|
6215
6951
|
import { mkdir as mkdir6, rename, rm, stat as stat3, writeFile as writeFile5 } from "fs/promises";
|
|
6216
6952
|
import { dirname as dirname6 } from "path";
|
|
6217
|
-
import { randomUUID } from "crypto";
|
|
6953
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
6218
6954
|
async function writeFileAtomic(path, data) {
|
|
6219
6955
|
await mkdir6(dirname6(path), { recursive: true });
|
|
6220
|
-
const tmp = `${path}.${process.pid}.${
|
|
6956
|
+
const tmp = `${path}.${process.pid}.${randomUUID2().slice(0, 8)}.tmp`;
|
|
6221
6957
|
try {
|
|
6222
6958
|
await writeFile5(tmp, data, "utf8");
|
|
6223
6959
|
await rename(tmp, path);
|
|
@@ -6272,19 +7008,19 @@ var init_atomic_file = __esm({
|
|
|
6272
7008
|
|
|
6273
7009
|
// src/usage-tracker.ts
|
|
6274
7010
|
import { readFile as readFile5, rename as rename2 } from "fs/promises";
|
|
6275
|
-
import { homedir as
|
|
6276
|
-
import { join as
|
|
7011
|
+
import { homedir as homedir9 } from "os";
|
|
7012
|
+
import { join as join15 } from "path";
|
|
6277
7013
|
import { EventEmitter } from "events";
|
|
6278
|
-
import { randomUUID as
|
|
7014
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
6279
7015
|
function usageDir() {
|
|
6280
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
6281
|
-
return
|
|
7016
|
+
const xdg = process.env.XDG_DATA_HOME || join15(homedir9(), ".local", "share");
|
|
7017
|
+
return join15(xdg, "kimiflare");
|
|
6282
7018
|
}
|
|
6283
7019
|
function usagePath() {
|
|
6284
|
-
return
|
|
7020
|
+
return join15(usageDir(), "usage.json");
|
|
6285
7021
|
}
|
|
6286
7022
|
function historyPath() {
|
|
6287
|
-
return
|
|
7023
|
+
return join15(usageDir(), "history.jsonl");
|
|
6288
7024
|
}
|
|
6289
7025
|
function today() {
|
|
6290
7026
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -6404,7 +7140,7 @@ async function recordUsage(sessionId, usage, lookup, model) {
|
|
|
6404
7140
|
);
|
|
6405
7141
|
const estimatedCost = cost.total;
|
|
6406
7142
|
const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
6407
|
-
const turnId =
|
|
7143
|
+
const turnId = randomUUID3();
|
|
6408
7144
|
const generationId = lookup?.meta.generationId;
|
|
6409
7145
|
const inlineCost = typeof usage.cost === "number" && Number.isFinite(usage.cost) ? usage.cost : void 0;
|
|
6410
7146
|
await withLock(async () => {
|
|
@@ -6705,12 +7441,41 @@ var init_tool_error = __esm({
|
|
|
6705
7441
|
}
|
|
6706
7442
|
});
|
|
6707
7443
|
|
|
7444
|
+
// src/trace/verification.ts
|
|
7445
|
+
function segment(alternatives) {
|
|
7446
|
+
return new RegExp(`${SEG}(?:${alternatives})(?:\\s|$)`);
|
|
7447
|
+
}
|
|
7448
|
+
function classifyVerification(command) {
|
|
7449
|
+
const trimmed = command.trim();
|
|
7450
|
+
if (!trimmed) return null;
|
|
7451
|
+
for (const [category, re] of TABLE) {
|
|
7452
|
+
if (re.test(trimmed)) return category;
|
|
7453
|
+
}
|
|
7454
|
+
return null;
|
|
7455
|
+
}
|
|
7456
|
+
var SEG, TABLE;
|
|
7457
|
+
var init_verification = __esm({
|
|
7458
|
+
"src/trace/verification.ts"() {
|
|
7459
|
+
"use strict";
|
|
7460
|
+
SEG = String.raw`(?:^|[;|(]\s*|&&\s*|\|\|\s*)(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*(?:npx\s+|pnpm\s+exec\s+|pnpm\s+dlx\s+|yarn\s+|bunx\s+|bun\s+run\s+|(?:\.\/)?node_modules\/\.bin\/)?`;
|
|
7461
|
+
TABLE = [
|
|
7462
|
+
[
|
|
7463
|
+
"test",
|
|
7464
|
+
segment(String.raw`npm\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|yarn\s+(?:run\s+)?test|tsx\s+--test|node\s+--test|vitest|jest|pytest|go\s+test|cargo\s+test`)
|
|
7465
|
+
],
|
|
7466
|
+
["lint", segment(String.raw`eslint|ruff|golangci-lint|npm\s+run\s+lint|pnpm\s+(?:run\s+)?lint|yarn\s+(?:run\s+)?lint`)],
|
|
7467
|
+
["typecheck", segment(String.raw`tsc|mypy|pyright|npm\s+run\s+typecheck|pnpm\s+(?:run\s+)?typecheck|yarn\s+(?:run\s+)?typecheck`)],
|
|
7468
|
+
["build", segment(String.raw`npm\s+run\s+build|tsup|cargo\s+build|go\s+build|pnpm\s+(?:run\s+)?build|yarn\s+(?:run\s+)?build`)]
|
|
7469
|
+
];
|
|
7470
|
+
}
|
|
7471
|
+
});
|
|
7472
|
+
|
|
6708
7473
|
// src/util/paths.ts
|
|
6709
7474
|
import { resolve, isAbsolute, relative, sep } from "path";
|
|
6710
|
-
import { homedir as
|
|
7475
|
+
import { homedir as homedir10 } from "os";
|
|
6711
7476
|
function resolvePath(cwd, input) {
|
|
6712
7477
|
if (input.startsWith("~/") || input === "~") {
|
|
6713
|
-
return resolve(
|
|
7478
|
+
return resolve(homedir10(), input === "~" ? "." : input.slice(2));
|
|
6714
7479
|
}
|
|
6715
7480
|
return isAbsolute(input) ? input : resolve(cwd, input);
|
|
6716
7481
|
}
|
|
@@ -6737,7 +7502,7 @@ function collapsePath(input, cwd, maxLen = 40) {
|
|
|
6737
7502
|
function collapsePathsInText(s, cwd, maxLen = 40) {
|
|
6738
7503
|
return s.replace(/([~/][^\s"',)}\]]+)/g, (match) => collapsePath(match, cwd, maxLen));
|
|
6739
7504
|
}
|
|
6740
|
-
var
|
|
7505
|
+
var init_paths2 = __esm({
|
|
6741
7506
|
"src/util/paths.ts"() {
|
|
6742
7507
|
"use strict";
|
|
6743
7508
|
}
|
|
@@ -6794,7 +7559,7 @@ var MAX_BYTES, MAX_STREAM_BYTES, readTool;
|
|
|
6794
7559
|
var init_read = __esm({
|
|
6795
7560
|
"src/tools/read.ts"() {
|
|
6796
7561
|
"use strict";
|
|
6797
|
-
|
|
7562
|
+
init_paths2();
|
|
6798
7563
|
MAX_BYTES = 2 * 1024 * 1024;
|
|
6799
7564
|
MAX_STREAM_BYTES = 50 * 1024 * 1024;
|
|
6800
7565
|
readTool = {
|
|
@@ -6845,7 +7610,7 @@ var writeTool;
|
|
|
6845
7610
|
var init_write = __esm({
|
|
6846
7611
|
"src/tools/write.ts"() {
|
|
6847
7612
|
"use strict";
|
|
6848
|
-
|
|
7613
|
+
init_paths2();
|
|
6849
7614
|
writeTool = {
|
|
6850
7615
|
name: "write",
|
|
6851
7616
|
description: "Create a file or overwrite an existing one with the given contents. Prompts the user for permission first and shows a diff preview.",
|
|
@@ -6896,7 +7661,7 @@ var editTool;
|
|
|
6896
7661
|
var init_edit = __esm({
|
|
6897
7662
|
"src/tools/edit.ts"() {
|
|
6898
7663
|
"use strict";
|
|
6899
|
-
|
|
7664
|
+
init_paths2();
|
|
6900
7665
|
editTool = {
|
|
6901
7666
|
name: "edit",
|
|
6902
7667
|
description: "Replace an exact string in a file. If replace_all is false (default), the old_string must appear exactly once or the call fails. Prompts the user for permission first and shows a diff preview.",
|
|
@@ -6937,7 +7702,7 @@ var init_edit = __esm({
|
|
|
6937
7702
|
// src/tools/bash.ts
|
|
6938
7703
|
import { spawn } from "child_process";
|
|
6939
7704
|
import { tmpdir, platform as platform2 } from "os";
|
|
6940
|
-
import { join as
|
|
7705
|
+
import { join as join16 } from "path";
|
|
6941
7706
|
function getShellCommand(override) {
|
|
6942
7707
|
const raw = override?.trim();
|
|
6943
7708
|
if (raw && raw !== "auto") {
|
|
@@ -7139,7 +7904,7 @@ function injectCoauthor(command, coauthor) {
|
|
|
7139
7904
|
if (movesHeadOnly) return unchanged;
|
|
7140
7905
|
const env2 = {
|
|
7141
7906
|
[COAUTHOR_TRAILER_ENV]: trailer,
|
|
7142
|
-
[COAUTHOR_MSG_ENV]:
|
|
7907
|
+
[COAUTHOR_MSG_ENV]: join16(tmpdir(), `kf-coauthor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`)
|
|
7143
7908
|
};
|
|
7144
7909
|
if (createsCommit || isRebaseContinue) {
|
|
7145
7910
|
return { command: `(${command}) && { ${AMEND_BLOCK}
|
|
@@ -7232,7 +7997,8 @@ ${stderr}`
|
|
|
7232
7997
|
resolve15({
|
|
7233
7998
|
content: raw,
|
|
7234
7999
|
rawBytes: Buffer.byteLength(raw, "utf8"),
|
|
7235
|
-
reducedBytes: Buffer.byteLength(raw, "utf8")
|
|
8000
|
+
reducedBytes: Buffer.byteLength(raw, "utf8"),
|
|
8001
|
+
meta: { exitCode: code, timedOut: killedByTimeout }
|
|
7236
8002
|
});
|
|
7237
8003
|
});
|
|
7238
8004
|
});
|
|
@@ -7280,7 +8046,7 @@ var init_bash = __esm({
|
|
|
7280
8046
|
|
|
7281
8047
|
// src/util/glob.ts
|
|
7282
8048
|
import { readdir as readdir3, lstat, realpath } from "fs/promises";
|
|
7283
|
-
import { join as
|
|
8049
|
+
import { join as join17, relative as relative2, resolve as resolve2 } from "path";
|
|
7284
8050
|
function parsePattern(pattern) {
|
|
7285
8051
|
const parts = pattern.split(/\//g);
|
|
7286
8052
|
return parts.map((p) => {
|
|
@@ -7321,8 +8087,8 @@ function globToRegex(glob2) {
|
|
|
7321
8087
|
re += "$";
|
|
7322
8088
|
return new RegExp(re);
|
|
7323
8089
|
}
|
|
7324
|
-
function matchSegment(
|
|
7325
|
-
return globToRegex(pattern).test(
|
|
8090
|
+
function matchSegment(segment2, pattern) {
|
|
8091
|
+
return globToRegex(pattern).test(segment2);
|
|
7326
8092
|
}
|
|
7327
8093
|
function shouldIgnore(relativePath, ignorePatterns) {
|
|
7328
8094
|
for (const pat of ignorePatterns) {
|
|
@@ -7389,7 +8155,7 @@ async function* walk(root, pattern, options) {
|
|
|
7389
8155
|
if (!dot && name.startsWith(".")) continue;
|
|
7390
8156
|
const matched = seg.type === "literal" ? seg.value === name : matchSegment(name, seg.value);
|
|
7391
8157
|
if (!matched) continue;
|
|
7392
|
-
const childPath =
|
|
8158
|
+
const childPath = join17(dirPath, name);
|
|
7393
8159
|
const childRel = [...relativeParts, name];
|
|
7394
8160
|
const childRelStr = childRel.join("/");
|
|
7395
8161
|
if (shouldIgnore(childRelStr, ignorePatterns)) continue;
|
|
@@ -7414,7 +8180,7 @@ async function* walk(root, pattern, options) {
|
|
|
7414
8180
|
if (!dot && name.startsWith(".")) continue;
|
|
7415
8181
|
const matched = seg.type === "literal" ? seg.value === name : matchSegment(name, seg.value);
|
|
7416
8182
|
if (!matched) continue;
|
|
7417
|
-
const childPath =
|
|
8183
|
+
const childPath = join17(dirPath, name);
|
|
7418
8184
|
const childRel = [...relativeParts, name];
|
|
7419
8185
|
const childRelStr = childRel.join("/");
|
|
7420
8186
|
if (shouldIgnore(childRelStr, ignorePatterns)) continue;
|
|
@@ -7456,7 +8222,7 @@ async function* walk(root, pattern, options) {
|
|
|
7456
8222
|
const name = String(ent.name);
|
|
7457
8223
|
if (name === "." || name === "..") continue;
|
|
7458
8224
|
if (!dot && name.startsWith(".")) continue;
|
|
7459
|
-
const childPath =
|
|
8225
|
+
const childPath = join17(dirPath, name);
|
|
7460
8226
|
const childRel = [...relativeParts, name];
|
|
7461
8227
|
const childRelStr = childRel.join("/");
|
|
7462
8228
|
if (shouldIgnore(childRelStr, ignorePatterns)) continue;
|
|
@@ -7550,7 +8316,7 @@ var globTool;
|
|
|
7550
8316
|
var init_glob2 = __esm({
|
|
7551
8317
|
"src/tools/glob.ts"() {
|
|
7552
8318
|
"use strict";
|
|
7553
|
-
|
|
8319
|
+
init_paths2();
|
|
7554
8320
|
init_glob();
|
|
7555
8321
|
globTool = {
|
|
7556
8322
|
name: "glob",
|
|
@@ -7601,8 +8367,8 @@ var init_glob2 = __esm({
|
|
|
7601
8367
|
});
|
|
7602
8368
|
|
|
7603
8369
|
// src/tools/grep.ts
|
|
7604
|
-
import { execFile } from "child_process";
|
|
7605
|
-
import { promisify } from "util";
|
|
8370
|
+
import { execFile as execFile2 } from "child_process";
|
|
8371
|
+
import { promisify as promisify2 } from "util";
|
|
7606
8372
|
import { readFile as readFile9 } from "fs/promises";
|
|
7607
8373
|
async function hasRipgrep() {
|
|
7608
8374
|
if (cachedHasRg !== null) return cachedHasRg;
|
|
@@ -7678,9 +8444,9 @@ var pExecFile, cachedHasRg, grepTool;
|
|
|
7678
8444
|
var init_grep = __esm({
|
|
7679
8445
|
"src/tools/grep.ts"() {
|
|
7680
8446
|
"use strict";
|
|
7681
|
-
|
|
8447
|
+
init_paths2();
|
|
7682
8448
|
init_glob();
|
|
7683
|
-
pExecFile =
|
|
8449
|
+
pExecFile = promisify2(execFile2);
|
|
7684
8450
|
cachedHasRg = null;
|
|
7685
8451
|
grepTool = {
|
|
7686
8452
|
name: "grep",
|
|
@@ -8224,7 +8990,7 @@ __export(changelog_image_exports, {
|
|
|
8224
8990
|
});
|
|
8225
8991
|
import { readFile as readFile10 } from "fs/promises";
|
|
8226
8992
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
8227
|
-
import { join as
|
|
8993
|
+
import { join as join18 } from "path";
|
|
8228
8994
|
async function githubFetch2(path, token) {
|
|
8229
8995
|
const controller = new AbortController();
|
|
8230
8996
|
const timer3 = setTimeout(() => controller.abort(), TIMEOUT_MS4);
|
|
@@ -8255,7 +9021,7 @@ function escapeXml(str) {
|
|
|
8255
9021
|
}
|
|
8256
9022
|
async function loadLogoBase64() {
|
|
8257
9023
|
try {
|
|
8258
|
-
const buf = await readFile10(
|
|
9024
|
+
const buf = await readFile10(join18(process.cwd(), "docs", "logo.png"));
|
|
8259
9025
|
return `data:image/png;base64,${buf.toString("base64")}`;
|
|
8260
9026
|
} catch {
|
|
8261
9027
|
return null;
|
|
@@ -8563,7 +9329,7 @@ Format your response as plain text bullet points, one per line, starting with "\
|
|
|
8563
9329
|
// src/tools/browser.ts
|
|
8564
9330
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
8565
9331
|
import { tmpdir as tmpdir2 } from "os";
|
|
8566
|
-
import { join as
|
|
9332
|
+
import { join as join19, dirname as dirname8 } from "path";
|
|
8567
9333
|
async function autoScroll(page) {
|
|
8568
9334
|
await page.evaluate(async () => {
|
|
8569
9335
|
await new Promise((resolve15) => {
|
|
@@ -8639,7 +9405,7 @@ var init_browser = __esm({
|
|
|
8639
9405
|
}
|
|
8640
9406
|
let screenshotPath;
|
|
8641
9407
|
if (args.screenshot) {
|
|
8642
|
-
screenshotPath =
|
|
9408
|
+
screenshotPath = join19(tmpdir2(), `kimiflare-browser-${Date.now()}.png`);
|
|
8643
9409
|
await mkdir8(dirname8(screenshotPath), { recursive: true });
|
|
8644
9410
|
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
8645
9411
|
}
|
|
@@ -8893,13 +9659,13 @@ var init_memory = __esm({
|
|
|
8893
9659
|
});
|
|
8894
9660
|
|
|
8895
9661
|
// src/tools/hotcell-snapshot.ts
|
|
8896
|
-
import { execFile as
|
|
9662
|
+
import { execFile as execFile3 } from "child_process";
|
|
8897
9663
|
import { mkdtemp, rm as rm2 } from "fs/promises";
|
|
8898
9664
|
import { tmpdir as tmpdir3 } from "os";
|
|
8899
|
-
import { join as
|
|
8900
|
-
import { promisify as
|
|
9665
|
+
import { join as join20 } from "path";
|
|
9666
|
+
import { promisify as promisify3 } from "util";
|
|
8901
9667
|
async function git(args, cwd, env2) {
|
|
8902
|
-
const { stdout } = await
|
|
9668
|
+
const { stdout } = await execFileAsync2("git", args, {
|
|
8903
9669
|
cwd,
|
|
8904
9670
|
env: env2 ? { ...process.env, ...env2 } : process.env,
|
|
8905
9671
|
maxBuffer: 64 * 1024 * 1024,
|
|
@@ -8908,7 +9674,7 @@ async function git(args, cwd, env2) {
|
|
|
8908
9674
|
return stdout.toString("utf8");
|
|
8909
9675
|
}
|
|
8910
9676
|
async function gitBuffer(args, cwd) {
|
|
8911
|
-
const { stdout } = await
|
|
9677
|
+
const { stdout } = await execFileAsync2("git", args, { cwd, maxBuffer: 64 * 1024 * 1024, encoding: "buffer" });
|
|
8912
9678
|
return stdout;
|
|
8913
9679
|
}
|
|
8914
9680
|
async function originBranchesContaining(commit, cwd) {
|
|
@@ -8944,9 +9710,9 @@ async function getRepositorySnapshot(cwd) {
|
|
|
8944
9710
|
if (!ref) {
|
|
8945
9711
|
throw new Error("Could not find an origin branch containing the base commit. Run git fetch, then retry.");
|
|
8946
9712
|
}
|
|
8947
|
-
const scratch = await mkdtemp(
|
|
9713
|
+
const scratch = await mkdtemp(join20(tmpdir3(), "autopilot-snapshot-"));
|
|
8948
9714
|
try {
|
|
8949
|
-
const env2 = { GIT_INDEX_FILE:
|
|
9715
|
+
const env2 = { GIT_INDEX_FILE: join20(scratch, "index") };
|
|
8950
9716
|
await git(["read-tree", "HEAD"], root, env2);
|
|
8951
9717
|
await git(["add", "-A", "--", ".", ...SECRET_EXCLUDES], root, env2);
|
|
8952
9718
|
const tree = (await git(["write-tree"], root, env2)).trim();
|
|
@@ -8971,9 +9737,9 @@ async function getRepositorySnapshot(cwd) {
|
|
|
8971
9737
|
async function getWorkingTreeArchive(cwd, limitBytes = MAX_ARCHIVE_BYTES) {
|
|
8972
9738
|
const root = (await git(["rev-parse", "--show-toplevel"], cwd).catch(() => "")).trim();
|
|
8973
9739
|
if (!root) throw new Error("Subagents need to run inside a Git repository.");
|
|
8974
|
-
const scratch = await mkdtemp(
|
|
9740
|
+
const scratch = await mkdtemp(join20(tmpdir3(), "autopilot-archive-"));
|
|
8975
9741
|
try {
|
|
8976
|
-
const env2 = { GIT_INDEX_FILE:
|
|
9742
|
+
const env2 = { GIT_INDEX_FILE: join20(scratch, "index") };
|
|
8977
9743
|
const hasHead = await git(["rev-parse", "--verify", "-q", "HEAD"], root).then(() => true, () => false);
|
|
8978
9744
|
await git(hasHead ? ["read-tree", "HEAD"] : ["read-tree", "--empty"], root, env2);
|
|
8979
9745
|
await git(["add", "-A", "--", ".", ...SECRET_EXCLUDES], root, env2);
|
|
@@ -8995,11 +9761,11 @@ function chunkPayload(base64, chunkSize = PAYLOAD_CHUNK_CHARS) {
|
|
|
8995
9761
|
for (let i = 0; i < base64.length; i += chunkSize) chunks.push(base64.slice(i, i + chunkSize));
|
|
8996
9762
|
return chunks;
|
|
8997
9763
|
}
|
|
8998
|
-
var
|
|
9764
|
+
var execFileAsync2, MAX_SNAPSHOT_PATCH_BYTES, MAX_ARCHIVE_BYTES, SECRET_EXCLUDES, PAYLOAD_CHUNK_CHARS;
|
|
8999
9765
|
var init_hotcell_snapshot = __esm({
|
|
9000
9766
|
"src/tools/hotcell-snapshot.ts"() {
|
|
9001
9767
|
"use strict";
|
|
9002
|
-
|
|
9768
|
+
execFileAsync2 = promisify3(execFile3);
|
|
9003
9769
|
MAX_SNAPSHOT_PATCH_BYTES = 4 * 1024 * 1024;
|
|
9004
9770
|
MAX_ARCHIVE_BYTES = 24 * 1024 * 1024;
|
|
9005
9771
|
SECRET_EXCLUDES = [
|
|
@@ -9022,11 +9788,11 @@ var init_hotcell_snapshot = __esm({
|
|
|
9022
9788
|
|
|
9023
9789
|
// src/tools/hotcell-worker-cache.ts
|
|
9024
9790
|
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile10 } from "fs/promises";
|
|
9025
|
-
import { homedir as
|
|
9026
|
-
import { dirname as dirname9, join as
|
|
9791
|
+
import { homedir as homedir11 } from "os";
|
|
9792
|
+
import { dirname as dirname9, join as join21 } from "path";
|
|
9027
9793
|
function workerCachePath() {
|
|
9028
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
9029
|
-
return
|
|
9794
|
+
const xdg = process.env.XDG_CONFIG_HOME || join21(homedir11(), ".config");
|
|
9795
|
+
return join21(xdg, "kimiflare", "hotcell-worker-cache.json");
|
|
9030
9796
|
}
|
|
9031
9797
|
async function readCache2() {
|
|
9032
9798
|
try {
|
|
@@ -9098,8 +9864,8 @@ var init_hotcell_worker_cache = __esm({
|
|
|
9098
9864
|
|
|
9099
9865
|
// src/tools/hotcell-worker.ts
|
|
9100
9866
|
import { spawn as spawn2 } from "child_process";
|
|
9101
|
-
import { promisify as
|
|
9102
|
-
import { execFile as
|
|
9867
|
+
import { promisify as promisify4 } from "util";
|
|
9868
|
+
import { execFile as execFile4 } from "child_process";
|
|
9103
9869
|
import { resolve as resolve3 } from "path";
|
|
9104
9870
|
async function runHotcellWorker(options) {
|
|
9105
9871
|
const workerId = `hotcell-${crypto.randomUUID().slice(0, 8)}`;
|
|
@@ -9505,7 +10271,7 @@ function releaseSlot() {
|
|
|
9505
10271
|
runningWorkers = Math.max(0, runningWorkers - 1);
|
|
9506
10272
|
for (const wake of workerQueue.slice()) wake();
|
|
9507
10273
|
}
|
|
9508
|
-
var
|
|
10274
|
+
var execFileAsync3, MAX_OUTPUT_BYTES, MAX_PROMPT_CHARS, DEFAULT_CELL_TIMEOUT_MS, DEFAULT_SETUP_TIMEOUT_MS, WORKER_BIN, PACKAGE_SPEC, LOCATE_REPO_ROOT, READ_REPO_ROOT, REPO_ASIDE, SNAPSHOT_PREFIX, ARCHIVE_ROOT, MAX_PARALLEL_WORKERS, MIN_WORKER_INPUT_TOKENS, MAX_WORKER_INPUT_TOKENS, DEFAULT_WORKER_INPUT_TOKENS, INPUT_SHARE_OF_BUDGET, MAX_REPORT_FINDINGS, MAX_REPORT_SUMMARY_CHARS, MAX_REPORT_LIST_ITEMS, runningWorkers, workerQueue, WORKER_REPORT_INSTRUCTIONS;
|
|
9509
10275
|
var init_hotcell_worker = __esm({
|
|
9510
10276
|
"src/tools/hotcell-worker.ts"() {
|
|
9511
10277
|
"use strict";
|
|
@@ -9514,7 +10280,7 @@ var init_hotcell_worker = __esm({
|
|
|
9514
10280
|
init_registry();
|
|
9515
10281
|
init_hotcell_snapshot();
|
|
9516
10282
|
init_hotcell_worker_cache();
|
|
9517
|
-
|
|
10283
|
+
execFileAsync3 = promisify4(execFile4);
|
|
9518
10284
|
MAX_OUTPUT_BYTES = 1e6;
|
|
9519
10285
|
MAX_PROMPT_CHARS = 4e4;
|
|
9520
10286
|
DEFAULT_CELL_TIMEOUT_MS = 3e5;
|
|
@@ -9636,11 +10402,11 @@ var init_worker_registry = __esm({
|
|
|
9636
10402
|
|
|
9637
10403
|
// src/tools/subagent-stats.ts
|
|
9638
10404
|
import { appendFile as appendFile2, mkdir as mkdir10, readFile as readFile12 } from "fs/promises";
|
|
9639
|
-
import { homedir as
|
|
9640
|
-
import { dirname as dirname10, join as
|
|
10405
|
+
import { homedir as homedir12 } from "os";
|
|
10406
|
+
import { dirname as dirname10, join as join22 } from "path";
|
|
9641
10407
|
function subagentStatsPath() {
|
|
9642
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
9643
|
-
return
|
|
10408
|
+
const xdg = process.env.XDG_DATA_HOME || join22(homedir12(), ".local", "share");
|
|
10409
|
+
return join22(xdg, "kimiflare", "subagents.jsonl");
|
|
9644
10410
|
}
|
|
9645
10411
|
async function recordSubagentEvent(event) {
|
|
9646
10412
|
const path = subagentStatsPath();
|
|
@@ -9866,16 +10632,29 @@ var init_subagent = __esm({
|
|
|
9866
10632
|
}
|
|
9867
10633
|
const handle = workerRegistry.start(args.mission, model, ctx.signal);
|
|
9868
10634
|
const startedAt2 = Date.now();
|
|
9869
|
-
const record = (status, extra = {}) =>
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
10635
|
+
const record = (status, extra = {}) => {
|
|
10636
|
+
void recordSubagentEvent({
|
|
10637
|
+
kind: "subagent",
|
|
10638
|
+
ts: Date.now(),
|
|
10639
|
+
sessionId: ctx.sessionId ?? "unknown",
|
|
10640
|
+
status,
|
|
10641
|
+
durationMs: Date.now() - startedAt2,
|
|
10642
|
+
costUsd: extra.costUsd ?? 0,
|
|
10643
|
+
...failureReason(extra.error) ? { reason: failureReason(extra.error) } : {},
|
|
10644
|
+
...extra.localChanges ? { localChanges: extra.localChanges } : {}
|
|
10645
|
+
});
|
|
10646
|
+
ctx.trace?.emit(
|
|
10647
|
+
"subagent",
|
|
10648
|
+
{
|
|
10649
|
+
worker_id: handle.id,
|
|
10650
|
+
mode: "hotcell",
|
|
10651
|
+
status,
|
|
10652
|
+
...extra.costUsd !== void 0 ? { cost_usd: extra.costUsd } : {},
|
|
10653
|
+
duration_ms: Date.now() - startedAt2
|
|
10654
|
+
},
|
|
10655
|
+
ctx.turnId ?? null
|
|
10656
|
+
);
|
|
10657
|
+
};
|
|
9879
10658
|
let result2;
|
|
9880
10659
|
try {
|
|
9881
10660
|
result2 = await runHotcellWorker({
|
|
@@ -10547,13 +11326,13 @@ var init_expand_artifact = __esm({
|
|
|
10547
11326
|
// src/jobs/manager.ts
|
|
10548
11327
|
import Database from "better-sqlite3";
|
|
10549
11328
|
import { spawn as spawn3, execFileSync } from "child_process";
|
|
10550
|
-
import { mkdirSync as
|
|
10551
|
-
import { homedir as
|
|
10552
|
-
import { dirname as dirname11, join as
|
|
10553
|
-
import { randomUUID as
|
|
11329
|
+
import { mkdirSync as mkdirSync5, openSync, readFileSync as readFileSync4, readSync, existsSync as existsSync4, statSync as statSync3, closeSync, rmSync } from "fs";
|
|
11330
|
+
import { homedir as homedir13 } from "os";
|
|
11331
|
+
import { dirname as dirname11, join as join23 } from "path";
|
|
11332
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
10554
11333
|
function defaultJobDbPath() {
|
|
10555
|
-
const root = process.env.XDG_STATE_HOME ||
|
|
10556
|
-
return process.env.AUTOPILOT_JOBS_DB ||
|
|
11334
|
+
const root = process.env.XDG_STATE_HOME || join23(homedir13(), ".local", "state");
|
|
11335
|
+
return process.env.AUTOPILOT_JOBS_DB || join23(root, "autopilot", "jobs.db");
|
|
10557
11336
|
}
|
|
10558
11337
|
function isProcessGroupAlive(pgid) {
|
|
10559
11338
|
try {
|
|
@@ -10571,7 +11350,7 @@ function waitForProcessStartTime(pid, jobId, exitPath) {
|
|
|
10571
11350
|
let previous = null;
|
|
10572
11351
|
const title = `autopilot-job-${jobId}`;
|
|
10573
11352
|
while (Date.now() < deadline) {
|
|
10574
|
-
if (
|
|
11353
|
+
if (existsSync4(exitPath)) break;
|
|
10575
11354
|
const current = readProcessStartTime(pid);
|
|
10576
11355
|
const hasUniqueTitle = process.platform !== "darwin" || current?.includes(title) === true;
|
|
10577
11356
|
if (current && current === previous && hasUniqueTitle && !isZombie(pid)) return current;
|
|
@@ -10583,8 +11362,8 @@ function waitForProcessStartTime(pid, jobId, exitPath) {
|
|
|
10583
11362
|
function readProcessStartTime(pid) {
|
|
10584
11363
|
if (process.platform === "linux") {
|
|
10585
11364
|
try {
|
|
10586
|
-
const
|
|
10587
|
-
const fieldsAfterCommand =
|
|
11365
|
+
const stat11 = readFileSync4(`/proc/${pid}/stat`, "utf8");
|
|
11366
|
+
const fieldsAfterCommand = stat11.slice(stat11.lastIndexOf(")") + 1).trim().split(/\s+/);
|
|
10588
11367
|
return fieldsAfterCommand[19] ? `linux:${fieldsAfterCommand[19]}` : null;
|
|
10589
11368
|
} catch {
|
|
10590
11369
|
return null;
|
|
@@ -10696,9 +11475,9 @@ child.on('close', (code, signal) => {
|
|
|
10696
11475
|
db;
|
|
10697
11476
|
jobsDir;
|
|
10698
11477
|
constructor(dbPath = defaultJobDbPath()) {
|
|
10699
|
-
|
|
10700
|
-
this.jobsDir =
|
|
10701
|
-
|
|
11478
|
+
mkdirSync5(dirname11(dbPath), { recursive: true, mode: 448 });
|
|
11479
|
+
this.jobsDir = join23(dirname11(dbPath), "jobs");
|
|
11480
|
+
mkdirSync5(this.jobsDir, { recursive: true, mode: 448 });
|
|
10702
11481
|
this.db = new Database(dbPath);
|
|
10703
11482
|
this.db.pragma("journal_mode = WAL");
|
|
10704
11483
|
this.db.pragma("busy_timeout = 5000");
|
|
@@ -10747,14 +11526,14 @@ child.on('close', (code, signal) => {
|
|
|
10747
11526
|
const existing = this.db.prepare("SELECT * FROM jobs WHERE cwd = ? AND idempotency_key = ?").get(options.cwd, options.idempotencyKey);
|
|
10748
11527
|
if (existing) return toJobRecord(existing);
|
|
10749
11528
|
}
|
|
10750
|
-
const id =
|
|
10751
|
-
const jobDir =
|
|
10752
|
-
|
|
11529
|
+
const id = randomUUID4();
|
|
11530
|
+
const jobDir = join23(this.jobsDir, id);
|
|
11531
|
+
mkdirSync5(jobDir, { recursive: true, mode: 448 });
|
|
10753
11532
|
const paths = {
|
|
10754
|
-
stdoutPath:
|
|
10755
|
-
stderrPath:
|
|
10756
|
-
exitPath:
|
|
10757
|
-
pidPath:
|
|
11533
|
+
stdoutPath: join23(jobDir, "stdout.log"),
|
|
11534
|
+
stderrPath: join23(jobDir, "stderr.log"),
|
|
11535
|
+
exitPath: join23(jobDir, "exit.json"),
|
|
11536
|
+
pidPath: join23(jobDir, "pid")
|
|
10758
11537
|
};
|
|
10759
11538
|
const now2 = Date.now();
|
|
10760
11539
|
try {
|
|
@@ -10807,7 +11586,7 @@ child.on('close', (code, signal) => {
|
|
|
10807
11586
|
const row = this.getRow(id);
|
|
10808
11587
|
if (!row) throw new Error(`Job not found: ${id}`);
|
|
10809
11588
|
const path = stream === "stdout" ? row.stdout_path : row.stderr_path;
|
|
10810
|
-
if (!
|
|
11589
|
+
if (!existsSync4(path)) return { content: "", offset: 0, size: 0, truncated: false };
|
|
10811
11590
|
const size = statSync3(path).size;
|
|
10812
11591
|
if (options.offset !== void 0) {
|
|
10813
11592
|
const offset = Math.max(0, Math.min(size, Math.trunc(options.offset)));
|
|
@@ -10875,9 +11654,9 @@ child.on('close', (code, signal) => {
|
|
|
10875
11654
|
reconcile() {
|
|
10876
11655
|
const rows = this.db.prepare("SELECT * FROM jobs WHERE status IN ('starting', 'running', 'cancelling', 'unknown')").all();
|
|
10877
11656
|
for (const row of rows) {
|
|
10878
|
-
if (
|
|
11657
|
+
if (existsSync4(row.exit_path)) {
|
|
10879
11658
|
try {
|
|
10880
|
-
const result2 = JSON.parse(
|
|
11659
|
+
const result2 = JSON.parse(readFileSync4(row.exit_path, "utf8"));
|
|
10881
11660
|
const status = result2.timedOut ? "timed_out" : row.status === "cancelling" ? "cancelled" : result2.code === 0 ? "completed" : "failed";
|
|
10882
11661
|
this.db.prepare("UPDATE jobs SET status = ?, exit_code = ?, finished_at = COALESCE(finished_at, ?) WHERE id = ?").run(status, result2.code, Date.now(), row.id);
|
|
10883
11662
|
continue;
|
|
@@ -10885,8 +11664,8 @@ child.on('close', (code, signal) => {
|
|
|
10885
11664
|
}
|
|
10886
11665
|
}
|
|
10887
11666
|
let pid = row.pid;
|
|
10888
|
-
if (!pid &&
|
|
10889
|
-
const parsed = Number(
|
|
11667
|
+
if (!pid && existsSync4(row.pid_path)) {
|
|
11668
|
+
const parsed = Number(readFileSync4(row.pid_path, "utf8").trim());
|
|
10890
11669
|
if (Number.isInteger(parsed) && parsed > 0) pid = parsed;
|
|
10891
11670
|
}
|
|
10892
11671
|
if (pid && row.process_start_time) {
|
|
@@ -11032,13 +11811,13 @@ ${result2.content}`;
|
|
|
11032
11811
|
|
|
11033
11812
|
// src/runs/store.ts
|
|
11034
11813
|
import Database2 from "better-sqlite3";
|
|
11035
|
-
import { createHash, randomUUID as
|
|
11036
|
-
import { chmodSync, mkdirSync as
|
|
11037
|
-
import { homedir as
|
|
11038
|
-
import { dirname as dirname12, join as
|
|
11814
|
+
import { createHash as createHash2, randomUUID as randomUUID5 } from "crypto";
|
|
11815
|
+
import { chmodSync, mkdirSync as mkdirSync6 } from "fs";
|
|
11816
|
+
import { homedir as homedir14 } from "os";
|
|
11817
|
+
import { dirname as dirname12, join as join24 } from "path";
|
|
11039
11818
|
function defaultRunsDbPath() {
|
|
11040
|
-
const root = process.env.XDG_STATE_HOME ||
|
|
11041
|
-
return process.env.AUTOPILOT_RUNS_DB ||
|
|
11819
|
+
const root = process.env.XDG_STATE_HOME || join24(homedir14(), ".local", "state");
|
|
11820
|
+
return process.env.AUTOPILOT_RUNS_DB || join24(root, "autopilot", "runs.db");
|
|
11042
11821
|
}
|
|
11043
11822
|
function validateOptionalPositiveInteger(name, value, minimum = 1) {
|
|
11044
11823
|
if (value !== null && (!Number.isSafeInteger(value) || value < minimum)) {
|
|
@@ -11054,7 +11833,7 @@ function stableJson(value) {
|
|
|
11054
11833
|
}
|
|
11055
11834
|
}
|
|
11056
11835
|
function sha256(value) {
|
|
11057
|
-
return
|
|
11836
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
11058
11837
|
}
|
|
11059
11838
|
function rowToRun(row) {
|
|
11060
11839
|
return {
|
|
@@ -11126,7 +11905,7 @@ var init_store = __esm({
|
|
|
11126
11905
|
RunStore = class {
|
|
11127
11906
|
db;
|
|
11128
11907
|
constructor(dbPath = defaultRunsDbPath()) {
|
|
11129
|
-
|
|
11908
|
+
mkdirSync6(dirname12(dbPath), { recursive: true, mode: 448 });
|
|
11130
11909
|
this.db = new Database2(dbPath);
|
|
11131
11910
|
this.db.pragma("journal_mode = WAL");
|
|
11132
11911
|
this.db.pragma("foreign_keys = ON");
|
|
@@ -11231,7 +12010,7 @@ var init_store = __esm({
|
|
|
11231
12010
|
if (maxCostUsd !== null && (!Number.isFinite(maxCostUsd) || maxCostUsd < 0.01)) {
|
|
11232
12011
|
throw new Error("maxCostUsd must be null or a finite number greater than or equal to 0.01");
|
|
11233
12012
|
}
|
|
11234
|
-
const id = input.id ?? input.worktree?.runId ??
|
|
12013
|
+
const id = input.id ?? input.worktree?.runId ?? randomUUID5();
|
|
11235
12014
|
if (!id.trim()) throw new Error("id must not be empty");
|
|
11236
12015
|
if (input.worktree && input.worktree.runId !== id) throw new Error("worktree runId must match run id");
|
|
11237
12016
|
const now2 = Date.now();
|
|
@@ -11425,7 +12204,7 @@ var init_store = __esm({
|
|
|
11425
12204
|
throw new Error("intervalMs must be an integer from 1000 through 604800000");
|
|
11426
12205
|
}
|
|
11427
12206
|
this.assertRunExists(input.runId);
|
|
11428
|
-
const id =
|
|
12207
|
+
const id = randomUUID5();
|
|
11429
12208
|
const now2 = Date.now();
|
|
11430
12209
|
this.db.prepare(`INSERT INTO run_timers
|
|
11431
12210
|
(id, run_id, condition_type, job_id, wake_at, interval_ms, status, max_attempts, created_at)
|
|
@@ -11721,6 +12500,12 @@ function capContentForHook(s) {
|
|
|
11721
12500
|
return `${cut}
|
|
11722
12501
|
[\u2026truncated for hook payload]`;
|
|
11723
12502
|
}
|
|
12503
|
+
function tracePermissionDecision(result2) {
|
|
12504
|
+
if (result2.decision === "deny") return "deny";
|
|
12505
|
+
if (result2.scope === "session") return "allow_session";
|
|
12506
|
+
if (result2.scope === "pattern") return "auto";
|
|
12507
|
+
return "allow";
|
|
12508
|
+
}
|
|
11724
12509
|
function isDiffCommand(cmd) {
|
|
11725
12510
|
const trimmed = cmd.trim();
|
|
11726
12511
|
if (/^git\s+show(?:\s|$)/.test(trimmed)) return true;
|
|
@@ -11741,11 +12526,13 @@ function normalizeToolOutput(result2) {
|
|
|
11741
12526
|
function truncateForError(s) {
|
|
11742
12527
|
return s.length <= 200 ? s : `${s.slice(0, 200)}... [${s.length - 200} more chars]`;
|
|
11743
12528
|
}
|
|
11744
|
-
var ALL_TOOLS, RESEARCH_WORKER_TOOL_NAMES, SENSITIVE_RESEARCH_PATHS, HOOK_RESULT_CONTENT_CAP_BYTES, ToolExecutor;
|
|
12529
|
+
var ALL_TOOLS, RESEARCH_WORKER_TOOL_NAMES, SENSITIVE_RESEARCH_PATHS, HOOK_RESULT_CONTENT_CAP_BYTES, ToolExecutor, TRACE_ARGS_INLINE_MAX, ToolTrace;
|
|
11745
12530
|
var init_executor = __esm({
|
|
11746
12531
|
"src/tools/executor.ts"() {
|
|
11747
12532
|
"use strict";
|
|
11748
12533
|
init_tool_error();
|
|
12534
|
+
init_verification();
|
|
12535
|
+
init_git_delta();
|
|
11749
12536
|
init_read();
|
|
11750
12537
|
init_write();
|
|
11751
12538
|
init_edit();
|
|
@@ -11825,17 +12612,25 @@ var init_executor = __esm({
|
|
|
11825
12612
|
* every `run` call regardless of caller (standard agent loop,
|
|
11826
12613
|
* code-mode sandbox, init turn, SDK, CLI print mode). */
|
|
11827
12614
|
hooks;
|
|
12615
|
+
/** Default session trace recorder; `ctx.trace` (set by the agent loop)
|
|
12616
|
+
* takes precedence per call. */
|
|
12617
|
+
trace;
|
|
11828
12618
|
constructor(tools = ALL_TOOLS, opts2) {
|
|
11829
12619
|
this.tools = new Map(tools.map((t) => [t.name, t]));
|
|
11830
12620
|
this.artifactStore = new ToolArtifactStore();
|
|
11831
12621
|
this.tools.set("expand_artifact", makeExpandArtifactTool(this.artifactStore));
|
|
11832
12622
|
this.hooks = opts2?.hooks ?? null;
|
|
12623
|
+
this.trace = opts2?.trace ?? null;
|
|
11833
12624
|
}
|
|
11834
12625
|
/** Swap or detach the hooks manager. Used by app startup so the
|
|
11835
12626
|
* executor created before the manager exists can pick it up later. */
|
|
11836
12627
|
setHooks(hooks) {
|
|
11837
12628
|
this.hooks = hooks;
|
|
11838
12629
|
}
|
|
12630
|
+
/** Swap or detach the default trace recorder (e.g. when the TUI starts a new session). */
|
|
12631
|
+
setTrace(trace) {
|
|
12632
|
+
this.trace = trace;
|
|
12633
|
+
}
|
|
11839
12634
|
list() {
|
|
11840
12635
|
return [...this.tools.values()];
|
|
11841
12636
|
}
|
|
@@ -11852,8 +12647,17 @@ var init_executor = __esm({
|
|
|
11852
12647
|
this.artifactStore.clear();
|
|
11853
12648
|
}
|
|
11854
12649
|
async run(call, askPermission, ctx, onFileChange) {
|
|
12650
|
+
const trace = ctx.trace ?? this.trace;
|
|
12651
|
+
if (!trace?.enabled) return this.runInner(call, askPermission, ctx, onFileChange, null);
|
|
12652
|
+
const tr = new ToolTrace(trace, ctx, call);
|
|
12653
|
+
const result2 = await this.runInner(call, askPermission, ctx, onFileChange, tr);
|
|
12654
|
+
tr.finish(result2);
|
|
12655
|
+
return result2;
|
|
12656
|
+
}
|
|
12657
|
+
async runInner(call, askPermission, ctx, onFileChange, tr) {
|
|
11855
12658
|
const tool = this.tools.get(call.name) ?? (call.name === "spawn_worker" ? this.tools.get("subagent") : void 0);
|
|
11856
12659
|
if (!tool) {
|
|
12660
|
+
tr?.call(null, false);
|
|
11857
12661
|
return {
|
|
11858
12662
|
tool_call_id: call.id,
|
|
11859
12663
|
name: call.name,
|
|
@@ -11867,6 +12671,7 @@ var init_executor = __esm({
|
|
|
11867
12671
|
try {
|
|
11868
12672
|
args = call.arguments.trim() ? JSON.parse(call.arguments) : {};
|
|
11869
12673
|
} catch (e) {
|
|
12674
|
+
tr?.call(null, tool.needsPermission);
|
|
11870
12675
|
return {
|
|
11871
12676
|
tool_call_id: call.id,
|
|
11872
12677
|
name: call.name,
|
|
@@ -11877,6 +12682,7 @@ var init_executor = __esm({
|
|
|
11877
12682
|
suggestion: "reformulate the tool call with valid JSON arguments"
|
|
11878
12683
|
};
|
|
11879
12684
|
}
|
|
12685
|
+
tr?.call(args, tool.needsPermission);
|
|
11880
12686
|
if (this.hooks?.hasEnabledHooks("PreToolUse")) {
|
|
11881
12687
|
const preOutcome = await this.hooks.fire(
|
|
11882
12688
|
"PreToolUse",
|
|
@@ -11908,8 +12714,10 @@ var init_executor = __esm({
|
|
|
11908
12714
|
if (tool.needsPermission) {
|
|
11909
12715
|
const sessionKey = this.permissionKey(tool, args);
|
|
11910
12716
|
if (!this.sessionAllowed.has(sessionKey)) {
|
|
12717
|
+
const askedAt = performance.now();
|
|
11911
12718
|
const raw = await askPermission({ tool, args, sessionKey });
|
|
11912
12719
|
const result2 = toPermissionResult(raw);
|
|
12720
|
+
tr?.permission(tracePermissionDecision(result2), Math.round(performance.now() - askedAt), result2.feedback);
|
|
11913
12721
|
if (result2.decision === "deny") {
|
|
11914
12722
|
const denied = {
|
|
11915
12723
|
tool_call_id: call.id,
|
|
@@ -11924,7 +12732,11 @@ var init_executor = __esm({
|
|
|
11924
12732
|
return denied;
|
|
11925
12733
|
}
|
|
11926
12734
|
if (result2.scope === "session") this.sessionAllowed.add(sessionKey);
|
|
12735
|
+
} else {
|
|
12736
|
+
tr?.permission("auto", 0);
|
|
11927
12737
|
}
|
|
12738
|
+
} else {
|
|
12739
|
+
tr?.permission("auto", 0);
|
|
11928
12740
|
}
|
|
11929
12741
|
let runStore = null;
|
|
11930
12742
|
let intentRecorded = false;
|
|
@@ -11938,6 +12750,7 @@ var init_executor = __esm({
|
|
|
11938
12750
|
const result2 = await tool.run(args, ctx);
|
|
11939
12751
|
toolCompleted = true;
|
|
11940
12752
|
const normalized = normalizeToolOutput(result2);
|
|
12753
|
+
tr?.raw(normalized);
|
|
11941
12754
|
if (runStore && ctx.runId) {
|
|
11942
12755
|
runStore.recordToolResult(ctx.runId, call.id, call.name, { ok: true, content: normalized.content });
|
|
11943
12756
|
}
|
|
@@ -11991,6 +12804,7 @@ var init_executor = __esm({
|
|
|
11991
12804
|
if (e instanceof RunToolBudgetExceededError) ctx.onRunBudgetExceeded?.(e.reason);
|
|
11992
12805
|
const err = wrapAsToolError(e);
|
|
11993
12806
|
const msg = `Error running ${call.name}: ${err.message}`;
|
|
12807
|
+
tr?.raw({ content: msg, rawBytes: Buffer.byteLength(msg, "utf8"), reducedBytes: Buffer.byteLength(msg, "utf8") });
|
|
11994
12808
|
const failure = {
|
|
11995
12809
|
tool_call_id: call.id,
|
|
11996
12810
|
name: call.name,
|
|
@@ -12051,6 +12865,121 @@ var init_executor = __esm({
|
|
|
12051
12865
|
return tool.name;
|
|
12052
12866
|
}
|
|
12053
12867
|
};
|
|
12868
|
+
TRACE_ARGS_INLINE_MAX = 2 * 1024;
|
|
12869
|
+
ToolTrace = class {
|
|
12870
|
+
constructor(trace, ctx, invocation) {
|
|
12871
|
+
this.trace = trace;
|
|
12872
|
+
this.invocation = invocation;
|
|
12873
|
+
this.turnId = ctx.turnId ?? null;
|
|
12874
|
+
this.parentId = ctx.codeModeParentToolCallId;
|
|
12875
|
+
this.sessionId = ctx.sessionId;
|
|
12876
|
+
}
|
|
12877
|
+
trace;
|
|
12878
|
+
invocation;
|
|
12879
|
+
startedAt = performance.now();
|
|
12880
|
+
turnId;
|
|
12881
|
+
parentId;
|
|
12882
|
+
sessionId;
|
|
12883
|
+
rawOutput = null;
|
|
12884
|
+
argsBlob = "";
|
|
12885
|
+
args = null;
|
|
12886
|
+
/** The call was parsed (args null when the arguments were not valid JSON). */
|
|
12887
|
+
call(args, needsPermission) {
|
|
12888
|
+
this.args = args;
|
|
12889
|
+
this.argsBlob = this.trace.blob(this.invocation.arguments) ?? "";
|
|
12890
|
+
if (this.parentId) return;
|
|
12891
|
+
const payload = {
|
|
12892
|
+
tool_call_id: this.invocation.id,
|
|
12893
|
+
name: this.invocation.name,
|
|
12894
|
+
args_blob: this.argsBlob,
|
|
12895
|
+
...args && this.invocation.arguments.length <= TRACE_ARGS_INLINE_MAX ? { args_inline: args } : {},
|
|
12896
|
+
needs_permission: needsPermission,
|
|
12897
|
+
code_mode_redirected: false
|
|
12898
|
+
};
|
|
12899
|
+
this.trace.emit("tool_call", payload, this.turnId);
|
|
12900
|
+
}
|
|
12901
|
+
permission(decision, latencyMs, feedback) {
|
|
12902
|
+
this.trace.emit(
|
|
12903
|
+
"permission_decision",
|
|
12904
|
+
{
|
|
12905
|
+
tool_call_id: this.invocation.id,
|
|
12906
|
+
decision,
|
|
12907
|
+
...feedback ? { feedback_text: feedback } : {},
|
|
12908
|
+
latency_ms: latencyMs
|
|
12909
|
+
},
|
|
12910
|
+
this.turnId
|
|
12911
|
+
);
|
|
12912
|
+
}
|
|
12913
|
+
/** The tool's output before reduction. */
|
|
12914
|
+
raw(output) {
|
|
12915
|
+
this.rawOutput = output;
|
|
12916
|
+
}
|
|
12917
|
+
finish(result2) {
|
|
12918
|
+
const durationMs = Math.round(performance.now() - this.startedAt);
|
|
12919
|
+
const rawContent = this.rawOutput?.content ?? result2.content;
|
|
12920
|
+
const rawBlob = this.trace.blob(rawContent) ?? "";
|
|
12921
|
+
this.outcomes(result2);
|
|
12922
|
+
if (this.parentId) {
|
|
12923
|
+
this.trace.emit(
|
|
12924
|
+
"code_mode_call",
|
|
12925
|
+
{
|
|
12926
|
+
parent_tool_call_id: this.parentId,
|
|
12927
|
+
name: this.invocation.name,
|
|
12928
|
+
args_blob: this.argsBlob,
|
|
12929
|
+
raw_blob: rawBlob,
|
|
12930
|
+
ok: result2.ok,
|
|
12931
|
+
duration_ms: durationMs
|
|
12932
|
+
},
|
|
12933
|
+
this.turnId
|
|
12934
|
+
);
|
|
12935
|
+
return;
|
|
12936
|
+
}
|
|
12937
|
+
const meta = this.rawOutput?.meta;
|
|
12938
|
+
const payload = {
|
|
12939
|
+
tool_call_id: this.invocation.id,
|
|
12940
|
+
name: this.invocation.name,
|
|
12941
|
+
ok: result2.ok,
|
|
12942
|
+
...result2.errorCode ? { error_code: result2.errorCode } : {},
|
|
12943
|
+
raw_blob: rawBlob,
|
|
12944
|
+
raw_bytes: Buffer.byteLength(rawContent, "utf8"),
|
|
12945
|
+
reduced_blob: rawContent === result2.content ? rawBlob : this.trace.blob(result2.content) ?? "",
|
|
12946
|
+
reduced_bytes: Buffer.byteLength(result2.content, "utf8"),
|
|
12947
|
+
...result2.artifactId ? { artifact_id: result2.artifactId } : {},
|
|
12948
|
+
...meta && meta.exitCode !== void 0 && meta.exitCode !== null ? { exit_code: meta.exitCode } : {},
|
|
12949
|
+
...meta?.timedOut !== void 0 ? { timed_out: meta.timedOut } : {},
|
|
12950
|
+
duration_ms: durationMs
|
|
12951
|
+
};
|
|
12952
|
+
this.trace.emit("tool_result", payload, this.turnId);
|
|
12953
|
+
}
|
|
12954
|
+
/** Outcome signals a bash call carries: a verification exit code, or a git revert of the last delta. */
|
|
12955
|
+
outcomes(result2) {
|
|
12956
|
+
if (this.invocation.name !== "bash") return;
|
|
12957
|
+
const command = this.args?.command;
|
|
12958
|
+
if (typeof command !== "string" || !command.trim()) return;
|
|
12959
|
+
const exitCode = this.rawOutput?.meta?.exitCode;
|
|
12960
|
+
const category = classifyVerification(command);
|
|
12961
|
+
if (category && typeof exitCode === "number") {
|
|
12962
|
+
this.trace.emit(
|
|
12963
|
+
"outcome",
|
|
12964
|
+
{
|
|
12965
|
+
kind: "verification",
|
|
12966
|
+
turn_id: this.turnId ?? "",
|
|
12967
|
+
tool_call_id: this.invocation.id,
|
|
12968
|
+
command,
|
|
12969
|
+
category,
|
|
12970
|
+
exit_code: exitCode
|
|
12971
|
+
},
|
|
12972
|
+
this.turnId
|
|
12973
|
+
);
|
|
12974
|
+
}
|
|
12975
|
+
if (result2.ok && this.sessionId) {
|
|
12976
|
+
const reverted = detectGitRevert(this.sessionId, command);
|
|
12977
|
+
if (reverted) {
|
|
12978
|
+
this.trace.emit("outcome", { kind: "reverted", turn_id: reverted.turnId, how: "git_checkout" }, this.turnId);
|
|
12979
|
+
}
|
|
12980
|
+
}
|
|
12981
|
+
}
|
|
12982
|
+
};
|
|
12054
12983
|
}
|
|
12055
12984
|
});
|
|
12056
12985
|
|
|
@@ -12068,11 +12997,11 @@ __export(sessions_exports, {
|
|
|
12068
12997
|
sessionsDir: () => sessionsDir
|
|
12069
12998
|
});
|
|
12070
12999
|
import { readFile as readFile13, writeFile as writeFile11, mkdir as mkdir11, readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
12071
|
-
import { homedir as
|
|
12072
|
-
import { join as
|
|
13000
|
+
import { homedir as homedir15 } from "os";
|
|
13001
|
+
import { join as join25 } from "path";
|
|
12073
13002
|
function sessionsDir() {
|
|
12074
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
12075
|
-
return
|
|
13003
|
+
const xdg = process.env.XDG_DATA_HOME || join25(homedir15(), ".local", "share");
|
|
13004
|
+
return join25(xdg, "kimiflare", "sessions");
|
|
12076
13005
|
}
|
|
12077
13006
|
function sanitize(text) {
|
|
12078
13007
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
@@ -12092,7 +13021,7 @@ function makeSessionId(firstPrompt) {
|
|
|
12092
13021
|
async function saveSession(file) {
|
|
12093
13022
|
const dir = sessionsDir();
|
|
12094
13023
|
await mkdir11(dir, { recursive: true });
|
|
12095
|
-
const path =
|
|
13024
|
+
const path = join25(dir, `${file.id}.json`);
|
|
12096
13025
|
await writeFile11(path, JSON.stringify(file, null, 2), "utf8");
|
|
12097
13026
|
return path;
|
|
12098
13027
|
}
|
|
@@ -12112,7 +13041,7 @@ async function listSessions(limit = 30, cwd) {
|
|
|
12112
13041
|
const summaries = [];
|
|
12113
13042
|
for (const name of entries) {
|
|
12114
13043
|
if (!name.endsWith(".json")) continue;
|
|
12115
|
-
const path =
|
|
13044
|
+
const path = join25(dir, name);
|
|
12116
13045
|
try {
|
|
12117
13046
|
const [s, raw] = await Promise.all([stat5(path), readFile13(path, "utf8")]);
|
|
12118
13047
|
const parsed = JSON.parse(raw);
|
|
@@ -12223,7 +13152,7 @@ var init_image = __esm({
|
|
|
12223
13152
|
|
|
12224
13153
|
// src/permissions-evaluator.ts
|
|
12225
13154
|
import { resolve as resolve5, relative as relative4 } from "path";
|
|
12226
|
-
import { homedir as
|
|
13155
|
+
import { homedir as homedir16 } from "os";
|
|
12227
13156
|
function evaluatePermissionRules(req, rules) {
|
|
12228
13157
|
const toolRules = rules[req.tool];
|
|
12229
13158
|
if (!toolRules) return "ask";
|
|
@@ -12239,7 +13168,7 @@ function evaluatePermissionRules(req, rules) {
|
|
|
12239
13168
|
}
|
|
12240
13169
|
function expandHome(path) {
|
|
12241
13170
|
if (path.startsWith("~/")) {
|
|
12242
|
-
return resolve5(
|
|
13171
|
+
return resolve5(homedir16(), path.slice(2));
|
|
12243
13172
|
}
|
|
12244
13173
|
return path;
|
|
12245
13174
|
}
|
|
@@ -12265,7 +13194,7 @@ function extractTarget(tool, args, cwd) {
|
|
|
12265
13194
|
function matchPattern(target, pattern, cwd) {
|
|
12266
13195
|
let expandedPattern = pattern;
|
|
12267
13196
|
if (pattern.startsWith("~/")) {
|
|
12268
|
-
expandedPattern = resolve5(
|
|
13197
|
+
expandedPattern = resolve5(homedir16(), pattern.slice(2));
|
|
12269
13198
|
}
|
|
12270
13199
|
if (target.startsWith(expandedPattern)) {
|
|
12271
13200
|
return true;
|
|
@@ -12342,9 +13271,237 @@ var init_classify = __esm({
|
|
|
12342
13271
|
}
|
|
12343
13272
|
});
|
|
12344
13273
|
|
|
13274
|
+
// src/trace/retention.ts
|
|
13275
|
+
var retention_exports = {};
|
|
13276
|
+
__export(retention_exports, {
|
|
13277
|
+
listTraceSessions: () => listTraceSessions,
|
|
13278
|
+
pruneTraces: () => pruneTraces
|
|
13279
|
+
});
|
|
13280
|
+
import { readdir as readdir5, rm as rm3, stat as stat6 } from "fs/promises";
|
|
13281
|
+
import { join as join26 } from "path";
|
|
13282
|
+
async function dirSize(path) {
|
|
13283
|
+
let total = 0;
|
|
13284
|
+
const entries = await readdir5(path, { withFileTypes: true });
|
|
13285
|
+
for (const entry of entries) {
|
|
13286
|
+
const p = join26(path, entry.name);
|
|
13287
|
+
if (entry.isDirectory()) total += await dirSize(p);
|
|
13288
|
+
else if (entry.isFile()) {
|
|
13289
|
+
try {
|
|
13290
|
+
total += (await stat6(p)).size;
|
|
13291
|
+
} catch {
|
|
13292
|
+
}
|
|
13293
|
+
}
|
|
13294
|
+
}
|
|
13295
|
+
return total;
|
|
13296
|
+
}
|
|
13297
|
+
async function listTraceSessions(root) {
|
|
13298
|
+
const base = traceRoot(root);
|
|
13299
|
+
let names;
|
|
13300
|
+
try {
|
|
13301
|
+
names = await readdir5(base);
|
|
13302
|
+
} catch {
|
|
13303
|
+
return [];
|
|
13304
|
+
}
|
|
13305
|
+
const sessions = [];
|
|
13306
|
+
for (const name of names) {
|
|
13307
|
+
const path = join26(base, name);
|
|
13308
|
+
try {
|
|
13309
|
+
const s = await stat6(path);
|
|
13310
|
+
if (!s.isDirectory()) continue;
|
|
13311
|
+
let mtime = s.mtime;
|
|
13312
|
+
try {
|
|
13313
|
+
mtime = (await stat6(join26(path, TRACE_FILE_NAME))).mtime;
|
|
13314
|
+
} catch {
|
|
13315
|
+
}
|
|
13316
|
+
sessions.push({ sessionId: name, path, mtime, bytes: await dirSize(path) });
|
|
13317
|
+
} catch {
|
|
13318
|
+
}
|
|
13319
|
+
}
|
|
13320
|
+
sessions.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
13321
|
+
return sessions;
|
|
13322
|
+
}
|
|
13323
|
+
async function pruneTraces(opts2 = {}) {
|
|
13324
|
+
const maxAgeDays = opts2.maxAgeDays ?? RETENTION.traceMaxAgeDays;
|
|
13325
|
+
const maxTotalBytes = opts2.maxTotalBytes ?? RETENTION.traceMaxTotalBytes;
|
|
13326
|
+
const maxSessionBytes = opts2.maxSessionBytes ?? RETENTION.traceMaxSessionBytes;
|
|
13327
|
+
const now2 = opts2.now ?? Date.now();
|
|
13328
|
+
const cutoff = now2 - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
13329
|
+
const removed = [];
|
|
13330
|
+
let sessions;
|
|
13331
|
+
try {
|
|
13332
|
+
sessions = await listTraceSessions(opts2.root);
|
|
13333
|
+
} catch {
|
|
13334
|
+
return { removed, keptBytes: 0 };
|
|
13335
|
+
}
|
|
13336
|
+
const remove = async (s) => {
|
|
13337
|
+
try {
|
|
13338
|
+
await rm3(s.path, { recursive: true, force: true });
|
|
13339
|
+
removed.push(s.sessionId);
|
|
13340
|
+
} catch {
|
|
13341
|
+
}
|
|
13342
|
+
};
|
|
13343
|
+
const kept = [];
|
|
13344
|
+
for (const s of sessions) {
|
|
13345
|
+
if (s.mtime.getTime() < cutoff || s.bytes > maxSessionBytes) await remove(s);
|
|
13346
|
+
else kept.push(s);
|
|
13347
|
+
}
|
|
13348
|
+
let total = kept.reduce((sum, s) => sum + s.bytes, 0);
|
|
13349
|
+
while (total > maxTotalBytes && kept.length > 0) {
|
|
13350
|
+
const oldest = kept.pop();
|
|
13351
|
+
await remove(oldest);
|
|
13352
|
+
total -= oldest.bytes;
|
|
13353
|
+
}
|
|
13354
|
+
return { removed, keptBytes: total };
|
|
13355
|
+
}
|
|
13356
|
+
var init_retention = __esm({
|
|
13357
|
+
"src/trace/retention.ts"() {
|
|
13358
|
+
"use strict";
|
|
13359
|
+
init_storage_limits();
|
|
13360
|
+
init_paths();
|
|
13361
|
+
}
|
|
13362
|
+
});
|
|
13363
|
+
|
|
13364
|
+
// src/trace/session-start.ts
|
|
13365
|
+
import { execFile as execFile5 } from "child_process";
|
|
13366
|
+
import { existsSync as existsSync5 } from "fs";
|
|
13367
|
+
import { dirname as dirname13, join as join27 } from "path";
|
|
13368
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13369
|
+
import { promisify as promisify5 } from "util";
|
|
13370
|
+
async function git2(args, cwd) {
|
|
13371
|
+
try {
|
|
13372
|
+
const { stdout } = await execFileAsync4("git", args, { cwd, timeout: 3e3, windowsHide: true });
|
|
13373
|
+
const out = stdout.trim();
|
|
13374
|
+
return out || null;
|
|
13375
|
+
} catch {
|
|
13376
|
+
return null;
|
|
13377
|
+
}
|
|
13378
|
+
}
|
|
13379
|
+
function harnessCheckoutDir() {
|
|
13380
|
+
const here = dirname13(fileURLToPath4(import.meta.url));
|
|
13381
|
+
for (const candidate of [join27(here, "..", ".."), join27(here, "..")]) {
|
|
13382
|
+
if (existsSync5(join27(candidate, "package.json")) && existsSync5(join27(candidate, ".git"))) return candidate;
|
|
13383
|
+
}
|
|
13384
|
+
return null;
|
|
13385
|
+
}
|
|
13386
|
+
async function resolveHarnessCommit() {
|
|
13387
|
+
const dir = harnessCheckoutDir();
|
|
13388
|
+
if (!dir) return null;
|
|
13389
|
+
return git2(["rev-parse", "HEAD"], dir);
|
|
13390
|
+
}
|
|
13391
|
+
async function collectSessionStart(opts2, blob) {
|
|
13392
|
+
const toolsJson = JSON.stringify(opts2.tools);
|
|
13393
|
+
const [harnessCommit, repoRemote, repoHead] = await Promise.all([
|
|
13394
|
+
resolveHarnessCommit(),
|
|
13395
|
+
git2(["remote", "get-url", "origin"], opts2.cwd),
|
|
13396
|
+
git2(["rev-parse", "HEAD"], opts2.cwd)
|
|
13397
|
+
]);
|
|
13398
|
+
return {
|
|
13399
|
+
autopilot_version: getAppVersion(),
|
|
13400
|
+
harness_commit: harnessCommit,
|
|
13401
|
+
cwd: opts2.cwd,
|
|
13402
|
+
repo_remote: repoRemote,
|
|
13403
|
+
repo_head: repoHead,
|
|
13404
|
+
model: opts2.model,
|
|
13405
|
+
mode: opts2.mode,
|
|
13406
|
+
config: redactConfig(opts2.config ?? {}),
|
|
13407
|
+
tool_profile: opts2.toolProfile ?? "full",
|
|
13408
|
+
tool_schemas_sha: sha256Hex(toolsJson),
|
|
13409
|
+
tools_blob: blob(toolsJson) ?? "",
|
|
13410
|
+
system_prompt_sha: sha256Hex(opts2.systemPrompt)
|
|
13411
|
+
};
|
|
13412
|
+
}
|
|
13413
|
+
async function emitSessionStart(recorder, opts2) {
|
|
13414
|
+
if (!recorder.enabled) return;
|
|
13415
|
+
const payload = await collectSessionStart(opts2, (content) => recorder.blob(content));
|
|
13416
|
+
recorder.emit("session_start", payload, null);
|
|
13417
|
+
}
|
|
13418
|
+
var execFileAsync4;
|
|
13419
|
+
var init_session_start = __esm({
|
|
13420
|
+
"src/trace/session-start.ts"() {
|
|
13421
|
+
"use strict";
|
|
13422
|
+
init_version();
|
|
13423
|
+
init_blobs();
|
|
13424
|
+
init_redact();
|
|
13425
|
+
execFileAsync4 = promisify5(execFile5);
|
|
13426
|
+
}
|
|
13427
|
+
});
|
|
13428
|
+
|
|
13429
|
+
// src/trace/compaction.ts
|
|
13430
|
+
function traceCompiledCompaction(trace, result2, turnId = null) {
|
|
13431
|
+
if (!trace?.enabled || result2.metrics.rawTurnsRemoved === 0) return;
|
|
13432
|
+
trace.emit(
|
|
13433
|
+
"compaction",
|
|
13434
|
+
{
|
|
13435
|
+
method: "compiled",
|
|
13436
|
+
tokens_before: result2.metrics.estimatedTokensBefore,
|
|
13437
|
+
tokens_after: result2.metrics.estimatedTokensAfter,
|
|
13438
|
+
turns_removed: result2.metrics.rawTurnsRemoved,
|
|
13439
|
+
artifacts_archived: result2.metrics.archivedArtifacts
|
|
13440
|
+
},
|
|
13441
|
+
turnId
|
|
13442
|
+
);
|
|
13443
|
+
}
|
|
13444
|
+
function traceLlmCompaction(trace, before, result2, turnId = null) {
|
|
13445
|
+
if (!trace?.enabled || result2.replacedCount === 0) return;
|
|
13446
|
+
trace.emit(
|
|
13447
|
+
"compaction",
|
|
13448
|
+
{
|
|
13449
|
+
method: "llm",
|
|
13450
|
+
tokens_before: estimatePromptTokens(before),
|
|
13451
|
+
tokens_after: estimatePromptTokens(result2.newMessages),
|
|
13452
|
+
turns_removed: result2.replacedCount,
|
|
13453
|
+
...result2.summary ? { summary_blob: trace.blob(result2.summary) ?? "" } : {}
|
|
13454
|
+
},
|
|
13455
|
+
turnId
|
|
13456
|
+
);
|
|
13457
|
+
}
|
|
13458
|
+
var init_compaction = __esm({
|
|
13459
|
+
"src/trace/compaction.ts"() {
|
|
13460
|
+
"use strict";
|
|
13461
|
+
init_artifact_compaction();
|
|
13462
|
+
}
|
|
13463
|
+
});
|
|
13464
|
+
|
|
13465
|
+
// src/trace/next-message.ts
|
|
13466
|
+
function classifyNextMessage(text) {
|
|
13467
|
+
const trimmed = text.trim();
|
|
13468
|
+
if (!trimmed) return "other";
|
|
13469
|
+
if (trimmed.startsWith("/")) return "other";
|
|
13470
|
+
if (CORRECTION.test(trimmed)) return "correction";
|
|
13471
|
+
if (CONTINUE.test(trimmed)) return "continue";
|
|
13472
|
+
if (trimmed.endsWith("?")) return "question";
|
|
13473
|
+
return "new_task";
|
|
13474
|
+
}
|
|
13475
|
+
var CORRECTION, CONTINUE;
|
|
13476
|
+
var init_next_message = __esm({
|
|
13477
|
+
"src/trace/next-message.ts"() {
|
|
13478
|
+
"use strict";
|
|
13479
|
+
CORRECTION = /^(no\b|nope\b|wrong\b|that's not\b|that is not\b|thats not\b|not what\b|undo\b|revert\b|stop\b|don't\b|dont\b)/i;
|
|
13480
|
+
CONTINUE = /^(go on\b|continue\b|yes\b|yep\b|yeah\b|ok\b|okay\b|do it\b|proceed\b|sure\b|sounds good\b|lgtm\b|looks good\b|next\b)/i;
|
|
13481
|
+
}
|
|
13482
|
+
});
|
|
13483
|
+
|
|
13484
|
+
// src/trace/index.ts
|
|
13485
|
+
var init_trace = __esm({
|
|
13486
|
+
"src/trace/index.ts"() {
|
|
13487
|
+
"use strict";
|
|
13488
|
+
init_types();
|
|
13489
|
+
init_recorder();
|
|
13490
|
+
init_blobs();
|
|
13491
|
+
init_redact();
|
|
13492
|
+
init_paths();
|
|
13493
|
+
init_retention();
|
|
13494
|
+
init_session_start();
|
|
13495
|
+
init_compaction();
|
|
13496
|
+
init_verification();
|
|
13497
|
+
init_next_message();
|
|
13498
|
+
init_git_delta();
|
|
13499
|
+
}
|
|
13500
|
+
});
|
|
13501
|
+
|
|
12345
13502
|
// src/hooks/types.ts
|
|
12346
13503
|
var HOOK_EVENTS;
|
|
12347
|
-
var
|
|
13504
|
+
var init_types2 = __esm({
|
|
12348
13505
|
"src/hooks/types.ts"() {
|
|
12349
13506
|
"use strict";
|
|
12350
13507
|
HOOK_EVENTS = [
|
|
@@ -12358,21 +13515,21 @@ var init_types = __esm({
|
|
|
12358
13515
|
});
|
|
12359
13516
|
|
|
12360
13517
|
// src/hooks/settings.ts
|
|
12361
|
-
import { existsSync as
|
|
12362
|
-
import { homedir as
|
|
12363
|
-
import { join as
|
|
12364
|
-
import { createHash as
|
|
13518
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
13519
|
+
import { homedir as homedir17 } from "os";
|
|
13520
|
+
import { join as join28, dirname as dirname14 } from "path";
|
|
13521
|
+
import { createHash as createHash3 } from "crypto";
|
|
12365
13522
|
function globalSettingsPath() {
|
|
12366
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
12367
|
-
return
|
|
13523
|
+
const xdg = process.env.XDG_CONFIG_HOME || join28(homedir17(), ".config");
|
|
13524
|
+
return join28(xdg, "kimiflare", "settings.json");
|
|
12368
13525
|
}
|
|
12369
13526
|
function projectSettingsPath(cwd) {
|
|
12370
|
-
return
|
|
13527
|
+
return join28(cwd, ".kimiflare", "settings.json");
|
|
12371
13528
|
}
|
|
12372
13529
|
function readSettingsFile(path) {
|
|
12373
|
-
if (!
|
|
13530
|
+
if (!existsSync6(path)) return null;
|
|
12374
13531
|
try {
|
|
12375
|
-
const raw =
|
|
13532
|
+
const raw = readFileSync5(path, "utf8");
|
|
12376
13533
|
const parsed = JSON.parse(raw);
|
|
12377
13534
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
12378
13535
|
} catch {
|
|
@@ -12380,7 +13537,7 @@ function readSettingsFile(path) {
|
|
|
12380
13537
|
}
|
|
12381
13538
|
}
|
|
12382
13539
|
function deriveHookId(event, command) {
|
|
12383
|
-
const h =
|
|
13540
|
+
const h = createHash3("sha256").update(`${event}\0${command}`).digest("hex");
|
|
12384
13541
|
return h.slice(0, 8);
|
|
12385
13542
|
}
|
|
12386
13543
|
function normalizeHook(event, raw, source) {
|
|
@@ -12443,8 +13600,8 @@ function appendHook(scope, cwd, event, hook) {
|
|
|
12443
13600
|
return rest;
|
|
12444
13601
|
});
|
|
12445
13602
|
existing.hooks = hooks;
|
|
12446
|
-
|
|
12447
|
-
|
|
13603
|
+
mkdirSync7(dirname14(path), { recursive: true });
|
|
13604
|
+
writeFileSync4(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
12448
13605
|
return path;
|
|
12449
13606
|
}
|
|
12450
13607
|
function setHookEnabled(cwd, id, enabled2) {
|
|
@@ -12469,8 +13626,8 @@ function setHookEnabled(cwd, id, enabled2) {
|
|
|
12469
13626
|
}
|
|
12470
13627
|
}
|
|
12471
13628
|
if (changed) {
|
|
12472
|
-
|
|
12473
|
-
|
|
13629
|
+
mkdirSync7(dirname14(path), { recursive: true });
|
|
13630
|
+
writeFileSync4(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
12474
13631
|
return path;
|
|
12475
13632
|
}
|
|
12476
13633
|
void scope;
|
|
@@ -12480,7 +13637,7 @@ function setHookEnabled(cwd, id, enabled2) {
|
|
|
12480
13637
|
var init_settings = __esm({
|
|
12481
13638
|
"src/hooks/settings.ts"() {
|
|
12482
13639
|
"use strict";
|
|
12483
|
-
|
|
13640
|
+
init_types2();
|
|
12484
13641
|
}
|
|
12485
13642
|
});
|
|
12486
13643
|
|
|
@@ -12698,11 +13855,11 @@ __export(cost_verify_exports, {
|
|
|
12698
13855
|
verifyTurn: () => verifyTurn
|
|
12699
13856
|
});
|
|
12700
13857
|
import { readFile as readFile16 } from "fs/promises";
|
|
12701
|
-
import { homedir as
|
|
12702
|
-
import { join as
|
|
13858
|
+
import { homedir as homedir18 } from "os";
|
|
13859
|
+
import { join as join29 } from "path";
|
|
12703
13860
|
function usagePath2() {
|
|
12704
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
12705
|
-
return
|
|
13861
|
+
const xdg = process.env.XDG_DATA_HOME || join29(homedir18(), ".local", "share");
|
|
13862
|
+
return join29(xdg, "kimiflare", "usage.json");
|
|
12706
13863
|
}
|
|
12707
13864
|
async function loadSessions() {
|
|
12708
13865
|
try {
|
|
@@ -12795,7 +13952,7 @@ var init_cost_verify = __esm({
|
|
|
12795
13952
|
|
|
12796
13953
|
// src/cost-attribution/types.ts
|
|
12797
13954
|
var ALL_CATEGORIES;
|
|
12798
|
-
var
|
|
13955
|
+
var init_types3 = __esm({
|
|
12799
13956
|
"src/cost-attribution/types.ts"() {
|
|
12800
13957
|
"use strict";
|
|
12801
13958
|
ALL_CATEGORIES = [
|
|
@@ -12874,7 +14031,7 @@ function buildReport(opts2) {
|
|
|
12874
14031
|
var init_report = __esm({
|
|
12875
14032
|
"src/cost-attribution/report.ts"() {
|
|
12876
14033
|
"use strict";
|
|
12877
|
-
|
|
14034
|
+
init_types3();
|
|
12878
14035
|
}
|
|
12879
14036
|
});
|
|
12880
14037
|
|
|
@@ -13238,11 +14395,11 @@ var init_heuristic = __esm({
|
|
|
13238
14395
|
|
|
13239
14396
|
// src/cost-attribution/classify-from-session.ts
|
|
13240
14397
|
import { readFile as readFile17 } from "fs/promises";
|
|
13241
|
-
import { join as
|
|
13242
|
-
import { homedir as
|
|
14398
|
+
import { join as join30 } from "path";
|
|
14399
|
+
import { homedir as homedir19 } from "os";
|
|
13243
14400
|
function sessionsDir2() {
|
|
13244
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
13245
|
-
return
|
|
14401
|
+
const xdg = process.env.XDG_DATA_HOME || join30(homedir19(), ".local", "share");
|
|
14402
|
+
return join30(xdg, "kimiflare", "sessions");
|
|
13246
14403
|
}
|
|
13247
14404
|
function parseToolCalls(calls) {
|
|
13248
14405
|
return calls.map((c) => {
|
|
@@ -13256,7 +14413,7 @@ function parseToolCalls(calls) {
|
|
|
13256
14413
|
}
|
|
13257
14414
|
async function classifyFromSessionFile(sessionId) {
|
|
13258
14415
|
try {
|
|
13259
|
-
const raw = await readFile17(
|
|
14416
|
+
const raw = await readFile17(join30(sessionsDir2(), `${sessionId}.json`), "utf8");
|
|
13260
14417
|
const session = JSON.parse(raw);
|
|
13261
14418
|
const messages = session.messages ?? [];
|
|
13262
14419
|
const turns = [];
|
|
@@ -13290,14 +14447,14 @@ __export(cli_exports, {
|
|
|
13290
14447
|
runCostCommand: () => runCostCommand
|
|
13291
14448
|
});
|
|
13292
14449
|
import { readFile as readFile18 } from "fs/promises";
|
|
13293
|
-
import { join as
|
|
13294
|
-
import { homedir as
|
|
14450
|
+
import { join as join31 } from "path";
|
|
14451
|
+
import { homedir as homedir20 } from "os";
|
|
13295
14452
|
function usageDir2() {
|
|
13296
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
13297
|
-
return
|
|
14453
|
+
const xdg = process.env.XDG_DATA_HOME || join31(homedir20(), ".local", "share");
|
|
14454
|
+
return join31(xdg, "kimiflare");
|
|
13298
14455
|
}
|
|
13299
14456
|
function usagePath3() {
|
|
13300
|
-
return
|
|
14457
|
+
return join31(usageDir2(), "usage.json");
|
|
13301
14458
|
}
|
|
13302
14459
|
function today2() {
|
|
13303
14460
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -13399,7 +14556,7 @@ __export(aster_workspaces_exports, {
|
|
|
13399
14556
|
findAsterWorkspace: () => findAsterWorkspace,
|
|
13400
14557
|
loadAsterServerConfig: () => loadAsterServerConfig
|
|
13401
14558
|
});
|
|
13402
|
-
import { readFile as readFile19, realpath as realpath3, stat as
|
|
14559
|
+
import { readFile as readFile19, realpath as realpath3, stat as stat7 } from "fs/promises";
|
|
13403
14560
|
import { isAbsolute as isAbsolute3, relative as relative5, sep as sep3 } from "path";
|
|
13404
14561
|
async function loadAsterServerConfig(configPath3 = process.env.AUTOPILOT_ASTER_CONFIG || "/etc/autopilot/aster.json") {
|
|
13405
14562
|
let raw;
|
|
@@ -13441,7 +14598,7 @@ async function loadAsterServerConfig(configPath3 = process.env.AUTOPILOT_ASTER_C
|
|
|
13441
14598
|
let rootPath;
|
|
13442
14599
|
try {
|
|
13443
14600
|
rootPath = await realpath3(workspace.rootPath);
|
|
13444
|
-
if (!(await
|
|
14601
|
+
if (!(await stat7(rootPath)).isDirectory()) throw new Error("not a directory");
|
|
13445
14602
|
} catch {
|
|
13446
14603
|
throw new AsterConfigError();
|
|
13447
14604
|
}
|
|
@@ -13481,16 +14638,16 @@ __export(aster_store_exports, {
|
|
|
13481
14638
|
defaultAsterDbPath: () => defaultAsterDbPath
|
|
13482
14639
|
});
|
|
13483
14640
|
import Database3 from "better-sqlite3";
|
|
13484
|
-
import { createHash as
|
|
13485
|
-
import { chmodSync as chmodSync2, mkdirSync as
|
|
13486
|
-
import { homedir as
|
|
13487
|
-
import { dirname as
|
|
14641
|
+
import { createHash as createHash4, randomBytes as randomBytes2, randomUUID as randomUUID6 } from "crypto";
|
|
14642
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync8 } from "fs";
|
|
14643
|
+
import { homedir as homedir21 } from "os";
|
|
14644
|
+
import { dirname as dirname15, join as join32 } from "path";
|
|
13488
14645
|
function defaultAsterDbPath() {
|
|
13489
|
-
const root = process.env.XDG_STATE_HOME ||
|
|
13490
|
-
return process.env.AUTOPILOT_ASTER_DB ||
|
|
14646
|
+
const root = process.env.XDG_STATE_HOME || join32(homedir21(), ".local", "state");
|
|
14647
|
+
return process.env.AUTOPILOT_ASTER_DB || join32(root, "autopilot", "aster.db");
|
|
13491
14648
|
}
|
|
13492
14649
|
function hashToken(token) {
|
|
13493
|
-
return
|
|
14650
|
+
return createHash4("sha256").update(token).digest("hex");
|
|
13494
14651
|
}
|
|
13495
14652
|
function cellMappingFromRow(row) {
|
|
13496
14653
|
return {
|
|
@@ -13554,9 +14711,9 @@ var init_aster_store = __esm({
|
|
|
13554
14711
|
AsterStore = class {
|
|
13555
14712
|
db;
|
|
13556
14713
|
constructor(dbPath = defaultAsterDbPath()) {
|
|
13557
|
-
|
|
14714
|
+
mkdirSync8(dirname15(dbPath), { recursive: true, mode: 448 });
|
|
13558
14715
|
try {
|
|
13559
|
-
chmodSync2(
|
|
14716
|
+
chmodSync2(dirname15(dbPath), 448);
|
|
13560
14717
|
} catch {
|
|
13561
14718
|
}
|
|
13562
14719
|
this.db = new Database3(dbPath);
|
|
@@ -13667,8 +14824,8 @@ var init_aster_store = __esm({
|
|
|
13667
14824
|
this.db.close();
|
|
13668
14825
|
}
|
|
13669
14826
|
reserveCellConversation(input) {
|
|
13670
|
-
const keyHash =
|
|
13671
|
-
const fingerprint =
|
|
14827
|
+
const keyHash = createHash4("sha256").update(input.idempotencyKey).digest("hex");
|
|
14828
|
+
const fingerprint = createHash4("sha256").update(JSON.stringify([input.workspaceId, input.model])).digest("hex");
|
|
13672
14829
|
const reserve = this.db.transaction(() => {
|
|
13673
14830
|
const existing = this.db.prepare("SELECT * FROM aster_cell_mappings WHERE credential_id = ? AND create_key_hash = ?").get(input.credentialId, keyHash);
|
|
13674
14831
|
if (existing) {
|
|
@@ -13719,7 +14876,7 @@ var init_aster_store = __esm({
|
|
|
13719
14876
|
const scopes = [...new Set(input.scopes)];
|
|
13720
14877
|
if (scopes.length === 0 || scopes.some((scope) => !VALID_SCOPES.has(scope))) throw new Error("credential scopes are invalid");
|
|
13721
14878
|
if (!Number.isSafeInteger(input.expiresAt) || input.expiresAt <= Date.now()) throw new Error("credential expiry must be in the future");
|
|
13722
|
-
const id =
|
|
14879
|
+
const id = randomUUID6();
|
|
13723
14880
|
const token = `aster_${randomBytes2(32).toString("base64url")}`;
|
|
13724
14881
|
const now2 = Date.now();
|
|
13725
14882
|
this.db.prepare(`INSERT INTO aster_credentials
|
|
@@ -13892,7 +15049,7 @@ var init_aster_store = __esm({
|
|
|
13892
15049
|
}));
|
|
13893
15050
|
}
|
|
13894
15051
|
createApproval(input) {
|
|
13895
|
-
const id =
|
|
15052
|
+
const id = randomUUID6();
|
|
13896
15053
|
const now2 = Date.now();
|
|
13897
15054
|
this.db.prepare(`INSERT INTO aster_approvals
|
|
13898
15055
|
(id, conversation_id, run_id, tool_name, explanation, arguments_json, status, expires_at, created_at)
|
|
@@ -14553,13 +15710,13 @@ var init_openapi = __esm({
|
|
|
14553
15710
|
});
|
|
14554
15711
|
|
|
14555
15712
|
// src/runs/worktrees.ts
|
|
14556
|
-
import { execFile as
|
|
15713
|
+
import { execFile as execFile6 } from "child_process";
|
|
14557
15714
|
import { access as access2, chmod as chmod2, mkdir as mkdir12, realpath as realpath4 } from "fs/promises";
|
|
14558
|
-
import { homedir as
|
|
14559
|
-
import { isAbsolute as isAbsolute4, join as
|
|
15715
|
+
import { homedir as homedir22 } from "os";
|
|
15716
|
+
import { isAbsolute as isAbsolute4, join as join33, parse, relative as relative6, resolve as resolve7, sep as sep4 } from "path";
|
|
14560
15717
|
function defaultWorktreesDir() {
|
|
14561
|
-
const dataHome = process.env.XDG_DATA_HOME ||
|
|
14562
|
-
return
|
|
15718
|
+
const dataHome = process.env.XDG_DATA_HOME || join33(homedir22(), ".local", "share");
|
|
15719
|
+
return join33(dataHome, "autopilot", "worktrees");
|
|
14563
15720
|
}
|
|
14564
15721
|
function isOutside(path) {
|
|
14565
15722
|
return path === ".." || path.startsWith(`..${sep4}`) || isAbsolute4(path);
|
|
@@ -14570,7 +15727,7 @@ function isWithin2(parent, candidate) {
|
|
|
14570
15727
|
}
|
|
14571
15728
|
function runGit(args, cwd) {
|
|
14572
15729
|
return new Promise((resolveOutput, reject) => {
|
|
14573
|
-
|
|
15730
|
+
execFile6("git", args, { cwd, encoding: "utf8", timeout: 3e4, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
|
|
14574
15731
|
if (error) {
|
|
14575
15732
|
const detail = stderr.trim();
|
|
14576
15733
|
reject(new Error(`git ${args[0]} failed${detail ? `: ${detail}` : ""}`, { cause: error }));
|
|
@@ -14610,7 +15767,7 @@ var init_worktrees = __esm({
|
|
|
14610
15767
|
throw new Error("worktree root must be outside the source repository");
|
|
14611
15768
|
}
|
|
14612
15769
|
await chmod2(worktreesRoot, 448);
|
|
14613
|
-
const worktreePath =
|
|
15770
|
+
const worktreePath = join33(worktreesRoot, runId);
|
|
14614
15771
|
try {
|
|
14615
15772
|
await access2(worktreePath);
|
|
14616
15773
|
throw new Error(`worktree path already exists for run ${runId}`);
|
|
@@ -14787,11 +15944,11 @@ __export(openrouter_catalog_exports, {
|
|
|
14787
15944
|
mapOpenRouterModel: () => mapOpenRouterModel
|
|
14788
15945
|
});
|
|
14789
15946
|
import { readFile as readFile20, writeFile as writeFile12, mkdir as mkdir13 } from "fs/promises";
|
|
14790
|
-
import { homedir as
|
|
14791
|
-
import { join as
|
|
15947
|
+
import { homedir as homedir23 } from "os";
|
|
15948
|
+
import { join as join34, dirname as dirname16 } from "path";
|
|
14792
15949
|
function catalogCachePath() {
|
|
14793
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
14794
|
-
return
|
|
15950
|
+
const xdg = process.env.XDG_CONFIG_HOME || join34(homedir23(), ".config");
|
|
15951
|
+
return join34(xdg, "kimiflare", "openrouter-models.json");
|
|
14795
15952
|
}
|
|
14796
15953
|
function perMtok(perToken) {
|
|
14797
15954
|
const n = Number(perToken);
|
|
@@ -14852,7 +16009,7 @@ async function readCache3() {
|
|
|
14852
16009
|
async function writeCache3(models) {
|
|
14853
16010
|
const path = catalogCachePath();
|
|
14854
16011
|
try {
|
|
14855
|
-
await mkdir13(
|
|
16012
|
+
await mkdir13(dirname16(path), { recursive: true });
|
|
14856
16013
|
const cache = { version: CACHE_VERSION, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), models };
|
|
14857
16014
|
await writeFile12(path, JSON.stringify(cache, null, 2), "utf8");
|
|
14858
16015
|
} catch {
|
|
@@ -15083,7 +16240,7 @@ var init_aster_tools = __esm({
|
|
|
15083
16240
|
init_edit();
|
|
15084
16241
|
init_read();
|
|
15085
16242
|
init_write();
|
|
15086
|
-
|
|
16243
|
+
init_paths2();
|
|
15087
16244
|
ASTR_TOOLS = [readTool, writeTool, editTool];
|
|
15088
16245
|
SENSITIVE_NAMES = /* @__PURE__ */ new Set([
|
|
15089
16246
|
".git",
|
|
@@ -15105,9 +16262,9 @@ var init_aster_tools = __esm({
|
|
|
15105
16262
|
|
|
15106
16263
|
// src/server/aster-memory.ts
|
|
15107
16264
|
import Database4 from "better-sqlite3";
|
|
15108
|
-
import { randomUUID as
|
|
15109
|
-
import { chmodSync as chmodSync3, mkdirSync as
|
|
15110
|
-
import { dirname as
|
|
16265
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
16266
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync9 } from "fs";
|
|
16267
|
+
import { dirname as dirname17 } from "path";
|
|
15111
16268
|
function rowToItem(row) {
|
|
15112
16269
|
return {
|
|
15113
16270
|
id: row.id,
|
|
@@ -15160,7 +16317,7 @@ var init_aster_memory = __esm({
|
|
|
15160
16317
|
AsterMemory = class {
|
|
15161
16318
|
constructor(dbPath = defaultAsterDbPath(), options = {}) {
|
|
15162
16319
|
this.options = options;
|
|
15163
|
-
|
|
16320
|
+
mkdirSync9(dirname17(dbPath), { recursive: true, mode: 448 });
|
|
15164
16321
|
this.db = new Database4(dbPath);
|
|
15165
16322
|
this.db.pragma("journal_mode = WAL");
|
|
15166
16323
|
this.db.pragma("busy_timeout = 5000");
|
|
@@ -15224,7 +16381,7 @@ var init_aster_memory = __esm({
|
|
|
15224
16381
|
this.db.prepare("UPDATE aster_memories SET title = ?, content = ?, pinned = ?, updated_at = ? WHERE id = ?").run(title, content, existing.pinned || input.pinned ? 1 : 0, now2, existing.id);
|
|
15225
16382
|
return { item: this.get(existing.id), change: "updated" };
|
|
15226
16383
|
}
|
|
15227
|
-
const id =
|
|
16384
|
+
const id = randomUUID7();
|
|
15228
16385
|
this.db.prepare(`INSERT INTO aster_memories (id, kind, key, title, content, pinned, source_conversation_id, created_at, updated_at)
|
|
15229
16386
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.kind, input.key ?? null, title, content, input.pinned ? 1 : 0, input.sourceConversationId ?? null, now2, now2);
|
|
15230
16387
|
return { item: this.get(id), change: "added" };
|
|
@@ -15436,9 +16593,9 @@ var init_aster_github_proxy = __esm({
|
|
|
15436
16593
|
}
|
|
15437
16594
|
handle(req, res) {
|
|
15438
16595
|
const url = new URL(req.url ?? "/", "http://proxy.local");
|
|
15439
|
-
const
|
|
15440
|
-
const api =
|
|
15441
|
-
if (!
|
|
16596
|
+
const git3 = url.pathname.match(GIT_PATH_RE);
|
|
16597
|
+
const api = git3 ? null : url.pathname.match(API_PATH_RE);
|
|
16598
|
+
if (!git3 && !api) return deny(res, 404, "Only this conversation's repository is reachable");
|
|
15442
16599
|
const grantToken = extractToken(req);
|
|
15443
16600
|
if (!grantToken) {
|
|
15444
16601
|
res.writeHead(401, { "www-authenticate": 'Basic realm="aster-github"', "content-type": "text/plain" });
|
|
@@ -15447,18 +16604,18 @@ var init_aster_github_proxy = __esm({
|
|
|
15447
16604
|
}
|
|
15448
16605
|
const grant = this.options.resolveGrant(grantToken);
|
|
15449
16606
|
if (!grant) return deny(res, 403, "This repository grant is invalid or was revoked");
|
|
15450
|
-
const [, owner, name] =
|
|
16607
|
+
const [, owner, name] = git3 ?? api;
|
|
15451
16608
|
if (`${owner}/${name}`.toLowerCase() !== grant.repository.toLowerCase()) {
|
|
15452
16609
|
return deny(res, 403, `This conversation can only access ${grant.repository}`);
|
|
15453
16610
|
}
|
|
15454
|
-
const upstream =
|
|
16611
|
+
const upstream = git3 ? new URL(`/${owner}/${name}.git/${git3[3]}${url.search}`, this.gitUpstream) : new URL(`/repos/${owner}/${name}${api[3] ?? ""}${url.search}`, this.apiUpstream);
|
|
15455
16612
|
const headers = {};
|
|
15456
16613
|
for (const name2 of FORWARDED_REQUEST_HEADERS) {
|
|
15457
16614
|
const value = req.headers[name2];
|
|
15458
16615
|
if (typeof value === "string") headers[name2] = value;
|
|
15459
16616
|
}
|
|
15460
16617
|
headers["user-agent"] ??= "autopilot-aster";
|
|
15461
|
-
headers.authorization =
|
|
16618
|
+
headers.authorization = git3 ? "Basic " + Buffer.from(`x-access-token:${this.options.token}`).toString("base64") : `Bearer ${this.options.token}`;
|
|
15462
16619
|
const send = upstream.protocol === "https:" ? httpsRequest : httpRequest;
|
|
15463
16620
|
const forward = send(upstream, { method: req.method, headers }, (upstreamRes) => {
|
|
15464
16621
|
const responseHeaders = {};
|
|
@@ -15481,7 +16638,7 @@ var init_aster_github_proxy = __esm({
|
|
|
15481
16638
|
// src/server/aster-push.ts
|
|
15482
16639
|
import Database5 from "better-sqlite3";
|
|
15483
16640
|
import { createSign } from "crypto";
|
|
15484
|
-
import { readFileSync as
|
|
16641
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
15485
16642
|
import { connect } from "http2";
|
|
15486
16643
|
function truncate2(value, limit) {
|
|
15487
16644
|
const clean = value.replace(/\s+/g, " ").trim();
|
|
@@ -15599,7 +16756,7 @@ var init_aster_push = __esm({
|
|
|
15599
16756
|
token() {
|
|
15600
16757
|
const now2 = Math.floor(Date.now() / 1e3);
|
|
15601
16758
|
if (this.jwt && now2 - this.jwt.issuedAt < 50 * 60) return this.jwt.value;
|
|
15602
|
-
this.privateKey ??=
|
|
16759
|
+
this.privateKey ??= readFileSync6(this.options.keyPath, "utf8");
|
|
15603
16760
|
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
15604
16761
|
const unsigned = `${encode({ alg: "ES256", kid: this.options.keyId })}.${encode({ iss: this.options.teamId, iat: now2 })}`;
|
|
15605
16762
|
const signature = createSign("SHA256").update(unsigned).sign({ key: this.privateKey, dsaEncoding: "ieee-p1363" }).toString("base64url");
|
|
@@ -15611,7 +16768,7 @@ var init_aster_push = __esm({
|
|
|
15611
16768
|
});
|
|
15612
16769
|
|
|
15613
16770
|
// src/server/aster-api.ts
|
|
15614
|
-
import { randomUUID as
|
|
16771
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
15615
16772
|
import { unlink as unlink2 } from "fs/promises";
|
|
15616
16773
|
import { resolve as resolve8 } from "path";
|
|
15617
16774
|
function parseCursor(header, query) {
|
|
@@ -16168,8 +17325,8 @@ var init_aster_api = __esm({
|
|
|
16168
17325
|
};
|
|
16169
17326
|
}
|
|
16170
17327
|
async createConversation(res, store, principal, workspace, model, idempotencyKey, kind, repository) {
|
|
16171
|
-
const candidateId =
|
|
16172
|
-
const candidateSessionId =
|
|
17328
|
+
const candidateId = randomUUID8();
|
|
17329
|
+
const candidateSessionId = randomUUID8();
|
|
16173
17330
|
const reservation = store.reserveCellConversation({
|
|
16174
17331
|
conversationId: candidateId,
|
|
16175
17332
|
credentialId: principal.credentialId,
|
|
@@ -16299,7 +17456,7 @@ var init_aster_api = __esm({
|
|
|
16299
17456
|
sendError(res, 409, "conversation_cell_unavailable", "Conversation cell is not available");
|
|
16300
17457
|
return;
|
|
16301
17458
|
}
|
|
16302
|
-
const runId =
|
|
17459
|
+
const runId = randomUUID8();
|
|
16303
17460
|
if (!idempotent && !store.beginTurn(conversation.id, runId)) {
|
|
16304
17461
|
sendError(res, 409, "conversation_busy", "A turn is already active for this conversation");
|
|
16305
17462
|
return;
|
|
@@ -16747,9 +17904,10 @@ data: ${JSON.stringify(event.data)}
|
|
|
16747
17904
|
});
|
|
16748
17905
|
|
|
16749
17906
|
// src/server/aster-cell-runtime.ts
|
|
16750
|
-
import { execFile as
|
|
16751
|
-
import {
|
|
16752
|
-
import {
|
|
17907
|
+
import { execFile as execFile7 } from "child_process";
|
|
17908
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
17909
|
+
import { promisify as promisify6 } from "util";
|
|
17910
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
16753
17911
|
async function writeCellFileChunked(cell, path, content) {
|
|
16754
17912
|
if (Buffer.byteLength(content, "utf8") <= CELL_FILE_WRITE_CHUNK_CHARS) {
|
|
16755
17913
|
await cell.writeFile(path, content, { mode: "0600" });
|
|
@@ -16774,8 +17932,17 @@ async function writeCellFileChunked(cell, path, content) {
|
|
|
16774
17932
|
});
|
|
16775
17933
|
}
|
|
16776
17934
|
}
|
|
17935
|
+
async function readBundledCellSdkBundle() {
|
|
17936
|
+
for (const url of [new URL("./sdk/index.js", import.meta.url), new URL("../sdk/index.js", import.meta.url)]) {
|
|
17937
|
+
try {
|
|
17938
|
+
return await readFile21(url, "utf8");
|
|
17939
|
+
} catch {
|
|
17940
|
+
}
|
|
17941
|
+
}
|
|
17942
|
+
throw new Error("cell_runtime_bundle_unavailable");
|
|
17943
|
+
}
|
|
16777
17944
|
async function defaultArchiveWorkspace(workspaceRoot) {
|
|
16778
|
-
const { stdout } = await
|
|
17945
|
+
const { stdout } = await execFileAsync5("git", ["-C", workspaceRoot, "archive", "--format=tar", "HEAD"], {
|
|
16779
17946
|
encoding: "buffer",
|
|
16780
17947
|
maxBuffer: 512 * 1024 * 1024
|
|
16781
17948
|
});
|
|
@@ -16823,17 +17990,19 @@ ${redacted.slice(-half)}`;
|
|
|
16823
17990
|
function sleep4(ms) {
|
|
16824
17991
|
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
16825
17992
|
}
|
|
16826
|
-
var
|
|
17993
|
+
var execFileAsync5, CELL_WORKSPACE, CELL_STATE_DIR, CHECKPOINT_MESSAGE_FILE, CELL_RUNTIME_DIR, INSTALLED_RUNTIME_PACKAGE, CELL_RUNTIME_SDK_ENTRY, NOTES_FILE, MEMORY_FILE, MEMORY_EXTRACTION_TIMEOUT_MS, LIVENESS_CHECK_MS, ARTIFACTS_DIR, MAX_ARTIFACTS, MAX_ARTIFACT_BYTES, BRANCH_RE, REPOSITORY_RE, GIT, COMMIT_SHA_RE, MAX_TOOL_OUTPUT_CHARS, MAX_TOOL_INPUT_CHARS, MAX_CHECKPOINTS, CELL_CONTROL_DIR, BRIDGE_MARKER, CONTROL_RUNNER, BRIDGE_RUNNER, HEALTH_TIMEOUT_MS, EVENT_POLL_MS, AsterHotcellRuntime, CELL_FILE_WRITE_CHUNK_CHARS, ARTIFACT_MIME_TYPES;
|
|
16827
17994
|
var init_aster_cell_runtime = __esm({
|
|
16828
17995
|
"src/server/aster-cell-runtime.ts"() {
|
|
16829
17996
|
"use strict";
|
|
16830
17997
|
init_version();
|
|
16831
17998
|
init_aster_tools();
|
|
16832
|
-
|
|
17999
|
+
execFileAsync5 = promisify6(execFile7);
|
|
16833
18000
|
CELL_WORKSPACE = "/workspace";
|
|
16834
18001
|
CELL_STATE_DIR = "/workspace/.aster";
|
|
16835
18002
|
CHECKPOINT_MESSAGE_FILE = `${CELL_STATE_DIR}/checkpoint-message`;
|
|
16836
|
-
|
|
18003
|
+
CELL_RUNTIME_DIR = "/opt/autopilot/node_modules/autopilot-ai";
|
|
18004
|
+
INSTALLED_RUNTIME_PACKAGE = `${CELL_RUNTIME_DIR}/package.json`;
|
|
18005
|
+
CELL_RUNTIME_SDK_ENTRY = `${CELL_RUNTIME_DIR}/dist/sdk/index.js`;
|
|
16837
18006
|
NOTES_FILE = `${CELL_STATE_DIR}/pending-notes`;
|
|
16838
18007
|
MEMORY_FILE = `${CELL_STATE_DIR}/memory.md`;
|
|
16839
18008
|
MEMORY_EXTRACTION_TIMEOUT_MS = 15e3;
|
|
@@ -16858,6 +18027,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
16858
18027
|
constructor(provider, options = {}) {
|
|
16859
18028
|
this.provider = provider;
|
|
16860
18029
|
this.packageSpec = options.autopilotPackageSpec ?? `autopilot-ai@${getAppVersion()}`;
|
|
18030
|
+
this.cellRuntimeSdkBundle = options.cellRuntimeSdkBundle;
|
|
16861
18031
|
this.archiveWorkspace = options.archiveWorkspace ?? defaultArchiveWorkspace;
|
|
16862
18032
|
this.pollIntervalMs = options.pollIntervalMs ?? EVENT_POLL_MS;
|
|
16863
18033
|
this.healthTimeoutMs = options.healthTimeoutMs ?? HEALTH_TIMEOUT_MS;
|
|
@@ -16872,6 +18042,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
16872
18042
|
}
|
|
16873
18043
|
provider;
|
|
16874
18044
|
packageSpec;
|
|
18045
|
+
cellRuntimeSdkBundle;
|
|
16875
18046
|
archiveWorkspace;
|
|
16876
18047
|
pollIntervalMs;
|
|
16877
18048
|
healthTimeoutMs;
|
|
@@ -16932,7 +18103,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
16932
18103
|
await this.installRuntime(cell);
|
|
16933
18104
|
await this.ensureBridge(cell);
|
|
16934
18105
|
await this.control(cell, "POST", "/rpc", {
|
|
16935
|
-
id:
|
|
18106
|
+
id: randomUUID9(),
|
|
16936
18107
|
type: "new_session",
|
|
16937
18108
|
sessionId: input.sessionId,
|
|
16938
18109
|
config: { model: input.model, cwd: CELL_WORKSPACE }
|
|
@@ -16951,7 +18122,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
16951
18122
|
/** Switch the cell session's model between turns. The bridge persists it for session restore. */
|
|
16952
18123
|
async setModel(cellId, model) {
|
|
16953
18124
|
await this.ensureReady(cellId);
|
|
16954
|
-
await this.controlById(cellId, "POST", "/rpc", { id:
|
|
18125
|
+
await this.controlById(cellId, "POST", "/rpc", { id: randomUUID9(), type: "set_model", modelId: model });
|
|
16955
18126
|
}
|
|
16956
18127
|
/** Reinstall the runtime if the cell's image lost it and restart the bridge if it isn't running. */
|
|
16957
18128
|
async ensureReady(cellId) {
|
|
@@ -17014,7 +18185,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
17014
18185
|
const active = this.activeRuns.get(runId);
|
|
17015
18186
|
if (!active) return;
|
|
17016
18187
|
active.stop();
|
|
17017
|
-
void this.controlById(active.cellId, "POST", "/rpc", { id:
|
|
18188
|
+
void this.controlById(active.cellId, "POST", "/rpc", { id: randomUUID9(), type: "abort" }).catch(() => {
|
|
17018
18189
|
});
|
|
17019
18190
|
}
|
|
17020
18191
|
async runTurn(turn, options = {}) {
|
|
@@ -17113,7 +18284,7 @@ ${turn.userText}` : turn.userText;
|
|
|
17113
18284
|
args: event.args ?? {}
|
|
17114
18285
|
});
|
|
17115
18286
|
await this.controlById(cellId, "POST", "/rpc", {
|
|
17116
|
-
id:
|
|
18287
|
+
id: randomUUID9(),
|
|
17117
18288
|
type: "resolve_permission",
|
|
17118
18289
|
requestId: event.requestId,
|
|
17119
18290
|
decision
|
|
@@ -17241,9 +18412,9 @@ ${turn.userText}` : turn.userText;
|
|
|
17241
18412
|
if (!isSafeArtifactPath(path)) throw new Error("invalid_artifact_path");
|
|
17242
18413
|
const cell = await this.provider.getCell(cellId);
|
|
17243
18414
|
const file = shellQuote2(`${ARTIFACTS_DIR}/${path}`);
|
|
17244
|
-
const
|
|
17245
|
-
if (
|
|
17246
|
-
if (Number(
|
|
18415
|
+
const stat11 = await cell.exec(`[ -f ${file} ] && [ ! -L ${file} ] && stat -c %s -- ${file}`);
|
|
18416
|
+
if (stat11.exitCode !== 0) throw new Error("artifact_not_found");
|
|
18417
|
+
if (Number(stat11.stdout.trim()) > MAX_ARTIFACT_BYTES) throw new Error("artifact_too_large");
|
|
17247
18418
|
const encoded = await cell.exec(`base64 -w0 -- ${file}`);
|
|
17248
18419
|
if (encoded.exitCode !== 0) throw new Error("artifact_not_found");
|
|
17249
18420
|
return { data: Buffer.from(encoded.stdout.trim(), "base64"), mimeType: artifactMimeType(path), name: path.split("/").pop() ?? path };
|
|
@@ -17268,11 +18439,38 @@ ${text}` : text, { mode: "0600" });
|
|
|
17268
18439
|
}
|
|
17269
18440
|
async installRuntime(cell) {
|
|
17270
18441
|
const expectedVersion = /@(\d[^@/]*)$/.exec(this.packageSpec)?.[1];
|
|
18442
|
+
let installed;
|
|
17271
18443
|
try {
|
|
17272
|
-
|
|
17273
|
-
if (!expectedVersion || installed
|
|
18444
|
+
installed = JSON.parse(await cell.readFile(INSTALLED_RUNTIME_PACKAGE));
|
|
18445
|
+
if (!expectedVersion || installed?.version === expectedVersion) return;
|
|
17274
18446
|
} catch {
|
|
17275
18447
|
}
|
|
18448
|
+
if (installed && expectedVersion) {
|
|
18449
|
+
const sdkBundle = this.cellRuntimeSdkBundle ?? await readBundledCellSdkBundle();
|
|
18450
|
+
await cell.mkdir(`${CELL_RUNTIME_DIR}/dist/sdk`, { parents: true });
|
|
18451
|
+
await writeCellFileChunked(cell, CELL_RUNTIME_SDK_ENTRY, sdkBundle);
|
|
18452
|
+
const bridge = (await cell.listProcesses()).find(
|
|
18453
|
+
(process3) => process3.status === "running" && process3.command.includes(BRIDGE_MARKER)
|
|
18454
|
+
);
|
|
18455
|
+
if (bridge) {
|
|
18456
|
+
await cell.killProcess(bridge.procId);
|
|
18457
|
+
const deadline = Date.now() + 5e3;
|
|
18458
|
+
for (; ; ) {
|
|
18459
|
+
const stillRunning = (await cell.listProcesses()).some(
|
|
18460
|
+
(process3) => process3.procId === bridge.procId && process3.status === "running"
|
|
18461
|
+
);
|
|
18462
|
+
if (!stillRunning) break;
|
|
18463
|
+
if (Date.now() >= deadline) throw new Error("cell_runtime_restart_failed");
|
|
18464
|
+
await sleep4(50);
|
|
18465
|
+
}
|
|
18466
|
+
}
|
|
18467
|
+
await cell.writeFile(
|
|
18468
|
+
INSTALLED_RUNTIME_PACKAGE,
|
|
18469
|
+
JSON.stringify({ ...installed, version: expectedVersion }),
|
|
18470
|
+
{ mode: "0600" }
|
|
18471
|
+
);
|
|
18472
|
+
return;
|
|
18473
|
+
}
|
|
17276
18474
|
const install = await cell.exec(`mkdir -p /opt/autopilot && npm install --prefix /opt/autopilot --no-audit --no-fund ${this.packageSpec}`);
|
|
17277
18475
|
if (install.exitCode !== 0) throw new Error("cell_runtime_install_failed");
|
|
17278
18476
|
}
|
|
@@ -17295,7 +18493,7 @@ ${text}` : text, { mode: "0600" });
|
|
|
17295
18493
|
}
|
|
17296
18494
|
}
|
|
17297
18495
|
async control(cell, method, path, body) {
|
|
17298
|
-
const requestId =
|
|
18496
|
+
const requestId = randomUUID9();
|
|
17299
18497
|
await cell.mkdir(CELL_CONTROL_DIR, { parents: true });
|
|
17300
18498
|
await writeCellFileChunked(cell, `${CELL_CONTROL_DIR}/${requestId}.json`, JSON.stringify({ method, path, body }));
|
|
17301
18499
|
const result2 = await cell.exec(`${CONTROL_RUNNER} ${requestId}`);
|
|
@@ -17511,9 +18709,9 @@ var init_hotcell_provider = __esm({
|
|
|
17511
18709
|
});
|
|
17512
18710
|
|
|
17513
18711
|
// src/server/routes.ts
|
|
17514
|
-
import { randomUUID as
|
|
18712
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
17515
18713
|
import { URL as URL2 } from "url";
|
|
17516
|
-
import { readFile as
|
|
18714
|
+
import { readFile as readFile22, unlink as unlink3 } from "fs/promises";
|
|
17517
18715
|
import { resolve as resolve9, basename as basename5 } from "path";
|
|
17518
18716
|
function createHotcellAsterRuntime() {
|
|
17519
18717
|
try {
|
|
@@ -17526,6 +18724,27 @@ function createHotcellAsterRuntime() {
|
|
|
17526
18724
|
throw error;
|
|
17527
18725
|
}
|
|
17528
18726
|
}
|
|
18727
|
+
async function startSessionTrace(config2, sessionFile, tools, messages) {
|
|
18728
|
+
const trace = createRecorder({
|
|
18729
|
+
enabled: config2.trace?.enabled === true,
|
|
18730
|
+
sessionId: sessionFile.id,
|
|
18731
|
+
root: config2.trace?.root
|
|
18732
|
+
});
|
|
18733
|
+
const systemPrompt = messages.filter((m) => m.role === "system" && typeof m.content === "string").map((m) => m.content).join("\n\n");
|
|
18734
|
+
await emitSessionStart(trace, {
|
|
18735
|
+
cwd: sessionFile.cwd,
|
|
18736
|
+
model: sessionFile.model,
|
|
18737
|
+
mode: "server",
|
|
18738
|
+
config: config2,
|
|
18739
|
+
tools: toOpenAIToolDefs(tools),
|
|
18740
|
+
systemPrompt
|
|
18741
|
+
});
|
|
18742
|
+
return trace;
|
|
18743
|
+
}
|
|
18744
|
+
function traceUserMessage(trace, text, attachments, turnId) {
|
|
18745
|
+
if (!trace?.enabled) return;
|
|
18746
|
+
trace.emit("user_message", { content_blob: trace.blob(text) ?? "", source: "server", attachments }, turnId);
|
|
18747
|
+
}
|
|
17529
18748
|
function parseOptionalBudgetInteger(body, name, minimum = 1) {
|
|
17530
18749
|
if (!Object.hasOwn(body, name)) return void 0;
|
|
17531
18750
|
const value = body[name];
|
|
@@ -17576,8 +18795,8 @@ async function resolveFiles2(filePatterns, cwd) {
|
|
|
17576
18795
|
const resolved = /* @__PURE__ */ new Set();
|
|
17577
18796
|
for (const pattern of filePatterns) {
|
|
17578
18797
|
try {
|
|
17579
|
-
const
|
|
17580
|
-
if (
|
|
18798
|
+
const stat11 = await import("fs/promises").then((m) => m.stat(resolve9(cwd, pattern)));
|
|
18799
|
+
if (stat11.isFile()) {
|
|
17581
18800
|
resolved.add(resolve9(cwd, pattern));
|
|
17582
18801
|
continue;
|
|
17583
18802
|
}
|
|
@@ -17606,7 +18825,7 @@ async function buildUserMessage2(prompt, files, cwd) {
|
|
|
17606
18825
|
}
|
|
17607
18826
|
} else {
|
|
17608
18827
|
try {
|
|
17609
|
-
const content = await
|
|
18828
|
+
const content = await readFile22(filePath, "utf8");
|
|
17610
18829
|
const relPath = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
|
|
17611
18830
|
fileContents.push(`
|
|
17612
18831
|
--- ${relPath} ---
|
|
@@ -17645,7 +18864,8 @@ function setupRoutes(config2, options = {}) {
|
|
|
17645
18864
|
executor: new ToolExecutor(tools),
|
|
17646
18865
|
sseClients: /* @__PURE__ */ new Set(),
|
|
17647
18866
|
runId: run.id,
|
|
17648
|
-
allowedTools
|
|
18867
|
+
allowedTools,
|
|
18868
|
+
trace: await startSessionTrace(config2, sessionFile, tools, sessionFile.messages)
|
|
17649
18869
|
};
|
|
17650
18870
|
activeSessions.set(run.sessionId, active);
|
|
17651
18871
|
activeRuns.set(run.id, active);
|
|
@@ -17833,7 +19053,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
17833
19053
|
const executor = new ToolExecutor(tools);
|
|
17834
19054
|
const { makeSessionId: makeSessionId2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
17835
19055
|
const sessionId = makeSessionId2(task);
|
|
17836
|
-
const runId =
|
|
19056
|
+
const runId = randomUUID10();
|
|
17837
19057
|
const worktreeManager = new RunWorktreeManager();
|
|
17838
19058
|
let worktree;
|
|
17839
19059
|
if (useWorktree) {
|
|
@@ -17891,7 +19111,10 @@ function setupRoutes(config2, options = {}) {
|
|
|
17891
19111
|
}
|
|
17892
19112
|
throw error;
|
|
17893
19113
|
}
|
|
19114
|
+
const runTrace = await startSessionTrace(config2, sessionFile, tools, messages);
|
|
19115
|
+
traceUserMessage(runTrace, task, 0, makeTurnId());
|
|
17894
19116
|
const active = {
|
|
19117
|
+
trace: runTrace,
|
|
17895
19118
|
sessionFile,
|
|
17896
19119
|
messages,
|
|
17897
19120
|
executor,
|
|
@@ -18073,11 +19296,16 @@ function setupRoutes(config2, options = {}) {
|
|
|
18073
19296
|
const resolvedFiles = await resolveFiles2(files, cwd);
|
|
18074
19297
|
const userContent = await buildUserMessage2(prompt, resolvedFiles, cwd);
|
|
18075
19298
|
messages.push({ role: "user", content: userContent });
|
|
19299
|
+
const trace = await startSessionTrace(config2, sessionFile, ALL_TOOLS, messages);
|
|
19300
|
+
const firstTurnId = makeTurnId();
|
|
19301
|
+
traceUserMessage(trace, prompt, resolvedFiles.length, firstTurnId);
|
|
18076
19302
|
const active = {
|
|
18077
19303
|
sessionFile,
|
|
18078
19304
|
messages,
|
|
18079
19305
|
executor,
|
|
18080
|
-
sseClients: /* @__PURE__ */ new Set()
|
|
19306
|
+
sseClients: /* @__PURE__ */ new Set(),
|
|
19307
|
+
trace,
|
|
19308
|
+
nextTurnId: firstTurnId
|
|
18081
19309
|
};
|
|
18082
19310
|
activeSessions.set(sessionId, active);
|
|
18083
19311
|
launchAgentTurnForSession(active, config2, allowAll);
|
|
@@ -18105,6 +19333,15 @@ function setupRoutes(config2, options = {}) {
|
|
|
18105
19333
|
const resolvedFiles = await resolveFiles2(files, active.sessionFile.cwd);
|
|
18106
19334
|
const userContent = await buildUserMessage2(prompt, resolvedFiles, active.sessionFile.cwd);
|
|
18107
19335
|
active.messages.push({ role: "user", content: userContent });
|
|
19336
|
+
active.nextTurnId = makeTurnId();
|
|
19337
|
+
if (active.trace?.enabled && active.lastTurnId) {
|
|
19338
|
+
active.trace.emit(
|
|
19339
|
+
"outcome",
|
|
19340
|
+
{ kind: "next_message", turn_id: active.lastTurnId, classification: classifyNextMessage(prompt), classifier: "regex" },
|
|
19341
|
+
active.lastTurnId
|
|
19342
|
+
);
|
|
19343
|
+
}
|
|
19344
|
+
traceUserMessage(active.trace, prompt, resolvedFiles.length, active.nextTurnId);
|
|
18108
19345
|
launchAgentTurnForSession(active, config2, allowAll);
|
|
18109
19346
|
json2(res, 202, { sessionId, status: "started" });
|
|
18110
19347
|
return;
|
|
@@ -18119,6 +19356,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
18119
19356
|
wakeScheduler.dispose();
|
|
18120
19357
|
for (const [, active] of activeSessions) {
|
|
18121
19358
|
active.shuttingDown = true;
|
|
19359
|
+
if (active.trace?.enabled) active.trace.emit("session_end", { reason: "exit" }, null);
|
|
18122
19360
|
active.controller?.abort(new Error("server_shutdown"));
|
|
18123
19361
|
for (const client of active.sseClients) {
|
|
18124
19362
|
client.close();
|
|
@@ -18287,6 +19525,9 @@ async function runAgentTurnForSession(active, config2, allowAll) {
|
|
|
18287
19525
|
}
|
|
18288
19526
|
};
|
|
18289
19527
|
const compaction = sessionFileCompactionTarget(sessionFile);
|
|
19528
|
+
const turnId = active.nextTurnId ?? makeTurnId();
|
|
19529
|
+
active.nextTurnId = void 0;
|
|
19530
|
+
active.lastTurnId = turnId;
|
|
18290
19531
|
try {
|
|
18291
19532
|
if (run && runStore) {
|
|
18292
19533
|
const tools = ALL_TOOLS.filter((tool) => active.allowedTools?.has(tool.name));
|
|
@@ -18308,6 +19549,8 @@ async function runAgentTurnForSession(active, config2, allowAll) {
|
|
|
18308
19549
|
preferPullRequests: config2.preferPullRequests,
|
|
18309
19550
|
compaction,
|
|
18310
19551
|
callbacks,
|
|
19552
|
+
trace: active.trace,
|
|
19553
|
+
turnId,
|
|
18311
19554
|
onIterationEnd: async (updatedMessages) => {
|
|
18312
19555
|
sessionFile.messages = updatedMessages;
|
|
18313
19556
|
sessionFile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -18334,7 +19577,9 @@ async function runAgentTurnForSession(active, config2, allowAll) {
|
|
|
18334
19577
|
allowDirectPush: config2.allowDirectPush,
|
|
18335
19578
|
preferPullRequests: config2.preferPullRequests,
|
|
18336
19579
|
compaction,
|
|
18337
|
-
callbacks
|
|
19580
|
+
callbacks,
|
|
19581
|
+
trace: active.trace,
|
|
19582
|
+
turnId
|
|
18338
19583
|
});
|
|
18339
19584
|
}
|
|
18340
19585
|
sessionFile.messages = messages;
|
|
@@ -18386,6 +19631,8 @@ var init_routes = __esm({
|
|
|
18386
19631
|
"use strict";
|
|
18387
19632
|
init_config();
|
|
18388
19633
|
init_llm_auth();
|
|
19634
|
+
init_trace();
|
|
19635
|
+
init_registry2();
|
|
18389
19636
|
init_loop();
|
|
18390
19637
|
init_system_prompt();
|
|
18391
19638
|
init_executor();
|
|
@@ -18518,8 +19765,8 @@ __export(db_exports, {
|
|
|
18518
19765
|
updateMemoryEmbedding: () => updateMemoryEmbedding
|
|
18519
19766
|
});
|
|
18520
19767
|
import Database6 from "better-sqlite3";
|
|
18521
|
-
import { dirname as
|
|
18522
|
-
import { mkdirSync as
|
|
19768
|
+
import { dirname as dirname18 } from "path";
|
|
19769
|
+
import { mkdirSync as mkdirSync10, statSync as statSync4 } from "fs";
|
|
18523
19770
|
function initSchema(db) {
|
|
18524
19771
|
db.exec(`
|
|
18525
19772
|
CREATE TABLE IF NOT EXISTS memories (
|
|
@@ -18603,7 +19850,7 @@ function openMemoryDb(dbPath) {
|
|
|
18603
19850
|
if (dbInstance) {
|
|
18604
19851
|
dbInstance.close();
|
|
18605
19852
|
}
|
|
18606
|
-
|
|
19853
|
+
mkdirSync10(dirname18(dbPath), { recursive: true });
|
|
18607
19854
|
dbInstance = new Database6(dbPath);
|
|
18608
19855
|
dbInstance.pragma("journal_mode = WAL");
|
|
18609
19856
|
dbInstance.pragma("foreign_keys = ON");
|
|
@@ -19737,12 +20984,12 @@ var init_connection = __esm({
|
|
|
19737
20984
|
});
|
|
19738
20985
|
|
|
19739
20986
|
// src/lsp/protocol.ts
|
|
19740
|
-
import { pathToFileURL as pathToFileURL2, fileURLToPath as
|
|
20987
|
+
import { pathToFileURL as pathToFileURL2, fileURLToPath as fileURLToPath5 } from "url";
|
|
19741
20988
|
function toUri(path) {
|
|
19742
20989
|
return pathToFileURL2(path).href;
|
|
19743
20990
|
}
|
|
19744
20991
|
function fromUri(uri) {
|
|
19745
|
-
return
|
|
20992
|
+
return fileURLToPath5(uri);
|
|
19746
20993
|
}
|
|
19747
20994
|
function symbolKindName(kind) {
|
|
19748
20995
|
const names = {
|
|
@@ -20553,7 +21800,7 @@ function makeLspTools(manager) {
|
|
|
20553
21800
|
var init_lsp = __esm({
|
|
20554
21801
|
"src/tools/lsp.ts"() {
|
|
20555
21802
|
"use strict";
|
|
20556
|
-
|
|
21803
|
+
init_paths2();
|
|
20557
21804
|
init_adapter();
|
|
20558
21805
|
}
|
|
20559
21806
|
});
|
|
@@ -20568,12 +21815,12 @@ __export(requesty_catalog_exports, {
|
|
|
20568
21815
|
loadRequestyCatalog: () => loadRequestyCatalog,
|
|
20569
21816
|
mapRequestyModel: () => mapRequestyModel
|
|
20570
21817
|
});
|
|
20571
|
-
import { readFile as
|
|
20572
|
-
import { homedir as
|
|
20573
|
-
import { join as
|
|
21818
|
+
import { readFile as readFile23, writeFile as writeFile13, mkdir as mkdir14 } from "fs/promises";
|
|
21819
|
+
import { homedir as homedir24 } from "os";
|
|
21820
|
+
import { join as join36, dirname as dirname19 } from "path";
|
|
20574
21821
|
function catalogCachePath2() {
|
|
20575
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
20576
|
-
return
|
|
21822
|
+
const xdg = process.env.XDG_CONFIG_HOME || join36(homedir24(), ".config");
|
|
21823
|
+
return join36(xdg, "kimiflare", "requesty-models.json");
|
|
20577
21824
|
}
|
|
20578
21825
|
function isSafeModelId(id) {
|
|
20579
21826
|
return typeof id === "string" && id.length > 0 && !CONTROL_CHARS.test(id);
|
|
@@ -20629,7 +21876,7 @@ async function fetchRequestyCatalog(fetchImpl2 = fetch) {
|
|
|
20629
21876
|
}
|
|
20630
21877
|
async function readCache4() {
|
|
20631
21878
|
try {
|
|
20632
|
-
const raw = await
|
|
21879
|
+
const raw = await readFile23(catalogCachePath2(), "utf8");
|
|
20633
21880
|
const parsed = JSON.parse(raw);
|
|
20634
21881
|
if (parsed.version !== CACHE_VERSION2) return null;
|
|
20635
21882
|
return {
|
|
@@ -20644,7 +21891,7 @@ async function readCache4() {
|
|
|
20644
21891
|
async function writeCache4(catalog) {
|
|
20645
21892
|
const path = catalogCachePath2();
|
|
20646
21893
|
try {
|
|
20647
|
-
await mkdir14(
|
|
21894
|
+
await mkdir14(dirname19(path), { recursive: true });
|
|
20648
21895
|
const cache = { version: CACHE_VERSION2, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), ...catalog };
|
|
20649
21896
|
await writeFile13(path, JSON.stringify(cache, null, 2), "utf8");
|
|
20650
21897
|
} catch {
|
|
@@ -20736,9 +21983,9 @@ var init_permissions = __esm({
|
|
|
20736
21983
|
|
|
20737
21984
|
// src/sdk/session.ts
|
|
20738
21985
|
import { resolve as resolve10 } from "path";
|
|
20739
|
-
import { homedir as
|
|
20740
|
-
import { join as
|
|
20741
|
-
import { existsSync as
|
|
21986
|
+
import { homedir as homedir25 } from "os";
|
|
21987
|
+
import { join as join37 } from "path";
|
|
21988
|
+
import { existsSync as existsSync7 } from "fs";
|
|
20742
21989
|
async function createAgentSession(opts2) {
|
|
20743
21990
|
const config2 = await resolveSdkConfig(opts2);
|
|
20744
21991
|
if (usesRequesty(llmAuthFromConfig(config2))) await ensureRequestyCatalog();
|
|
@@ -20754,7 +22001,7 @@ async function createAgentSession(opts2) {
|
|
|
20754
22001
|
let memoryManager = null;
|
|
20755
22002
|
const memoryEnabled = opts2.memoryEnabled ?? config2.memoryEnabled ?? false;
|
|
20756
22003
|
if (memoryEnabled) {
|
|
20757
|
-
const dbPath = config2.memoryDbPath ??
|
|
22004
|
+
const dbPath = config2.memoryDbPath ?? join37(homedir25(), ".local", "share", "kimiflare", "memory.db");
|
|
20758
22005
|
memoryManager = new MemoryManager({
|
|
20759
22006
|
dbPath,
|
|
20760
22007
|
...llmAuthFromConfig(config2),
|
|
@@ -20784,7 +22031,7 @@ async function createAgentSession(opts2) {
|
|
|
20784
22031
|
}
|
|
20785
22032
|
let sessionFile;
|
|
20786
22033
|
if (opts2.sessionId) {
|
|
20787
|
-
const filePath =
|
|
22034
|
+
const filePath = join37(sessionsDir(), `${opts2.sessionId}.json`);
|
|
20788
22035
|
try {
|
|
20789
22036
|
sessionFile = await loadSession(filePath);
|
|
20790
22037
|
} catch {
|
|
@@ -20819,6 +22066,19 @@ async function createAgentSession(opts2) {
|
|
|
20819
22066
|
...sessionFile.messages.filter((m) => m.role !== "system")
|
|
20820
22067
|
];
|
|
20821
22068
|
const isNewSession = sessionFile.messages.length === 0;
|
|
22069
|
+
const trace = createRecorder({
|
|
22070
|
+
enabled: config2.trace?.enabled === true,
|
|
22071
|
+
sessionId: sessionFile.id,
|
|
22072
|
+
root: config2.trace?.root
|
|
22073
|
+
});
|
|
22074
|
+
await emitSessionStart(trace, {
|
|
22075
|
+
cwd,
|
|
22076
|
+
model: config2.model,
|
|
22077
|
+
mode: "sdk",
|
|
22078
|
+
config: config2,
|
|
22079
|
+
tools: toOpenAIToolDefs(allTools),
|
|
22080
|
+
systemPrompt
|
|
22081
|
+
});
|
|
20822
22082
|
const session = new InternalSession({
|
|
20823
22083
|
sessionFile,
|
|
20824
22084
|
cwd,
|
|
@@ -20832,7 +22092,8 @@ async function createAgentSession(opts2) {
|
|
|
20832
22092
|
permissionHandler: opts2.permissionHandler,
|
|
20833
22093
|
onKimiMdStale: opts2.onKimiMdStale,
|
|
20834
22094
|
hooks,
|
|
20835
|
-
provider: opts2.provider
|
|
22095
|
+
provider: opts2.provider,
|
|
22096
|
+
trace
|
|
20836
22097
|
});
|
|
20837
22098
|
if (isNewSession) {
|
|
20838
22099
|
await session.save().catch((err) => {
|
|
@@ -20865,6 +22126,8 @@ var init_session = __esm({
|
|
|
20865
22126
|
init_context_budget();
|
|
20866
22127
|
init_config2();
|
|
20867
22128
|
init_permissions();
|
|
22129
|
+
init_trace();
|
|
22130
|
+
init_registry2();
|
|
20868
22131
|
InternalSession = class {
|
|
20869
22132
|
sessionId;
|
|
20870
22133
|
cwd;
|
|
@@ -20880,6 +22143,10 @@ var init_session = __esm({
|
|
|
20880
22143
|
onKimiMdStale;
|
|
20881
22144
|
provider;
|
|
20882
22145
|
hooks;
|
|
22146
|
+
trace;
|
|
22147
|
+
traceCostUsd = 0;
|
|
22148
|
+
/** Turn id of the last turn that ran, for next-message outcome signals. */
|
|
22149
|
+
lastTurnId = null;
|
|
20883
22150
|
listeners = /* @__PURE__ */ new Set();
|
|
20884
22151
|
steerQueue = [];
|
|
20885
22152
|
followUpQueue = [];
|
|
@@ -20920,6 +22187,7 @@ var init_session = __esm({
|
|
|
20920
22187
|
this.onKimiMdStale = opts2.onKimiMdStale;
|
|
20921
22188
|
this.provider = opts2.provider;
|
|
20922
22189
|
this.hooks = opts2.hooks;
|
|
22190
|
+
this.trace = opts2.trace;
|
|
20923
22191
|
this.permissionHandler = opts2.permissionHandler ?? createDefaultPermissionHandler({
|
|
20924
22192
|
mode: this.currentMode,
|
|
20925
22193
|
onRequest: (req) => {
|
|
@@ -20961,8 +22229,8 @@ var init_session = __esm({
|
|
|
20961
22229
|
const parts = [{ type: "text", text }];
|
|
20962
22230
|
for (const img of options.images) {
|
|
20963
22231
|
if ("path" in img) {
|
|
20964
|
-
const { readFile:
|
|
20965
|
-
const data = await
|
|
22232
|
+
const { readFile: readFile32 } = await import("fs/promises");
|
|
22233
|
+
const data = await readFile32(img.path, "base64");
|
|
20966
22234
|
const mimeType = img.path.endsWith(".png") ? "image/png" : img.path.endsWith(".jpg") || img.path.endsWith(".jpeg") ? "image/jpeg" : "image/webp";
|
|
20967
22235
|
parts.push({ type: "image_url", image_url: { url: `data:${mimeType};base64,${data}` } });
|
|
20968
22236
|
} else {
|
|
@@ -20973,6 +22241,22 @@ var init_session = __esm({
|
|
|
20973
22241
|
}
|
|
20974
22242
|
const userMessage = { role: "user", content: userContent };
|
|
20975
22243
|
this.messages.push(userMessage);
|
|
22244
|
+
const turnId = makeTurnId();
|
|
22245
|
+
if (this.trace.enabled) {
|
|
22246
|
+
if (this.lastTurnId) {
|
|
22247
|
+
this.trace.emit(
|
|
22248
|
+
"outcome",
|
|
22249
|
+
{ kind: "next_message", turn_id: this.lastTurnId, classification: classifyNextMessage(text), classifier: "regex" },
|
|
22250
|
+
this.lastTurnId
|
|
22251
|
+
);
|
|
22252
|
+
}
|
|
22253
|
+
this.trace.emit(
|
|
22254
|
+
"user_message",
|
|
22255
|
+
{ content_blob: this.trace.blob(text) ?? "", source: "sdk", attachments: options?.images?.length ?? 0 },
|
|
22256
|
+
turnId
|
|
22257
|
+
);
|
|
22258
|
+
}
|
|
22259
|
+
this.lastTurnId = turnId;
|
|
20976
22260
|
this.emit({ type: "message.start", messageId, role: "user" });
|
|
20977
22261
|
this.emit({ type: "message.end", messageId });
|
|
20978
22262
|
this.isStreaming = true;
|
|
@@ -20980,7 +22264,7 @@ var init_session = __esm({
|
|
|
20980
22264
|
this.abortController = new AbortController();
|
|
20981
22265
|
let succeeded = false;
|
|
20982
22266
|
try {
|
|
20983
|
-
await this.runTurn(mode, options?.maxToolIterations);
|
|
22267
|
+
await this.runTurn(mode, options?.maxToolIterations, turnId);
|
|
20984
22268
|
succeeded = true;
|
|
20985
22269
|
this.isStreaming = false;
|
|
20986
22270
|
this.emit({ type: "status", status: "idle" });
|
|
@@ -21075,6 +22359,9 @@ var init_session = __esm({
|
|
|
21075
22359
|
}
|
|
21076
22360
|
dispose() {
|
|
21077
22361
|
this.disposed = true;
|
|
22362
|
+
if (this.trace.enabled) {
|
|
22363
|
+
this.trace.emit("session_end", { reason: "exit", ...this.traceCostUsd > 0 ? { total_cost_usd: this.traceCostUsd } : {} }, null);
|
|
22364
|
+
}
|
|
21078
22365
|
this.denyPendingPermissions();
|
|
21079
22366
|
this.abortController?.abort();
|
|
21080
22367
|
this.lspManager?.stopAll().catch(() => {
|
|
@@ -21082,7 +22369,7 @@ var init_session = __esm({
|
|
|
21082
22369
|
this.memoryManager?.close();
|
|
21083
22370
|
this.listeners.clear();
|
|
21084
22371
|
}
|
|
21085
|
-
async runTurn(mode, maxToolIterations) {
|
|
22372
|
+
async runTurn(mode, maxToolIterations, turnId = makeTurnId()) {
|
|
21086
22373
|
const signal = this.abortController.signal;
|
|
21087
22374
|
const coauthor = this.config.coauthor !== false ? {
|
|
21088
22375
|
name: this.config.coauthorName || "autopilot",
|
|
@@ -21155,6 +22442,7 @@ var init_session = __esm({
|
|
|
21155
22442
|
});
|
|
21156
22443
|
},
|
|
21157
22444
|
onUsageFinal: (usage, gatewayMeta) => {
|
|
22445
|
+
if (typeof usage.cost === "number") this.traceCostUsd += usage.cost;
|
|
21158
22446
|
this.usage.totalInputTokens += usage.prompt_tokens;
|
|
21159
22447
|
this.usage.totalOutputTokens += usage.completion_tokens;
|
|
21160
22448
|
this.usage.turnCount += 1;
|
|
@@ -21232,6 +22520,8 @@ var init_session = __esm({
|
|
|
21232
22520
|
compaction: this.compaction,
|
|
21233
22521
|
allowDirectPush: this.config.allowDirectPush,
|
|
21234
22522
|
preferPullRequests: this.config.preferPullRequests,
|
|
22523
|
+
trace: this.trace,
|
|
22524
|
+
turnId,
|
|
21235
22525
|
// Steering lands at the next tool boundary, or before the final answer
|
|
21236
22526
|
// ends the turn (the loop continues so the model can respond to it).
|
|
21237
22527
|
drainPendingInput: () => {
|
|
@@ -21240,6 +22530,9 @@ var init_session = __esm({
|
|
|
21240
22530
|
const messageId = `msg_${this.nextMessageId++}`;
|
|
21241
22531
|
this.emit({ type: "message.start", messageId, role: "user" });
|
|
21242
22532
|
this.emit({ type: "message.end", messageId });
|
|
22533
|
+
if (this.trace.enabled) {
|
|
22534
|
+
this.trace.emit("user_message", { content_blob: this.trace.blob(text) ?? "", source: "continuation", attachments: 0 }, turnId);
|
|
22535
|
+
}
|
|
21243
22536
|
return { role: "user", content: text };
|
|
21244
22537
|
});
|
|
21245
22538
|
},
|
|
@@ -21248,14 +22541,14 @@ var init_session = __esm({
|
|
|
21248
22541
|
this.lspManager?.notifyChange(path, content);
|
|
21249
22542
|
} else {
|
|
21250
22543
|
void import("fs/promises").then(
|
|
21251
|
-
({ readFile:
|
|
22544
|
+
({ readFile: readFile32 }) => readFile32(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
|
|
21252
22545
|
})
|
|
21253
22546
|
).catch(() => {
|
|
21254
22547
|
});
|
|
21255
22548
|
}
|
|
21256
22549
|
}
|
|
21257
22550
|
});
|
|
21258
|
-
if (["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) =>
|
|
22551
|
+
if (["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) => existsSync7(join37(this.cwd, name)))) {
|
|
21259
22552
|
this.messages[0] = {
|
|
21260
22553
|
role: "system",
|
|
21261
22554
|
content: buildSystemPrompt({
|
|
@@ -22067,6 +23360,8 @@ var init_builtins = __esm({
|
|
|
22067
23360
|
{ name: "checkpoint", argHint: "[label]", description: "Save current point in session", source: "builtin" },
|
|
22068
23361
|
{ name: "checkpoints", description: "List checkpoints in current session", source: "builtin" },
|
|
22069
23362
|
{ name: "compact", description: "Summarize old turns to free context", source: "builtin" },
|
|
23363
|
+
{ name: "good", description: "Rate the last turn as good (recorded in the local session trace)", source: "builtin" },
|
|
23364
|
+
{ name: "bad", argHint: "[reason]", description: "Rate the last turn as bad (recorded in the local session trace)", source: "builtin" },
|
|
22070
23365
|
{ name: "clear", description: "Clear current conversation", source: "builtin" },
|
|
22071
23366
|
{ name: "fresh", description: "Start a fresh session with a summarized plan or continuation context", source: "builtin" },
|
|
22072
23367
|
{ name: "init", description: "Scan repo and write AGENTS.md", source: "builtin" },
|
|
@@ -22533,7 +23828,9 @@ var init_help_menu = __esm({
|
|
|
22533
23828
|
{ command: "/resume", description: "pick a past conversation" },
|
|
22534
23829
|
{ command: "/compact", description: "summarize old turns to free context" },
|
|
22535
23830
|
{ command: "/clear", description: "clear current conversation" },
|
|
22536
|
-
{ command: "/fresh", description: "reset session and start fresh with the last plan" }
|
|
23831
|
+
{ command: "/fresh", description: "reset session and start fresh with the last plan" },
|
|
23832
|
+
{ command: "/good", description: "rate the last turn good (local session trace)" },
|
|
23833
|
+
{ command: "/bad [reason]", description: "rate the last turn bad (local session trace)", selectable: false }
|
|
22537
23834
|
]
|
|
22538
23835
|
},
|
|
22539
23836
|
{
|
|
@@ -22692,8 +23989,8 @@ __export(app_helpers_exports, {
|
|
|
22692
23989
|
trackRecentFile: () => trackRecentFile
|
|
22693
23990
|
});
|
|
22694
23991
|
import { execSync as execSync3, spawn as spawn6 } from "child_process";
|
|
22695
|
-
import { existsSync as
|
|
22696
|
-
import { join as
|
|
23992
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
23993
|
+
import { join as join38 } from "path";
|
|
22697
23994
|
import { platform as platform3 } from "os";
|
|
22698
23995
|
function replaceQueuedPrompt(queue2, key, full, display) {
|
|
22699
23996
|
const index = queue2.findIndex((item) => item.key === key);
|
|
@@ -22801,12 +24098,12 @@ function buildFilePickerIgnoreList(cwd) {
|
|
|
22801
24098
|
];
|
|
22802
24099
|
const gitignorePatterns = [];
|
|
22803
24100
|
try {
|
|
22804
|
-
const gitignorePath =
|
|
24101
|
+
const gitignorePath = join38(cwd, ".gitignore");
|
|
22805
24102
|
const stats = statSync5(gitignorePath);
|
|
22806
24103
|
if (stats.size > MAX_GITIGNORE_SIZE) {
|
|
22807
24104
|
return hardcoded;
|
|
22808
24105
|
}
|
|
22809
|
-
const content =
|
|
24106
|
+
const content = readFileSync7(gitignorePath, "utf-8");
|
|
22810
24107
|
for (const line of content.split(/\r?\n/)) {
|
|
22811
24108
|
const trimmed = line.trim();
|
|
22812
24109
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -22940,7 +24237,7 @@ function findImagePaths(text) {
|
|
|
22940
24237
|
let match;
|
|
22941
24238
|
while ((match = quotedRegex.exec(text)) !== null) {
|
|
22942
24239
|
const path = match[1] ?? match[2];
|
|
22943
|
-
if (path && isImagePath(path) &&
|
|
24240
|
+
if (path && isImagePath(path) && existsSync8(path)) {
|
|
22944
24241
|
paths.push(path);
|
|
22945
24242
|
}
|
|
22946
24243
|
}
|
|
@@ -22949,7 +24246,7 @@ function findImagePaths(text) {
|
|
|
22949
24246
|
const processed = remaining.replace(/\\ /g, ESCAPED_SPACE);
|
|
22950
24247
|
for (const token of processed.split(/\s+/)) {
|
|
22951
24248
|
const clean = token.replace(new RegExp(ESCAPED_SPACE, "g"), " ").replace(/^["']|["',;:!?]$/g, "").replace(/[.,;:!?]$/, "");
|
|
22952
|
-
if (clean && isImagePath(clean) &&
|
|
24249
|
+
if (clean && isImagePath(clean) && existsSync8(clean) && !paths.includes(clean)) {
|
|
22953
24250
|
paths.push(clean);
|
|
22954
24251
|
}
|
|
22955
24252
|
}
|
|
@@ -22979,9 +24276,9 @@ var init_app_helpers = __esm({
|
|
|
22979
24276
|
|
|
22980
24277
|
// src/ui/bang-command.ts
|
|
22981
24278
|
import { spawn as spawn7 } from "child_process";
|
|
22982
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
24279
|
+
import { mkdtemp as mkdtemp2, readFile as readFile24, rm as rm4 } from "fs/promises";
|
|
22983
24280
|
import { tmpdir as tmpdir4, platform as platform4 } from "os";
|
|
22984
|
-
import { join as
|
|
24281
|
+
import { join as join39 } from "path";
|
|
22985
24282
|
function registerInkInstance(instance) {
|
|
22986
24283
|
inkInstance = instance;
|
|
22987
24284
|
}
|
|
@@ -23084,8 +24381,8 @@ async function runBangCommand(command, cwd) {
|
|
|
23084
24381
|
const origErr = stderr.write;
|
|
23085
24382
|
const readable = stdin.listeners("readable");
|
|
23086
24383
|
const wasRaw = stdin.isTTY ? stdin.isRaw : false;
|
|
23087
|
-
const logDir2 = isWindows ? null : await mkdtemp2(
|
|
23088
|
-
const logPath = logDir2 ?
|
|
24384
|
+
const logDir2 = isWindows ? null : await mkdtemp2(join39(tmpdir4(), "autopilot-bang-"));
|
|
24385
|
+
const logPath = logDir2 ? join39(logDir2, "out.log") : null;
|
|
23089
24386
|
const viaScript = logPath ? scriptInvocation(command, shell, logPath) : null;
|
|
23090
24387
|
inkInstance?.clear();
|
|
23091
24388
|
origOut.call(stdout, `
|
|
@@ -23127,9 +24424,9 @@ $ ${command}
|
|
|
23127
24424
|
}
|
|
23128
24425
|
let output = spawnError ? `failed to start: ${spawnError}` : null;
|
|
23129
24426
|
if (captured && logPath) {
|
|
23130
|
-
output = cleanTerminalOutput(await
|
|
24427
|
+
output = cleanTerminalOutput(await readFile24(logPath, "utf8").catch(() => ""));
|
|
23131
24428
|
}
|
|
23132
|
-
if (logDir2) await
|
|
24429
|
+
if (logDir2) await rm4(logDir2, { recursive: true, force: true }).catch(() => {
|
|
23133
24430
|
});
|
|
23134
24431
|
return { command, exitCode: result2.code, signal: result2.signal, output };
|
|
23135
24432
|
}
|
|
@@ -25316,9 +26613,9 @@ __export(camouflage_view_exports, {
|
|
|
25316
26613
|
validateCommandName: () => validateCommandName
|
|
25317
26614
|
});
|
|
25318
26615
|
import { execFileSync as execFileSync2, spawn as spawn9 } from "child_process";
|
|
25319
|
-
import { readdir as
|
|
25320
|
-
import { homedir as
|
|
25321
|
-
import { join as
|
|
26616
|
+
import { readdir as readdir6 } from "fs/promises";
|
|
26617
|
+
import { homedir as homedir26 } from "os";
|
|
26618
|
+
import { join as join40, resolve as resolve11 } from "path";
|
|
25322
26619
|
async function loadSdk() {
|
|
25323
26620
|
const sdk = await import("camouflage-tui").catch((err) => {
|
|
25324
26621
|
throw new CamouflageUnavailable(`camouflage-tui is not installed (${err.message})`);
|
|
@@ -25497,10 +26794,10 @@ function truncateText(s, max) {
|
|
|
25497
26794
|
async function listMentionDir(cwd, query) {
|
|
25498
26795
|
const exact = query === "~" || query === "." || query === ".." || query.endsWith("/..");
|
|
25499
26796
|
const dir = exact ? `${query}/` : query.slice(0, query.lastIndexOf("/") + 1);
|
|
25500
|
-
const expanded = dir.startsWith("~") ?
|
|
26797
|
+
const expanded = dir.startsWith("~") ? join40(homedir26(), dir.slice(1)) : dir;
|
|
25501
26798
|
const target = resolve11(cwd, expanded || ".");
|
|
25502
26799
|
try {
|
|
25503
|
-
const items = await
|
|
26800
|
+
const items = await readdir6(target, { withFileTypes: true });
|
|
25504
26801
|
const entries = items.filter((d) => !d.name.startsWith(".")).map((d) => ({ name: d.name, isDir: d.isDirectory() })).sort((a, b) => a.isDir !== b.isDir ? a.isDir ? -1 : 1 : a.name.localeCompare(b.name)).slice(0, 300).map((d) => `${dir}${d.name}${d.isDir ? "/" : ""}`);
|
|
25505
26802
|
return { dir, entries };
|
|
25506
26803
|
} catch {
|
|
@@ -25578,7 +26875,7 @@ function shortModel(id) {
|
|
|
25578
26875
|
return id.includes("/") ? id.slice(id.indexOf("/") + 1) : id;
|
|
25579
26876
|
}
|
|
25580
26877
|
function shortPath(p) {
|
|
25581
|
-
const home =
|
|
26878
|
+
const home = homedir26();
|
|
25582
26879
|
return p === home ? "~" : p.startsWith(home + "/") ? `~${p.slice(home.length)}` : p;
|
|
25583
26880
|
}
|
|
25584
26881
|
function gitBranch(cwd) {
|
|
@@ -27585,7 +28882,7 @@ var init_tool_view = __esm({
|
|
|
27585
28882
|
"use strict";
|
|
27586
28883
|
init_spinner();
|
|
27587
28884
|
init_diff_view();
|
|
27588
|
-
|
|
28885
|
+
init_paths2();
|
|
27589
28886
|
init_theme_context();
|
|
27590
28887
|
init_narrator();
|
|
27591
28888
|
ToolView = React7.memo(function ToolView2({ evt, verbose, isRepeated, intentTier }) {
|
|
@@ -28332,7 +29629,7 @@ function PermissionModal({ tool, args, onDecide, onFeedback }) {
|
|
|
28332
29629
|
);
|
|
28333
29630
|
const handleFeedbackSubmit = useCallback(
|
|
28334
29631
|
(text) => {
|
|
28335
|
-
onDecide(DENY);
|
|
29632
|
+
onDecide(text.trim() ? { ...DENY, feedback: text.trim() } : DENY);
|
|
28336
29633
|
if (text.trim()) {
|
|
28337
29634
|
onFeedback?.(text);
|
|
28338
29635
|
}
|
|
@@ -28847,8 +30144,8 @@ var init_queue_plan_picker = __esm({
|
|
|
28847
30144
|
|
|
28848
30145
|
// src/agent/jev-context.ts
|
|
28849
30146
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
28850
|
-
import { readFile as
|
|
28851
|
-
import { basename as basename6, join as
|
|
30147
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
30148
|
+
import { basename as basename6, join as join41, resolve as resolve12 } from "path";
|
|
28852
30149
|
function messageText(message2) {
|
|
28853
30150
|
if (typeof message2.content === "string") return message2.content.trim();
|
|
28854
30151
|
if (!Array.isArray(message2.content)) return "";
|
|
@@ -28870,7 +30167,7 @@ function projectRoot(cwd) {
|
|
|
28870
30167
|
}
|
|
28871
30168
|
async function readPackageMetadata(root) {
|
|
28872
30169
|
try {
|
|
28873
|
-
const parsed = JSON.parse(await
|
|
30170
|
+
const parsed = JSON.parse(await readFile25(join41(root, "package.json"), "utf8"));
|
|
28874
30171
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
28875
30172
|
const pkg = parsed;
|
|
28876
30173
|
const metadata = {};
|
|
@@ -28887,7 +30184,7 @@ async function readPackageMetadata(root) {
|
|
|
28887
30184
|
async function readLicenseEvidence(root) {
|
|
28888
30185
|
for (const name of ["LICENSE", "LICENSE.md", "LICENSE.txt", "LICENCE", "COPYING"]) {
|
|
28889
30186
|
try {
|
|
28890
|
-
const content = (await
|
|
30187
|
+
const content = (await readFile25(join41(root, name), "utf8")).trim();
|
|
28891
30188
|
if (content) return { source: `project license (${name})`, content: content.slice(0, 700) };
|
|
28892
30189
|
} catch {
|
|
28893
30190
|
}
|
|
@@ -29969,8 +31266,8 @@ var init_frontmatter = __esm({
|
|
|
29969
31266
|
});
|
|
29970
31267
|
|
|
29971
31268
|
// src/skills/loader.ts
|
|
29972
|
-
import { readFile as
|
|
29973
|
-
import { join as
|
|
31269
|
+
import { readFile as readFile26, readdir as readdir7, stat as stat8 } from "fs/promises";
|
|
31270
|
+
import { join as join42, extname } from "path";
|
|
29974
31271
|
function normalizeManifest(raw, filePath) {
|
|
29975
31272
|
const name = typeof raw.name === "string" ? raw.name : "";
|
|
29976
31273
|
const description = typeof raw.description === "string" ? raw.description : "";
|
|
@@ -29984,7 +31281,7 @@ function normalizeManifest(raw, filePath) {
|
|
|
29984
31281
|
return { name, description, match, scope, priority, enabled: enabled2 };
|
|
29985
31282
|
}
|
|
29986
31283
|
async function loadSkillFile(filePath) {
|
|
29987
|
-
const raw = await
|
|
31284
|
+
const raw = await readFile26(filePath, "utf-8");
|
|
29988
31285
|
const parsed = parseFrontmatter(raw);
|
|
29989
31286
|
const manifest = normalizeManifest(parsed.data, filePath);
|
|
29990
31287
|
const body = parsed.content.trim();
|
|
@@ -30003,11 +31300,11 @@ async function loadSkillFile(filePath) {
|
|
|
30003
31300
|
}
|
|
30004
31301
|
async function loadSkillsFromDir(dirPath) {
|
|
30005
31302
|
try {
|
|
30006
|
-
const entries = await
|
|
31303
|
+
const entries = await readdir7(dirPath);
|
|
30007
31304
|
const files = [];
|
|
30008
31305
|
for (const entry of entries) {
|
|
30009
|
-
const full =
|
|
30010
|
-
const s = await
|
|
31306
|
+
const full = join42(dirPath, entry);
|
|
31307
|
+
const s = await stat8(full);
|
|
30011
31308
|
if (s.isFile() && extname(entry) === ".md") {
|
|
30012
31309
|
files.push(full);
|
|
30013
31310
|
}
|
|
@@ -30034,12 +31331,12 @@ var init_loader = __esm({
|
|
|
30034
31331
|
});
|
|
30035
31332
|
|
|
30036
31333
|
// src/skills/manager.ts
|
|
30037
|
-
import { mkdir as mkdir15, writeFile as writeFile14, unlink as unlink4, readFile as
|
|
30038
|
-
import { join as
|
|
31334
|
+
import { mkdir as mkdir15, writeFile as writeFile14, unlink as unlink4, readFile as readFile27 } from "fs/promises";
|
|
31335
|
+
import { join as join43 } from "path";
|
|
30039
31336
|
function getSkillDirs(cwd) {
|
|
30040
31337
|
return {
|
|
30041
|
-
projectDir:
|
|
30042
|
-
globalDir:
|
|
31338
|
+
projectDir: join43(cwd, ".kimiflare", "skills"),
|
|
31339
|
+
globalDir: join43(process.env.HOME ?? "", ".config", "kimiflare", "skills")
|
|
30043
31340
|
};
|
|
30044
31341
|
}
|
|
30045
31342
|
async function listAllSkills(cwd) {
|
|
@@ -30053,7 +31350,7 @@ async function listAllSkills(cwd) {
|
|
|
30053
31350
|
async function createSkill(opts2) {
|
|
30054
31351
|
const dirs = getSkillDirs(opts2.cwd);
|
|
30055
31352
|
const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
|
|
30056
|
-
const filepath =
|
|
31353
|
+
const filepath = join43(dir, `${opts2.name}.md`);
|
|
30057
31354
|
const frontmatter = {
|
|
30058
31355
|
name: opts2.name,
|
|
30059
31356
|
enabled: true,
|
|
@@ -30089,7 +31386,7 @@ async function setSkillEnabled(name, enabled2, cwd) {
|
|
|
30089
31386
|
const all = await listAllSkills(cwd);
|
|
30090
31387
|
const skill = all.project.find((s) => s.name === name) ?? all.global.find((s) => s.name === name);
|
|
30091
31388
|
if (!skill) throw new Error(`skill "${name}" not found`);
|
|
30092
|
-
const raw = await
|
|
31389
|
+
const raw = await readFile27(skill.filePath, "utf-8");
|
|
30093
31390
|
const parsed = parseFrontmatter(raw);
|
|
30094
31391
|
parsed.data.enabled = enabled2;
|
|
30095
31392
|
const yaml = Object.entries(parsed.data).map(([k, v]) => {
|
|
@@ -30361,15 +31658,15 @@ var tui_report_exports = {};
|
|
|
30361
31658
|
__export(tui_report_exports, {
|
|
30362
31659
|
getCategoryReportText: () => getCategoryReportText
|
|
30363
31660
|
});
|
|
30364
|
-
import { readFile as
|
|
30365
|
-
import { join as
|
|
30366
|
-
import { homedir as
|
|
31661
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
31662
|
+
import { join as join44 } from "path";
|
|
31663
|
+
import { homedir as homedir27 } from "os";
|
|
30367
31664
|
function usageDir3() {
|
|
30368
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
30369
|
-
return
|
|
31665
|
+
const xdg = process.env.XDG_DATA_HOME || join44(homedir27(), ".local", "share");
|
|
31666
|
+
return join44(xdg, "kimiflare");
|
|
30370
31667
|
}
|
|
30371
31668
|
function usagePath4() {
|
|
30372
|
-
return
|
|
31669
|
+
return join44(usageDir3(), "usage.json");
|
|
30373
31670
|
}
|
|
30374
31671
|
function today3() {
|
|
30375
31672
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -30381,7 +31678,7 @@ function daysAgo2(n) {
|
|
|
30381
31678
|
}
|
|
30382
31679
|
async function loadLog3() {
|
|
30383
31680
|
try {
|
|
30384
|
-
const raw = await
|
|
31681
|
+
const raw = await readFile28(usagePath4(), "utf8");
|
|
30385
31682
|
return JSON.parse(raw);
|
|
30386
31683
|
} catch {
|
|
30387
31684
|
return { version: 1, days: [], sessions: [] };
|
|
@@ -30438,7 +31735,7 @@ var init_tui_report = __esm({
|
|
|
30438
31735
|
});
|
|
30439
31736
|
|
|
30440
31737
|
// src/ui/slash-commands.ts
|
|
30441
|
-
import { join as
|
|
31738
|
+
import { join as join45 } from "path";
|
|
30442
31739
|
import QRCode from "qrcode";
|
|
30443
31740
|
function executeFreshStart(ctx, planText, overrideMode, opts2 = {}) {
|
|
30444
31741
|
const oldSessionId = ctx.sessionIdRef.current;
|
|
@@ -30529,6 +31826,28 @@ function modesGate(ctx) {
|
|
|
30529
31826
|
]);
|
|
30530
31827
|
return false;
|
|
30531
31828
|
}
|
|
31829
|
+
function rateLastTurn(ctx, value, reason) {
|
|
31830
|
+
const { mkKey: mkKey2, setEvents } = ctx;
|
|
31831
|
+
const last = ctx.turnHistoryRef.current.at(-1);
|
|
31832
|
+
if (!last) {
|
|
31833
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "nothing to rate yet \u2014 finish a turn first" }]);
|
|
31834
|
+
return true;
|
|
31835
|
+
}
|
|
31836
|
+
const trace = ctx.traceRef.current;
|
|
31837
|
+
if (!trace.enabled) {
|
|
31838
|
+
setEvents((e) => [
|
|
31839
|
+
...e,
|
|
31840
|
+
{ kind: "info", key: mkKey2(), text: "session tracing is off, so the rating was not recorded (enable with KIMIFLARE_TRACE=1)" }
|
|
31841
|
+
]);
|
|
31842
|
+
return true;
|
|
31843
|
+
}
|
|
31844
|
+
trace.emit("outcome", { kind: "rating", turn_id: last.turnId, value, ...reason ? { reason } : {} }, last.turnId);
|
|
31845
|
+
setEvents((e) => [
|
|
31846
|
+
...e,
|
|
31847
|
+
{ kind: "info", key: mkKey2(), text: `recorded ${value === "good" ? "\u{1F44D} good" : "\u{1F44E} bad"} for the last turn${reason ? `: ${reason}` : ""}` }
|
|
31848
|
+
]);
|
|
31849
|
+
return true;
|
|
31850
|
+
}
|
|
30532
31851
|
function dispatchSlashCommand(ctx, cmd) {
|
|
30533
31852
|
const raw = cmd.trim();
|
|
30534
31853
|
const [head, ...rest] = raw.split(/\s+/);
|
|
@@ -30538,7 +31857,7 @@ function dispatchSlashCommand(ctx, cmd) {
|
|
|
30538
31857
|
if (!handler) return false;
|
|
30539
31858
|
return handler(ctx, rest, arg);
|
|
30540
31859
|
}
|
|
30541
|
-
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;
|
|
31860
|
+
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, handleGood, handleBad, handlers;
|
|
30542
31861
|
var init_slash_commands = __esm({
|
|
30543
31862
|
"src/ui/slash-commands.ts"() {
|
|
30544
31863
|
"use strict";
|
|
@@ -30561,7 +31880,7 @@ var init_slash_commands = __esm({
|
|
|
30561
31880
|
init_executor();
|
|
30562
31881
|
init_recommended();
|
|
30563
31882
|
init_settings();
|
|
30564
|
-
|
|
31883
|
+
init_types2();
|
|
30565
31884
|
init_update_check();
|
|
30566
31885
|
init_version();
|
|
30567
31886
|
init_app_helpers();
|
|
@@ -31348,7 +32667,7 @@ ${lines.join("\n")}` }]);
|
|
|
31348
32667
|
void (async () => {
|
|
31349
32668
|
try {
|
|
31350
32669
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
31351
|
-
const file = await loadSession(
|
|
32670
|
+
const file = await loadSession(join45(sessionsDir3(), `${currentId}.json`));
|
|
31352
32671
|
const cps = file.checkpoints ?? [];
|
|
31353
32672
|
if (cps.length === 0) {
|
|
31354
32673
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "no checkpoints in this session" }]);
|
|
@@ -31393,7 +32712,7 @@ ${lines.join("\n")}` }]);
|
|
|
31393
32712
|
try {
|
|
31394
32713
|
ctx.ensureSessionId();
|
|
31395
32714
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
31396
|
-
const filePath =
|
|
32715
|
+
const filePath = join45(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
|
|
31397
32716
|
await addCheckpoint(filePath, cp);
|
|
31398
32717
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `checkpoint saved: "${label}"` }]);
|
|
31399
32718
|
} catch (e) {
|
|
@@ -31984,6 +33303,8 @@ project: ${projectSettingsPath(cwd)}`
|
|
|
31984
33303
|
});
|
|
31985
33304
|
return true;
|
|
31986
33305
|
};
|
|
33306
|
+
handleGood = (ctx, rest) => rateLastTurn(ctx, "good", rest.join(" ").trim());
|
|
33307
|
+
handleBad = (ctx, rest) => rateLastTurn(ctx, "bad", rest.join(" ").trim());
|
|
31987
33308
|
handlers = {
|
|
31988
33309
|
"/exit": handleExit,
|
|
31989
33310
|
"/clear": handleClear,
|
|
@@ -32007,6 +33328,8 @@ project: ${projectSettingsPath(cwd)}`
|
|
|
32007
33328
|
"/resume": handleResume,
|
|
32008
33329
|
"/checkpoint": handleCheckpoint,
|
|
32009
33330
|
"/compact": handleCompact,
|
|
33331
|
+
"/good": handleGood,
|
|
33332
|
+
"/bad": handleBad,
|
|
32010
33333
|
"/init": handleInit,
|
|
32011
33334
|
"/update": handleUpdate,
|
|
32012
33335
|
"/mcp": handleMcp,
|
|
@@ -32338,7 +33661,7 @@ __export(openrouter_oauth_exports, {
|
|
|
32338
33661
|
pkceChallenge: () => pkceChallenge,
|
|
32339
33662
|
startLoopbackListener: () => startLoopbackListener
|
|
32340
33663
|
});
|
|
32341
|
-
import { createHash as
|
|
33664
|
+
import { createHash as createHash5, randomBytes as randomBytes3 } from "crypto";
|
|
32342
33665
|
import { createServer as createServer3 } from "http";
|
|
32343
33666
|
import { hostname } from "os";
|
|
32344
33667
|
function createPkce() {
|
|
@@ -32346,7 +33669,7 @@ function createPkce() {
|
|
|
32346
33669
|
return { verifier, challenge: pkceChallenge(verifier) };
|
|
32347
33670
|
}
|
|
32348
33671
|
function pkceChallenge(verifier) {
|
|
32349
|
-
return
|
|
33672
|
+
return createHash5("sha256").update(verifier).digest("base64url");
|
|
32350
33673
|
}
|
|
32351
33674
|
function openRouterAuthPageUrl() {
|
|
32352
33675
|
return (process.env.OPENROUTER_AUTH_URL?.trim() || "https://openrouter.ai/auth").replace(/\/+$/, "");
|
|
@@ -32858,14 +34181,14 @@ var init_frontmatter2 = __esm({
|
|
|
32858
34181
|
|
|
32859
34182
|
// src/commands/loader.ts
|
|
32860
34183
|
import { open, realpath as realpath6 } from "fs/promises";
|
|
32861
|
-
import { homedir as
|
|
32862
|
-
import { join as
|
|
34184
|
+
import { homedir as homedir28 } from "os";
|
|
34185
|
+
import { join as join46, relative as relative10, sep as sep6 } from "path";
|
|
32863
34186
|
function projectCommandsDir(cwd = process.cwd()) {
|
|
32864
|
-
return
|
|
34187
|
+
return join46(cwd, ".kimiflare", "commands");
|
|
32865
34188
|
}
|
|
32866
34189
|
function globalCommandsDir() {
|
|
32867
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
32868
|
-
return
|
|
34190
|
+
const xdg = process.env.XDG_CONFIG_HOME || join46(homedir28(), ".config");
|
|
34191
|
+
return join46(xdg, "kimiflare", "commands");
|
|
32869
34192
|
}
|
|
32870
34193
|
async function loadCustomCommands(cwd = process.cwd()) {
|
|
32871
34194
|
const warnings = [];
|
|
@@ -33009,7 +34332,7 @@ var init_loader2 = __esm({
|
|
|
33009
34332
|
init_mode();
|
|
33010
34333
|
init_glob();
|
|
33011
34334
|
init_config();
|
|
33012
|
-
|
|
34335
|
+
init_paths2();
|
|
33013
34336
|
init_frontmatter2();
|
|
33014
34337
|
MAX_COMMAND_FILE_BYTES = 256 * 1024;
|
|
33015
34338
|
}
|
|
@@ -33019,7 +34342,7 @@ var init_loader2 = __esm({
|
|
|
33019
34342
|
import { exec } from "child_process";
|
|
33020
34343
|
import { open as open2, realpath as realpath7 } from "fs/promises";
|
|
33021
34344
|
import { isAbsolute as isAbsolute6, relative as relative11, resolve as resolvePathJoin, basename as pathBasename } from "path";
|
|
33022
|
-
import { promisify as
|
|
34345
|
+
import { promisify as promisify7 } from "util";
|
|
33023
34346
|
function tokenizeArgs(s) {
|
|
33024
34347
|
return [...s.matchAll(ARG_TOKEN_RE)].map((match) => {
|
|
33025
34348
|
const token = match[0];
|
|
@@ -33171,8 +34494,8 @@ var execAsync, ARG_TOKEN_RE, POSITIONAL_RE, HAS_POSITIONAL, SHELL_RE, FILE_RE, D
|
|
|
33171
34494
|
var init_renderer2 = __esm({
|
|
33172
34495
|
"src/commands/renderer.ts"() {
|
|
33173
34496
|
"use strict";
|
|
33174
|
-
|
|
33175
|
-
execAsync =
|
|
34497
|
+
init_paths2();
|
|
34498
|
+
execAsync = promisify7(exec);
|
|
33176
34499
|
ARG_TOKEN_RE = /(?:"[^"]*"|'[^']*'|[^\s"']+)/g;
|
|
33177
34500
|
POSITIONAL_RE = /\$(\d+)/g;
|
|
33178
34501
|
HAS_POSITIONAL = /\$\d+/;
|
|
@@ -33247,11 +34570,11 @@ var init_wcag = __esm({
|
|
|
33247
34570
|
});
|
|
33248
34571
|
|
|
33249
34572
|
// src/ui/theme-loader.ts
|
|
33250
|
-
import { readFile as
|
|
33251
|
-
import { join as
|
|
33252
|
-
import { homedir as
|
|
34573
|
+
import { readFile as readFile29, readdir as readdir8 } from "fs/promises";
|
|
34574
|
+
import { join as join47 } from "path";
|
|
34575
|
+
import { homedir as homedir29 } from "os";
|
|
33253
34576
|
function projectThemesDir(cwd = process.cwd()) {
|
|
33254
|
-
return
|
|
34577
|
+
return join47(cwd, ".kimiflare", "themes");
|
|
33255
34578
|
}
|
|
33256
34579
|
function isHexColor(c) {
|
|
33257
34580
|
return /^#[0-9a-fA-F]{6}$/.test(c);
|
|
@@ -33335,15 +34658,15 @@ async function loadThemesFromDir(dir, source) {
|
|
|
33335
34658
|
const errors = [];
|
|
33336
34659
|
let files;
|
|
33337
34660
|
try {
|
|
33338
|
-
files = await
|
|
34661
|
+
files = await readdir8(dir);
|
|
33339
34662
|
} catch {
|
|
33340
34663
|
return { themes, errors };
|
|
33341
34664
|
}
|
|
33342
34665
|
for (const file of files.filter((f) => f.endsWith(".json"))) {
|
|
33343
|
-
const path =
|
|
34666
|
+
const path = join47(dir, file);
|
|
33344
34667
|
let raw;
|
|
33345
34668
|
try {
|
|
33346
|
-
raw = await
|
|
34669
|
+
raw = await readFile29(path, "utf-8");
|
|
33347
34670
|
} catch (e) {
|
|
33348
34671
|
errors.push(`${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
33349
34672
|
continue;
|
|
@@ -33489,8 +34812,8 @@ var init_theme_loader = __esm({
|
|
|
33489
34812
|
"use strict";
|
|
33490
34813
|
init_wcag();
|
|
33491
34814
|
init_theme();
|
|
33492
|
-
USER_THEMES_DIR =
|
|
33493
|
-
process.env.XDG_CONFIG_HOME ||
|
|
34815
|
+
USER_THEMES_DIR = join47(
|
|
34816
|
+
process.env.XDG_CONFIG_HOME || join47(homedir29(), ".config"),
|
|
33494
34817
|
"kimiflare",
|
|
33495
34818
|
"themes"
|
|
33496
34819
|
);
|
|
@@ -35648,6 +36971,61 @@ function useSessionManager(deps) {
|
|
|
35648
36971
|
setLogSessionId(sessionIdRef.current);
|
|
35649
36972
|
return sessionIdRef.current;
|
|
35650
36973
|
}, []);
|
|
36974
|
+
const traceRef = useRef7(createNoopRecorder());
|
|
36975
|
+
const traceStartedRef = useRef7(false);
|
|
36976
|
+
const turnHistoryRef = useRef7([]);
|
|
36977
|
+
const noteTurnEnded = useCallback6((turnId) => {
|
|
36978
|
+
turnHistoryRef.current.push({ turnId, endedAt: Date.now() });
|
|
36979
|
+
if (turnHistoryRef.current.length > 100) turnHistoryRef.current.shift();
|
|
36980
|
+
}, []);
|
|
36981
|
+
const endTrace = useCallback6((reason) => {
|
|
36982
|
+
const trace = traceRef.current;
|
|
36983
|
+
if (trace.enabled && traceStartedRef.current) trace.emit("session_end", { reason }, null);
|
|
36984
|
+
traceStartedRef.current = false;
|
|
36985
|
+
traceRef.current = createNoopRecorder();
|
|
36986
|
+
}, []);
|
|
36987
|
+
const traceReverted = useCallback6((how, turns) => {
|
|
36988
|
+
const trace = traceRef.current;
|
|
36989
|
+
if (!trace.enabled) return;
|
|
36990
|
+
for (const turn of turns) trace.emit("outcome", { kind: "reverted", turn_id: turn.turnId, how }, turn.turnId);
|
|
36991
|
+
}, []);
|
|
36992
|
+
const ensureTrace = useCallback6(() => {
|
|
36993
|
+
const id = ensureSessionId();
|
|
36994
|
+
const cfgTrace = depsRef.current.cfg?.trace;
|
|
36995
|
+
const wantEnabled = cfgTrace?.enabled === true;
|
|
36996
|
+
const current = traceRef.current;
|
|
36997
|
+
if (current.sessionId === id && current.enabled === wantEnabled) return current;
|
|
36998
|
+
if (current.enabled && traceStartedRef.current) current.emit("session_end", { reason: "exit" }, null);
|
|
36999
|
+
traceStartedRef.current = false;
|
|
37000
|
+
traceRef.current = createRecorder({ enabled: wantEnabled, sessionId: id, root: cfgTrace?.root });
|
|
37001
|
+
return traceRef.current;
|
|
37002
|
+
}, [ensureSessionId]);
|
|
37003
|
+
const ensureTraceStarted = useCallback6(
|
|
37004
|
+
async (info) => {
|
|
37005
|
+
const trace = ensureTrace();
|
|
37006
|
+
if (!trace.enabled || traceStartedRef.current) return trace;
|
|
37007
|
+
traceStartedRef.current = true;
|
|
37008
|
+
await emitSessionStart(trace, {
|
|
37009
|
+
cwd: process.cwd(),
|
|
37010
|
+
model: info.model,
|
|
37011
|
+
mode: info.mode,
|
|
37012
|
+
config: info.config,
|
|
37013
|
+
tools: info.tools,
|
|
37014
|
+
systemPrompt: info.systemPrompt,
|
|
37015
|
+
toolProfile: info.toolProfile
|
|
37016
|
+
});
|
|
37017
|
+
return trace;
|
|
37018
|
+
},
|
|
37019
|
+
[ensureTrace]
|
|
37020
|
+
);
|
|
37021
|
+
React23.useEffect(() => {
|
|
37022
|
+
const onExit = () => endTrace("exit");
|
|
37023
|
+
process.on("exit", onExit);
|
|
37024
|
+
return () => {
|
|
37025
|
+
process.off("exit", onExit);
|
|
37026
|
+
endTrace("exit");
|
|
37027
|
+
};
|
|
37028
|
+
}, [endTrace]);
|
|
35651
37029
|
const saveSessionSafe = useCallback6(async () => {
|
|
35652
37030
|
const d = depsRef.current;
|
|
35653
37031
|
if (!d.cfg) return;
|
|
@@ -35686,11 +37064,18 @@ function useSessionManager(deps) {
|
|
|
35686
37064
|
setResumeProgress(0);
|
|
35687
37065
|
setResumeStage("Loading session file\u2026");
|
|
35688
37066
|
startSmoothProgress(0, 10, 400);
|
|
35689
|
-
const
|
|
37067
|
+
const loaded = checkpointId ? await loadSessionFromCheckpoint(filePath, checkpointId) : null;
|
|
37068
|
+
const file = loaded ? loaded.file : await loadSession(filePath);
|
|
35690
37069
|
setResumeProgress(10);
|
|
35691
37070
|
setResumeStage("Restoring artifact store\u2026");
|
|
35692
37071
|
startSmoothProgress(10, 20, 400);
|
|
35693
37072
|
d.messagesRef.current = file.messages;
|
|
37073
|
+
if (loaded && file.id === sessionIdRef.current) {
|
|
37074
|
+
const since = Date.parse(loaded.checkpoint.timestamp);
|
|
37075
|
+
traceReverted("restore_checkpoint", turnHistoryRef.current.filter((t) => Number.isNaN(since) || t.endedAt > since));
|
|
37076
|
+
}
|
|
37077
|
+
endTrace(checkpointId ? "restore_checkpoint" : "exit");
|
|
37078
|
+
turnHistoryRef.current = [];
|
|
35694
37079
|
sessionIdRef.current = file.id;
|
|
35695
37080
|
setLogSessionId(file.id);
|
|
35696
37081
|
sessionCreatedAtRef.current = file.createdAt;
|
|
@@ -35866,6 +37251,9 @@ ${summary}`
|
|
|
35866
37251
|
[checkpointSession, doResumeSession]
|
|
35867
37252
|
);
|
|
35868
37253
|
const resetSession = useCallback6(() => {
|
|
37254
|
+
traceReverted("clear", turnHistoryRef.current.slice(-2));
|
|
37255
|
+
turnHistoryRef.current = [];
|
|
37256
|
+
endTrace("clear");
|
|
35869
37257
|
sessionIdRef.current = null;
|
|
35870
37258
|
setLogSessionId(null);
|
|
35871
37259
|
sessionCreatedAtRef.current = null;
|
|
@@ -35873,7 +37261,7 @@ ${summary}`
|
|
|
35873
37261
|
const d = depsRef.current;
|
|
35874
37262
|
d.sessionStateRef.current = emptySessionState();
|
|
35875
37263
|
d.artifactStoreRef.current = new ArtifactStore();
|
|
35876
|
-
}, []);
|
|
37264
|
+
}, [endTrace, traceReverted]);
|
|
35877
37265
|
return {
|
|
35878
37266
|
sessionIdRef,
|
|
35879
37267
|
sessionCreatedAtRef,
|
|
@@ -35895,7 +37283,13 @@ ${summary}`
|
|
|
35895
37283
|
doResumeSession,
|
|
35896
37284
|
handleResumePick,
|
|
35897
37285
|
handleCheckpointPick,
|
|
35898
|
-
resetSession
|
|
37286
|
+
resetSession,
|
|
37287
|
+
traceRef,
|
|
37288
|
+
ensureTrace,
|
|
37289
|
+
ensureTraceStarted,
|
|
37290
|
+
endTrace,
|
|
37291
|
+
turnHistoryRef,
|
|
37292
|
+
noteTurnEnded
|
|
35899
37293
|
};
|
|
35900
37294
|
}
|
|
35901
37295
|
var init_use_session_manager = __esm({
|
|
@@ -35907,6 +37301,7 @@ var init_use_session_manager = __esm({
|
|
|
35907
37301
|
init_recall_inject();
|
|
35908
37302
|
init_usage_tracker();
|
|
35909
37303
|
init_log_sink();
|
|
37304
|
+
init_trace();
|
|
35910
37305
|
init_app_helpers();
|
|
35911
37306
|
init_continuation_summary();
|
|
35912
37307
|
init_config();
|
|
@@ -35945,9 +37340,9 @@ var init_resume_progress = __esm({
|
|
|
35945
37340
|
});
|
|
35946
37341
|
|
|
35947
37342
|
// src/agent/supervisor.ts
|
|
35948
|
-
import { readdir as
|
|
35949
|
-
import { createHash as
|
|
35950
|
-
import { resolve as resolve13, join as
|
|
37343
|
+
import { readdir as readdir9, readFile as readFile30, stat as stat9 } from "fs/promises";
|
|
37344
|
+
import { createHash as createHash6 } from "crypto";
|
|
37345
|
+
import { resolve as resolve13, join as join48 } from "path";
|
|
35951
37346
|
var TurnSupervisor;
|
|
35952
37347
|
var init_supervisor = __esm({
|
|
35953
37348
|
"src/agent/supervisor.ts"() {
|
|
@@ -36334,13 +37729,13 @@ var init_input_handlers = __esm({
|
|
|
36334
37729
|
});
|
|
36335
37730
|
|
|
36336
37731
|
// src/init/context-generator.ts
|
|
36337
|
-
import { existsSync as
|
|
36338
|
-
import { join as
|
|
37732
|
+
import { existsSync as existsSync9, statSync as statSync6 } from "fs";
|
|
37733
|
+
import { join as join49 } from "path";
|
|
36339
37734
|
function detectFlavor(cwd) {
|
|
36340
37735
|
for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
|
|
36341
37736
|
if (flavor === "generic") continue;
|
|
36342
37737
|
for (const sig of signatures) {
|
|
36343
|
-
const path =
|
|
37738
|
+
const path = join49(cwd, sig);
|
|
36344
37739
|
if (sig.includes("*")) {
|
|
36345
37740
|
try {
|
|
36346
37741
|
const parts = sig.split("*");
|
|
@@ -36352,7 +37747,7 @@ function detectFlavor(cwd) {
|
|
|
36352
37747
|
}
|
|
36353
37748
|
} catch {
|
|
36354
37749
|
}
|
|
36355
|
-
} else if (
|
|
37750
|
+
} else if (existsSync9(path)) {
|
|
36356
37751
|
return flavor;
|
|
36357
37752
|
}
|
|
36358
37753
|
}
|
|
@@ -36361,14 +37756,14 @@ function detectFlavor(cwd) {
|
|
|
36361
37756
|
}
|
|
36362
37757
|
function findFile(cwd, candidates) {
|
|
36363
37758
|
for (const c of candidates) {
|
|
36364
|
-
if (
|
|
37759
|
+
if (existsSync9(join49(cwd, c))) return c;
|
|
36365
37760
|
}
|
|
36366
37761
|
return null;
|
|
36367
37762
|
}
|
|
36368
37763
|
function findSourceRoots(cwd) {
|
|
36369
37764
|
const roots = [];
|
|
36370
37765
|
for (const r of SOURCE_ROOT_CANDIDATES) {
|
|
36371
|
-
const p =
|
|
37766
|
+
const p = join49(cwd, r);
|
|
36372
37767
|
try {
|
|
36373
37768
|
const s = statSync6(p);
|
|
36374
37769
|
if (s.isDirectory()) roots.push(r);
|
|
@@ -36379,9 +37774,9 @@ function findSourceRoots(cwd) {
|
|
|
36379
37774
|
}
|
|
36380
37775
|
function findCiConfig(cwd) {
|
|
36381
37776
|
for (const c of CI_PATHS) {
|
|
36382
|
-
if (
|
|
37777
|
+
if (existsSync9(join49(cwd, c))) {
|
|
36383
37778
|
try {
|
|
36384
|
-
const s = statSync6(
|
|
37779
|
+
const s = statSync6(join49(cwd, c));
|
|
36385
37780
|
return s.isDirectory() ? c : c;
|
|
36386
37781
|
} catch {
|
|
36387
37782
|
}
|
|
@@ -36518,7 +37913,7 @@ function analyzeProject(cwd) {
|
|
|
36518
37913
|
ciConfig: findCiConfig(cwd),
|
|
36519
37914
|
readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
|
|
36520
37915
|
sourceRoots: findSourceRoots(cwd),
|
|
36521
|
-
hasGit:
|
|
37916
|
+
hasGit: existsSync9(join49(cwd, ".git"))
|
|
36522
37917
|
};
|
|
36523
37918
|
}
|
|
36524
37919
|
function bashDiscoveryCommands(profile) {
|
|
@@ -36683,7 +38078,7 @@ Aim for 100\u2013200 lines total. Use markdown tables where they save space.
|
|
|
36683
38078
|
}
|
|
36684
38079
|
function buildInitPrompt(cwd) {
|
|
36685
38080
|
const existingName = ["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
|
|
36686
|
-
(n) =>
|
|
38081
|
+
(n) => existsSync9(join49(cwd, n))
|
|
36687
38082
|
);
|
|
36688
38083
|
const isRefresh = existingName !== void 0;
|
|
36689
38084
|
const targetFilename = existingName ?? "AGENTS.md";
|
|
@@ -36766,8 +38161,8 @@ var init_context_generator = __esm({
|
|
|
36766
38161
|
});
|
|
36767
38162
|
|
|
36768
38163
|
// src/init/run-init.ts
|
|
36769
|
-
import { existsSync as
|
|
36770
|
-
import { join as
|
|
38164
|
+
import { existsSync as existsSync10 } from "fs";
|
|
38165
|
+
import { join as join50 } from "path";
|
|
36771
38166
|
async function runInit(deps) {
|
|
36772
38167
|
const {
|
|
36773
38168
|
cfg,
|
|
@@ -36860,7 +38255,7 @@ async function runInit(deps) {
|
|
|
36860
38255
|
lspManagerRef.current.notifyChange(path, content);
|
|
36861
38256
|
} else {
|
|
36862
38257
|
void import("fs/promises").then(
|
|
36863
|
-
({ readFile:
|
|
38258
|
+
({ readFile: readFile32 }) => readFile32(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
|
|
36864
38259
|
})
|
|
36865
38260
|
);
|
|
36866
38261
|
}
|
|
@@ -36965,7 +38360,7 @@ async function runInit(deps) {
|
|
|
36965
38360
|
}
|
|
36966
38361
|
}
|
|
36967
38362
|
});
|
|
36968
|
-
if (
|
|
38363
|
+
if (existsSync10(join50(cwd, targetFilename))) {
|
|
36969
38364
|
if (cacheStableRef.current) {
|
|
36970
38365
|
messagesRef.current[1] = {
|
|
36971
38366
|
role: "system",
|
|
@@ -37057,11 +38452,11 @@ var init_run_init = __esm({
|
|
|
37057
38452
|
});
|
|
37058
38453
|
|
|
37059
38454
|
// src/skills/discovery.ts
|
|
37060
|
-
import { readdir as
|
|
37061
|
-
import { join as
|
|
38455
|
+
import { readdir as readdir10, stat as stat10, readFile as readFile31 } from "fs/promises";
|
|
38456
|
+
import { join as join51, extname as extname2 } from "path";
|
|
37062
38457
|
async function dirExists(path) {
|
|
37063
38458
|
try {
|
|
37064
|
-
const s = await
|
|
38459
|
+
const s = await stat10(path);
|
|
37065
38460
|
return s.isDirectory();
|
|
37066
38461
|
} catch {
|
|
37067
38462
|
return false;
|
|
@@ -37069,7 +38464,7 @@ async function dirExists(path) {
|
|
|
37069
38464
|
}
|
|
37070
38465
|
async function fileExists(path) {
|
|
37071
38466
|
try {
|
|
37072
|
-
const s = await
|
|
38467
|
+
const s = await stat10(path);
|
|
37073
38468
|
return s.isFile();
|
|
37074
38469
|
} catch {
|
|
37075
38470
|
return false;
|
|
@@ -37077,25 +38472,25 @@ async function fileExists(path) {
|
|
|
37077
38472
|
}
|
|
37078
38473
|
async function scanSkillDir(dirPath, source) {
|
|
37079
38474
|
if (!await dirExists(dirPath)) return [];
|
|
37080
|
-
const entries = await
|
|
38475
|
+
const entries = await readdir10(dirPath, { withFileTypes: true });
|
|
37081
38476
|
const files = [];
|
|
37082
38477
|
for (const entry of entries) {
|
|
37083
38478
|
if (!entry.isFile()) continue;
|
|
37084
38479
|
if (!SKILL_EXTENSIONS.has(extname2(entry.name))) continue;
|
|
37085
|
-
files.push({ filePath:
|
|
38480
|
+
files.push({ filePath: join51(dirPath, entry.name), source });
|
|
37086
38481
|
}
|
|
37087
38482
|
return files;
|
|
37088
38483
|
}
|
|
37089
38484
|
async function discoverSkills(cwd) {
|
|
37090
|
-
const agentsSkills = await scanSkillDir(
|
|
37091
|
-
const agentsMd = await fileExists(
|
|
37092
|
-
const githubSkills = await scanSkillDir(
|
|
37093
|
-
const kimiflareSkills = await scanSkillDir(
|
|
38485
|
+
const agentsSkills = await scanSkillDir(join51(cwd, ".agents", "skills"), "agents");
|
|
38486
|
+
const agentsMd = await fileExists(join51(cwd, "AGENTS.md")) ? [{ filePath: join51(cwd, "AGENTS.md"), source: "agents-md" }] : [];
|
|
38487
|
+
const githubSkills = await scanSkillDir(join51(cwd, ".github", "skills"), "github");
|
|
38488
|
+
const kimiflareSkills = await scanSkillDir(join51(cwd, ".kimiflare", "skills"), "kimiflare");
|
|
37094
38489
|
const ordered = [...agentsSkills, ...agentsMd, ...githubSkills, ...kimiflareSkills];
|
|
37095
38490
|
return ordered;
|
|
37096
38491
|
}
|
|
37097
38492
|
async function readSkillFile(filePath) {
|
|
37098
|
-
const bytes = await
|
|
38493
|
+
const bytes = await readFile31(filePath);
|
|
37099
38494
|
return { bytes, text: bytes.toString("utf-8") };
|
|
37100
38495
|
}
|
|
37101
38496
|
var SKILL_EXTENSIONS;
|
|
@@ -37107,9 +38502,9 @@ var init_discovery = __esm({
|
|
|
37107
38502
|
});
|
|
37108
38503
|
|
|
37109
38504
|
// src/skills/parser.ts
|
|
37110
|
-
import { createHash as
|
|
38505
|
+
import { createHash as createHash7 } from "crypto";
|
|
37111
38506
|
function sha2562(input) {
|
|
37112
|
-
return
|
|
38507
|
+
return createHash7("sha256").update(input).digest("hex");
|
|
37113
38508
|
}
|
|
37114
38509
|
function computeContentHash(rawText, parserVersion) {
|
|
37115
38510
|
return sha2562(`${rawText}
|
|
@@ -37300,8 +38695,8 @@ var init_skills = __esm({
|
|
|
37300
38695
|
});
|
|
37301
38696
|
|
|
37302
38697
|
// src/ui/run-startup-tasks.ts
|
|
37303
|
-
import { existsSync as
|
|
37304
|
-
import { join as
|
|
38698
|
+
import { existsSync as existsSync11 } from "fs";
|
|
38699
|
+
import { join as join52 } from "path";
|
|
37305
38700
|
function runStartupTasks(deps) {
|
|
37306
38701
|
const {
|
|
37307
38702
|
cfg,
|
|
@@ -37315,6 +38710,7 @@ function runStartupTasks(deps) {
|
|
|
37315
38710
|
} = deps;
|
|
37316
38711
|
const auth = llmAuthFromConfig(cfg);
|
|
37317
38712
|
void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(({ pruneSessions: pruneSessions2 }) => pruneSessions2());
|
|
38713
|
+
void Promise.resolve().then(() => (init_retention(), retention_exports)).then(({ pruneTraces: pruneTraces2 }) => pruneTraces2({ root: cfg.trace?.root }));
|
|
37318
38714
|
void Promise.resolve().then(() => (init_log_sink(), log_sink_exports)).then(({ pruneOldLogs: pruneOldLogs2, isLogSinkEnabled: isLogSinkEnabled2 }) => {
|
|
37319
38715
|
if (!isLogSinkEnabled2()) return;
|
|
37320
38716
|
try {
|
|
@@ -37323,7 +38719,7 @@ function runStartupTasks(deps) {
|
|
|
37323
38719
|
}
|
|
37324
38720
|
});
|
|
37325
38721
|
if (cfg.memoryEnabled) {
|
|
37326
|
-
const dbPath = cfg.memoryDbPath ??
|
|
38722
|
+
const dbPath = cfg.memoryDbPath ?? join52(process.cwd(), ".kimiflare", "memory.db");
|
|
37327
38723
|
const manager = new MemoryManager({
|
|
37328
38724
|
dbPath,
|
|
37329
38725
|
...auth,
|
|
@@ -37355,7 +38751,7 @@ function runStartupTasks(deps) {
|
|
|
37355
38751
|
});
|
|
37356
38752
|
const cwd = process.cwd();
|
|
37357
38753
|
sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
|
|
37358
|
-
if (["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) =>
|
|
38754
|
+
if (["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) => existsSync11(join52(cwd, name)))) {
|
|
37359
38755
|
const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
|
|
37360
38756
|
const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
|
|
37361
38757
|
if (driftCount >= 5) {
|
|
@@ -37366,7 +38762,7 @@ function runStartupTasks(deps) {
|
|
|
37366
38762
|
memoryManagerRef.current?.close();
|
|
37367
38763
|
memoryManagerRef.current = null;
|
|
37368
38764
|
}
|
|
37369
|
-
const skillDbPath = cfg.memoryDbPath ??
|
|
38765
|
+
const skillDbPath = cfg.memoryDbPath ?? join52(process.cwd(), ".kimiflare", "memory.db");
|
|
37370
38766
|
const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
|
|
37371
38767
|
initSkillsSchema(skillDb);
|
|
37372
38768
|
void indexSkills({
|
|
@@ -37611,7 +39007,8 @@ async function runCompact(deps) {
|
|
|
37611
39007
|
pendingToolCallsRef,
|
|
37612
39008
|
hooks,
|
|
37613
39009
|
sessionId,
|
|
37614
|
-
supervisorRef
|
|
39010
|
+
supervisorRef,
|
|
39011
|
+
trace
|
|
37615
39012
|
} = deps;
|
|
37616
39013
|
if (busy || supervisorRef.current.isRunning) {
|
|
37617
39014
|
setEvents((e) => [
|
|
@@ -37647,6 +39044,7 @@ async function runCompact(deps) {
|
|
|
37647
39044
|
if (result2.metrics.rawTurnsRemoved === 0) {
|
|
37648
39045
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "nothing to compact yet" }]);
|
|
37649
39046
|
} else {
|
|
39047
|
+
traceCompiledCompaction(trace, result2);
|
|
37650
39048
|
messagesRef.current = result2.newMessages;
|
|
37651
39049
|
sessionStateRef.current = result2.newState;
|
|
37652
39050
|
setEvents(
|
|
@@ -37665,6 +39063,7 @@ async function runCompact(deps) {
|
|
|
37665
39063
|
await saveSessionSafe();
|
|
37666
39064
|
}
|
|
37667
39065
|
} else {
|
|
39066
|
+
const before = messagesRef.current;
|
|
37668
39067
|
const result2 = await summarizeMessagesViaLlm({
|
|
37669
39068
|
...llmAuthFromConfig(cfg),
|
|
37670
39069
|
model: cfg.model,
|
|
@@ -37674,6 +39073,7 @@ async function runCompact(deps) {
|
|
|
37674
39073
|
if (result2.replacedCount === 0) {
|
|
37675
39074
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "nothing to compact yet" }]);
|
|
37676
39075
|
} else {
|
|
39076
|
+
traceLlmCompaction(trace, before, result2);
|
|
37677
39077
|
messagesRef.current = result2.newMessages;
|
|
37678
39078
|
setEvents(
|
|
37679
39079
|
(e) => compactEventsVisual(
|
|
@@ -37714,12 +39114,13 @@ var init_run_compact = __esm({
|
|
|
37714
39114
|
init_logger();
|
|
37715
39115
|
init_app_helpers();
|
|
37716
39116
|
init_llm_auth();
|
|
39117
|
+
init_compaction();
|
|
37717
39118
|
}
|
|
37718
39119
|
});
|
|
37719
39120
|
|
|
37720
39121
|
// src/commands/save.ts
|
|
37721
39122
|
import { mkdir as mkdir16, writeFile as writeFile15, unlink as unlink5 } from "fs/promises";
|
|
37722
|
-
import { dirname as
|
|
39123
|
+
import { dirname as dirname21 } from "path";
|
|
37723
39124
|
async function saveCustomCommand(opts2) {
|
|
37724
39125
|
const dir = opts2.source === "project" ? projectCommandsDir(opts2.cwd) : globalCommandsDir();
|
|
37725
39126
|
const filepath = `${dir}/${opts2.name}.md`;
|
|
@@ -37730,7 +39131,7 @@ async function saveCustomCommand(opts2) {
|
|
|
37730
39131
|
if (opts2.effort) data.effort = opts2.effort;
|
|
37731
39132
|
const frontmatter = serializeFrontmatter(data);
|
|
37732
39133
|
const content = frontmatter + opts2.template;
|
|
37733
|
-
await mkdir16(
|
|
39134
|
+
await mkdir16(dirname21(filepath), { recursive: true });
|
|
37734
39135
|
await writeFile15(filepath, content, "utf8");
|
|
37735
39136
|
return { filepath };
|
|
37736
39137
|
}
|
|
@@ -37853,9 +39254,9 @@ __export(app_exports, {
|
|
|
37853
39254
|
});
|
|
37854
39255
|
import React25, { useState as useState27, useRef as useRef9, useEffect as useEffect11, useCallback as useCallback8, useMemo as useMemo6 } from "react";
|
|
37855
39256
|
import { Box as Box40, Text as Text41, useApp, useInput as useInput19, useWindowSize as useWindowSize2, render } from "ink";
|
|
37856
|
-
import { existsSync as
|
|
37857
|
-
import { join as
|
|
37858
|
-
import { homedir as
|
|
39257
|
+
import { existsSync as existsSync12 } from "fs";
|
|
39258
|
+
import { join as join53, resolve as resolve14 } from "path";
|
|
39259
|
+
import { homedir as homedir30 } from "os";
|
|
37859
39260
|
import { jsx as jsx42, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
37860
39261
|
function App({
|
|
37861
39262
|
initialCfg,
|
|
@@ -38112,6 +39513,8 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38112
39513
|
}, []);
|
|
38113
39514
|
const sessionStartRecallRef = useRef9(null);
|
|
38114
39515
|
const kimiMdStaleNudgedRef = useRef9(false);
|
|
39516
|
+
const currentTurnIdRef = useRef9(null);
|
|
39517
|
+
const denyFeedbackRef = useRef9(false);
|
|
38115
39518
|
const sessionPlanRef = useRef9(null);
|
|
38116
39519
|
const planCompletePlanRef = useRef9(null);
|
|
38117
39520
|
const sessionMgr = useSessionManager({
|
|
@@ -38149,7 +39552,11 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38149
39552
|
doResumeSession,
|
|
38150
39553
|
handleResumePick,
|
|
38151
39554
|
handleCheckpointPick,
|
|
38152
|
-
resetSession
|
|
39555
|
+
resetSession,
|
|
39556
|
+
traceRef,
|
|
39557
|
+
ensureTraceStarted,
|
|
39558
|
+
turnHistoryRef,
|
|
39559
|
+
noteTurnEnded
|
|
38153
39560
|
} = sessionMgr;
|
|
38154
39561
|
const pendingTextRef = useRef9(/* @__PURE__ */ new Map());
|
|
38155
39562
|
const flushTimeoutRef = useRef9(null);
|
|
@@ -38175,7 +39582,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38175
39582
|
const exactDirectory = query === "~" || query === "." || query === ".." || query.endsWith("/..");
|
|
38176
39583
|
const prefix = exactDirectory ? `${query.replace(/\/$/, "")}/` : slash >= 0 ? query.slice(0, slash + 1) : "";
|
|
38177
39584
|
const directory = exactDirectory ? query : slash >= 0 ? query.slice(0, slash + 1) : query;
|
|
38178
|
-
const expanded = directory.startsWith("~") ?
|
|
39585
|
+
const expanded = directory.startsWith("~") ? join53(homedir30(), directory.slice(1)) : directory;
|
|
38179
39586
|
const target = resolve14(cwd, expanded || ".");
|
|
38180
39587
|
const entries2 = await glob("*", {
|
|
38181
39588
|
cwd: target,
|
|
@@ -38436,6 +39843,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38436
39843
|
store
|
|
38437
39844
|
});
|
|
38438
39845
|
if (result2.metrics.rawTurnsRemoved > 0) {
|
|
39846
|
+
traceCompiledCompaction(traceRef.current, result2, currentTurnIdRef.current);
|
|
38439
39847
|
sessionStateRef.current = result2.newState;
|
|
38440
39848
|
setEvents((e) => [
|
|
38441
39849
|
...e,
|
|
@@ -38481,6 +39889,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38481
39889
|
signal
|
|
38482
39890
|
});
|
|
38483
39891
|
if (result2.replacedCount > 0) {
|
|
39892
|
+
traceLlmCompaction(traceRef.current, messages, result2, currentTurnIdRef.current);
|
|
38484
39893
|
setEvents((e) => [
|
|
38485
39894
|
...e,
|
|
38486
39895
|
{
|
|
@@ -38654,7 +40063,8 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38654
40063
|
pendingToolCallsRef,
|
|
38655
40064
|
hooks: hooksManagerRef.current,
|
|
38656
40065
|
sessionId: sessionIdRef.current,
|
|
38657
|
-
supervisorRef
|
|
40066
|
+
supervisorRef,
|
|
40067
|
+
trace: traceRef.current
|
|
38658
40068
|
});
|
|
38659
40069
|
}, [cfg, busy, saveSessionSafe]);
|
|
38660
40070
|
const runInit2 = useCallback8(async () => {
|
|
@@ -38834,7 +40244,9 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38834
40244
|
compiledContextRef,
|
|
38835
40245
|
lastApiErrorRef,
|
|
38836
40246
|
activeScopeRef,
|
|
38837
|
-
sessionPlanRef
|
|
40247
|
+
sessionPlanRef,
|
|
40248
|
+
traceRef,
|
|
40249
|
+
turnHistoryRef
|
|
38838
40250
|
}), [
|
|
38839
40251
|
exit,
|
|
38840
40252
|
busy,
|
|
@@ -39137,6 +40549,8 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39137
40549
|
}
|
|
39138
40550
|
}
|
|
39139
40551
|
messagesRef.current.push({ role: "user", content });
|
|
40552
|
+
const traceUserSource = denyFeedbackRef.current ? "deny_feedback" : "typed";
|
|
40553
|
+
denyFeedbackRef.current = false;
|
|
39140
40554
|
await saveSessionSafe();
|
|
39141
40555
|
if (compiledContextRef.current) {
|
|
39142
40556
|
const { ids, recalled } = recallArtifacts(messagesRef.current, artifactStoreRef.current, sessionStateRef.current);
|
|
@@ -39150,7 +40564,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39150
40564
|
}
|
|
39151
40565
|
}
|
|
39152
40566
|
turnCounterRef.current += 1;
|
|
39153
|
-
if (turnCounterRef.current % 15 === 0 && ["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) =>
|
|
40567
|
+
if (turnCounterRef.current % 15 === 0 && ["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].some((name) => existsSync12(join53(process.cwd(), name))) && !kimiMdStale) {
|
|
39154
40568
|
setEvents((e) => [
|
|
39155
40569
|
...e,
|
|
39156
40570
|
{ kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps autopilot (formerly kimiflare) stay accurate as your project evolves." }
|
|
@@ -39174,7 +40588,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39174
40588
|
};
|
|
39175
40589
|
ensureSessionId();
|
|
39176
40590
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
39177
|
-
const filePath =
|
|
40591
|
+
const filePath = join53(sessionsDir3(), `${sessionIdRef.current}.json`);
|
|
39178
40592
|
await addCheckpoint(filePath, cp);
|
|
39179
40593
|
}
|
|
39180
40594
|
const summary = await generateContinuationSummary({
|
|
@@ -39423,6 +40837,34 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39423
40837
|
offered: turnTools.some((tool) => tool.name === "subagent"),
|
|
39424
40838
|
guidance: delegationGuidance.kind
|
|
39425
40839
|
});
|
|
40840
|
+
const turnId = makeTurnId();
|
|
40841
|
+
currentTurnIdRef.current = turnId;
|
|
40842
|
+
const trace = await ensureTraceStarted({
|
|
40843
|
+
tools: toOpenAIToolDefs(turnTools),
|
|
40844
|
+
systemPrompt: messagesRef.current.filter((m) => m.role === "system" && typeof m.content === "string").map((m) => m.content).join("\n\n"),
|
|
40845
|
+
model: overrideModel ?? cfg.model,
|
|
40846
|
+
mode: "tui",
|
|
40847
|
+
config: cfg
|
|
40848
|
+
});
|
|
40849
|
+
if (trace.enabled) {
|
|
40850
|
+
const previousTurn = turnHistoryRef.current.at(-1);
|
|
40851
|
+
if (previousTurn) {
|
|
40852
|
+
trace.emit(
|
|
40853
|
+
"outcome",
|
|
40854
|
+
{ kind: "next_message", turn_id: previousTurn.turnId, classification: classifyNextMessage(trimmed), classifier: "regex" },
|
|
40855
|
+
previousTurn.turnId
|
|
40856
|
+
);
|
|
40857
|
+
}
|
|
40858
|
+
trace.emit(
|
|
40859
|
+
"user_message",
|
|
40860
|
+
{
|
|
40861
|
+
content_blob: trace.blob(typeof content === "string" ? content : trimmed) ?? "",
|
|
40862
|
+
source: traceUserSource,
|
|
40863
|
+
attachments: Array.isArray(content) ? content.filter((p) => p.type !== "text").length : 0
|
|
40864
|
+
},
|
|
40865
|
+
turnId
|
|
40866
|
+
);
|
|
40867
|
+
}
|
|
39426
40868
|
supervisorRef.current.startTurn(
|
|
39427
40869
|
{
|
|
39428
40870
|
...llmAuthFromConfig(cfg),
|
|
@@ -39468,12 +40910,14 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39468
40910
|
},
|
|
39469
40911
|
mode: modeRef.current,
|
|
39470
40912
|
cacheStable: cacheStableRef.current,
|
|
40913
|
+
trace,
|
|
40914
|
+
turnId,
|
|
39471
40915
|
onFileChange: (path, content2) => {
|
|
39472
40916
|
if (content2) {
|
|
39473
40917
|
lspManagerRef.current.notifyChange(path, content2);
|
|
39474
40918
|
} else {
|
|
39475
40919
|
void import("fs/promises").then(
|
|
39476
|
-
({ readFile:
|
|
40920
|
+
({ readFile: readFile32 }) => readFile32(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
|
|
39477
40921
|
})
|
|
39478
40922
|
);
|
|
39479
40923
|
}
|
|
@@ -39482,6 +40926,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39482
40926
|
},
|
|
39483
40927
|
{
|
|
39484
40928
|
onDone: () => {
|
|
40929
|
+
noteTurnEnded(turnId);
|
|
39485
40930
|
void (async () => {
|
|
39486
40931
|
await saveSessionSafe();
|
|
39487
40932
|
if (turnScope.signal.aborted) {
|
|
@@ -39509,6 +40954,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39509
40954
|
store
|
|
39510
40955
|
});
|
|
39511
40956
|
if (result2.metrics.rawTurnsRemoved > 0) {
|
|
40957
|
+
traceCompiledCompaction(traceRef.current, result2, turnId);
|
|
39512
40958
|
messagesRef.current = result2.newMessages;
|
|
39513
40959
|
sessionStateRef.current = result2.newState;
|
|
39514
40960
|
setEvents((e) => [
|
|
@@ -39524,6 +40970,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39524
40970
|
}
|
|
39525
40971
|
} else {
|
|
39526
40972
|
try {
|
|
40973
|
+
const before = messagesRef.current;
|
|
39527
40974
|
const result2 = await summarizeMessagesViaLlm({
|
|
39528
40975
|
...llmAuthFromConfig(cfg),
|
|
39529
40976
|
model: cfg.model,
|
|
@@ -39531,6 +40978,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39531
40978
|
signal: turnScope.signal
|
|
39532
40979
|
});
|
|
39533
40980
|
if (result2.replacedCount > 0) {
|
|
40981
|
+
traceLlmCompaction(traceRef.current, before, result2, turnId);
|
|
39534
40982
|
messagesRef.current = result2.newMessages;
|
|
39535
40983
|
setEvents((e) => [
|
|
39536
40984
|
...e,
|
|
@@ -39596,6 +41044,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39596
41044
|
keepLastTurns: 1
|
|
39597
41045
|
});
|
|
39598
41046
|
if (result2.metrics.rawTurnsRemoved > 0) {
|
|
41047
|
+
traceCompiledCompaction(traceRef.current, result2, turnId);
|
|
39599
41048
|
messagesRef.current = result2.newMessages;
|
|
39600
41049
|
sessionStateRef.current = result2.newState;
|
|
39601
41050
|
setEvents((e) => [
|
|
@@ -39611,6 +41060,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39611
41060
|
}
|
|
39612
41061
|
} else {
|
|
39613
41062
|
try {
|
|
41063
|
+
const before = messagesRef.current;
|
|
39614
41064
|
const result2 = await summarizeMessagesViaLlm({
|
|
39615
41065
|
...llmAuthFromConfig(cfg),
|
|
39616
41066
|
model: cfg.model,
|
|
@@ -39619,6 +41069,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39619
41069
|
signal: turnScope.signal
|
|
39620
41070
|
});
|
|
39621
41071
|
if (result2.replacedCount > 0) {
|
|
41072
|
+
traceLlmCompaction(traceRef.current, before, result2, turnId);
|
|
39622
41073
|
messagesRef.current = result2.newMessages;
|
|
39623
41074
|
setEvents((e) => [
|
|
39624
41075
|
...e,
|
|
@@ -39659,7 +41110,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39659
41110
|
};
|
|
39660
41111
|
ensureSessionId();
|
|
39661
41112
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
39662
|
-
const filePath =
|
|
41113
|
+
const filePath = join53(sessionsDir3(), `${sessionIdRef.current}.json`);
|
|
39663
41114
|
await addCheckpoint(filePath, cp);
|
|
39664
41115
|
}
|
|
39665
41116
|
const summary = await generateContinuationSummary({
|
|
@@ -39710,6 +41161,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39710
41161
|
}
|
|
39711
41162
|
},
|
|
39712
41163
|
onError: async (e) => {
|
|
41164
|
+
noteTurnEnded(turnId);
|
|
39713
41165
|
if (e.name === "AbortError") {
|
|
39714
41166
|
for (const [tcId, tcName] of pendingToolCallsRef.current) {
|
|
39715
41167
|
messagesRef.current.push({
|
|
@@ -39741,7 +41193,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39741
41193
|
}
|
|
39742
41194
|
);
|
|
39743
41195
|
},
|
|
39744
|
-
[cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateResponseMeta, setShowPlanCompletePicker, runUserShellCommand]
|
|
41196
|
+
[cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateResponseMeta, setShowPlanCompletePicker, runUserShellCommand, ensureTraceStarted]
|
|
39745
41197
|
);
|
|
39746
41198
|
useEffect11(() => {
|
|
39747
41199
|
if (!busy && queuePlanDraft === null && supervisorRef.current.phase === "idle") {
|
|
@@ -40242,7 +41694,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40242
41694
|
lsp: {
|
|
40243
41695
|
servers: cfg?.lspServers ?? {},
|
|
40244
41696
|
scope: lspScope,
|
|
40245
|
-
hasProjectDir:
|
|
41697
|
+
hasProjectDir: existsSync12(join53(process.cwd(), ".kimiflare"))
|
|
40246
41698
|
}
|
|
40247
41699
|
});
|
|
40248
41700
|
});
|
|
@@ -40299,7 +41751,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40299
41751
|
onCommandDelete: handleCommandDelete2,
|
|
40300
41752
|
lspServers: cfg?.lspServers ?? {},
|
|
40301
41753
|
lspScope,
|
|
40302
|
-
hasProjectDir:
|
|
41754
|
+
hasProjectDir: existsSync12(join53(process.cwd(), ".kimiflare")),
|
|
40303
41755
|
onLspSave: handleLspSave2,
|
|
40304
41756
|
themes: themeList(),
|
|
40305
41757
|
onPickTheme: handleThemePick,
|
|
@@ -40370,6 +41822,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40370
41822
|
args: perm.args,
|
|
40371
41823
|
onDecide: decidePermission2,
|
|
40372
41824
|
onFeedback: (text) => {
|
|
41825
|
+
denyFeedbackRef.current = true;
|
|
40373
41826
|
submitRef.current(text);
|
|
40374
41827
|
}
|
|
40375
41828
|
}
|
|
@@ -40635,6 +42088,8 @@ var init_app = __esm({
|
|
|
40635
42088
|
init_run_startup_tasks();
|
|
40636
42089
|
init_manager_init();
|
|
40637
42090
|
init_run_compact();
|
|
42091
|
+
init_trace();
|
|
42092
|
+
init_registry2();
|
|
40638
42093
|
init_distill();
|
|
40639
42094
|
init_plan_resolver();
|
|
40640
42095
|
init_user_shell();
|
|
@@ -40780,6 +42235,8 @@ init_glob();
|
|
|
40780
42235
|
init_permissions_evaluator();
|
|
40781
42236
|
init_classify();
|
|
40782
42237
|
init_subagent_policy();
|
|
42238
|
+
init_trace();
|
|
42239
|
+
init_registry2();
|
|
40783
42240
|
import { readFile as readFile15 } from "fs/promises";
|
|
40784
42241
|
import { resolve as resolve6, basename as basename3 } from "path";
|
|
40785
42242
|
async function resolveSession(opts2) {
|
|
@@ -40819,8 +42276,8 @@ async function resolveFiles(filePatterns, cwd) {
|
|
|
40819
42276
|
const resolved = /* @__PURE__ */ new Set();
|
|
40820
42277
|
for (const pattern of filePatterns) {
|
|
40821
42278
|
try {
|
|
40822
|
-
const
|
|
40823
|
-
if (
|
|
42279
|
+
const stat11 = await import("fs/promises").then((m) => m.stat(resolve6(cwd, pattern)));
|
|
42280
|
+
if (stat11.isFile()) {
|
|
40824
42281
|
resolved.add(resolve6(cwd, pattern));
|
|
40825
42282
|
continue;
|
|
40826
42283
|
}
|
|
@@ -40914,24 +42371,43 @@ async function runPrintMode(opts2) {
|
|
|
40914
42371
|
});
|
|
40915
42372
|
}
|
|
40916
42373
|
const messages = [];
|
|
42374
|
+
const systemPrompt = buildSystemPrompt({ cwd, tools: workerTools, model: opts2.model, preferPullRequests: opts2.preferPullRequests });
|
|
40917
42375
|
if (isNew || sessionFile.messages.length === 0) {
|
|
40918
|
-
messages.push({
|
|
40919
|
-
role: "system",
|
|
40920
|
-
content: buildSystemPrompt({ cwd, tools: workerTools, model: opts2.model, preferPullRequests: opts2.preferPullRequests })
|
|
40921
|
-
});
|
|
42376
|
+
messages.push({ role: "system", content: systemPrompt });
|
|
40922
42377
|
} else {
|
|
40923
42378
|
const nonSystem = sessionFile.messages.filter(
|
|
40924
42379
|
(m) => m.role !== "system" || typeof m.content === "string" && m.content.startsWith("[compiled session state]")
|
|
40925
42380
|
);
|
|
40926
|
-
messages.push({
|
|
40927
|
-
role: "system",
|
|
40928
|
-
content: buildSystemPrompt({ cwd, tools: workerTools, model: opts2.model, preferPullRequests: opts2.preferPullRequests })
|
|
40929
|
-
});
|
|
42381
|
+
messages.push({ role: "system", content: systemPrompt });
|
|
40930
42382
|
messages.push(...nonSystem);
|
|
40931
42383
|
}
|
|
42384
|
+
const trace = createRecorder({
|
|
42385
|
+
enabled: opts2.trace?.enabled === true,
|
|
42386
|
+
sessionId: sessionFile.id,
|
|
42387
|
+
root: opts2.trace?.root
|
|
42388
|
+
});
|
|
42389
|
+
await emitSessionStart(trace, {
|
|
42390
|
+
cwd,
|
|
42391
|
+
model: opts2.model,
|
|
42392
|
+
mode: "print",
|
|
42393
|
+
config: opts2.traceConfigSnapshot,
|
|
42394
|
+
tools: toOpenAIToolDefs(workerTools),
|
|
42395
|
+
systemPrompt,
|
|
42396
|
+
toolProfile: opts2.workerProfile ?? "full"
|
|
42397
|
+
});
|
|
42398
|
+
let traceCostUsd = 0;
|
|
42399
|
+
let traceEndReason = "exit";
|
|
40932
42400
|
const files = opts2.files ? await resolveFiles(opts2.files, cwd) : [];
|
|
40933
42401
|
const { content: userContent } = await buildUserMessage(opts2.prompt, files, cwd);
|
|
40934
42402
|
messages.push({ role: "user", content: userContent });
|
|
42403
|
+
const turnId = makeTurnId();
|
|
42404
|
+
if (trace.enabled) {
|
|
42405
|
+
trace.emit(
|
|
42406
|
+
"user_message",
|
|
42407
|
+
{ content_blob: trace.blob(opts2.prompt) ?? "", source: "print", attachments: files.length },
|
|
42408
|
+
turnId
|
|
42409
|
+
);
|
|
42410
|
+
}
|
|
40935
42411
|
const controller = new AbortController();
|
|
40936
42412
|
process.on("SIGINT", () => controller.abort());
|
|
40937
42413
|
const format = opts2.format ?? "text";
|
|
@@ -40955,6 +42431,7 @@ async function runPrintMode(opts2) {
|
|
|
40955
42431
|
onUsageFinal: (u, meta) => {
|
|
40956
42432
|
const lookup = opts2.openrouterApiKey && !auth.customEndpoint && meta?.generationId ? { apiKey: opts2.openrouterApiKey, meta } : void 0;
|
|
40957
42433
|
usageWrites.push(recordUsage(sessionFile.id, u, lookup, opts2.model).catch(() => void 0));
|
|
42434
|
+
if (typeof u.cost === "number") traceCostUsd += u.cost;
|
|
40958
42435
|
},
|
|
40959
42436
|
onReasoningDelta: opts2.showReasoning ? (delta) => {
|
|
40960
42437
|
if (format === "text") {
|
|
@@ -41096,7 +42573,9 @@ async function runPrintMode(opts2) {
|
|
|
41096
42573
|
// --continue sessions grow across runs; archive older turns onto the
|
|
41097
42574
|
// session file (saved below) instead of failing on context size.
|
|
41098
42575
|
compaction: sessionFileCompactionTarget(sessionFile),
|
|
41099
|
-
callbacks
|
|
42576
|
+
callbacks,
|
|
42577
|
+
trace,
|
|
42578
|
+
turnId
|
|
41100
42579
|
});
|
|
41101
42580
|
} catch (err) {
|
|
41102
42581
|
if (err instanceof BudgetExhaustedError) {
|
|
@@ -41126,9 +42605,12 @@ async function runPrintMode(opts2) {
|
|
|
41126
42605
|
process.exitCode = 1;
|
|
41127
42606
|
return;
|
|
41128
42607
|
}
|
|
42608
|
+
traceEndReason = "crash";
|
|
41129
42609
|
throw err;
|
|
41130
42610
|
} finally {
|
|
41131
42611
|
await Promise.all(usageWrites);
|
|
42612
|
+
trace.emit("session_end", { reason: traceEndReason, total_cost_usd: traceCostUsd || void 0 }, null);
|
|
42613
|
+
await trace.flush();
|
|
41132
42614
|
}
|
|
41133
42615
|
sessionFile.messages = messages;
|
|
41134
42616
|
sessionFile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -41442,7 +42924,9 @@ To use Requesty instead, set REQUESTY_API_KEY or run autopilot auth requesty`
|
|
|
41442
42924
|
dir: opts.dir,
|
|
41443
42925
|
title: opts.title,
|
|
41444
42926
|
permissions: cfg.permissions,
|
|
41445
|
-
workerProfile: opts.workerProfile
|
|
42927
|
+
workerProfile: opts.workerProfile,
|
|
42928
|
+
trace: cfg.trace,
|
|
42929
|
+
traceConfigSnapshot: cfg
|
|
41446
42930
|
});
|
|
41447
42931
|
return;
|
|
41448
42932
|
}
|