autopilot-ai 1.19.3 → 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 +1850 -407
- 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,10 +17904,10 @@ data: ${JSON.stringify(event.data)}
|
|
|
16747
17904
|
});
|
|
16748
17905
|
|
|
16749
17906
|
// src/server/aster-cell-runtime.ts
|
|
16750
|
-
import { execFile as
|
|
17907
|
+
import { execFile as execFile7 } from "child_process";
|
|
16751
17908
|
import { readFile as readFile21 } from "fs/promises";
|
|
16752
|
-
import { promisify as
|
|
16753
|
-
import { randomUUID as
|
|
17909
|
+
import { promisify as promisify6 } from "util";
|
|
17910
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
16754
17911
|
async function writeCellFileChunked(cell, path, content) {
|
|
16755
17912
|
if (Buffer.byteLength(content, "utf8") <= CELL_FILE_WRITE_CHUNK_CHARS) {
|
|
16756
17913
|
await cell.writeFile(path, content, { mode: "0600" });
|
|
@@ -16785,7 +17942,7 @@ async function readBundledCellSdkBundle() {
|
|
|
16785
17942
|
throw new Error("cell_runtime_bundle_unavailable");
|
|
16786
17943
|
}
|
|
16787
17944
|
async function defaultArchiveWorkspace(workspaceRoot) {
|
|
16788
|
-
const { stdout } = await
|
|
17945
|
+
const { stdout } = await execFileAsync5("git", ["-C", workspaceRoot, "archive", "--format=tar", "HEAD"], {
|
|
16789
17946
|
encoding: "buffer",
|
|
16790
17947
|
maxBuffer: 512 * 1024 * 1024
|
|
16791
17948
|
});
|
|
@@ -16833,13 +17990,13 @@ ${redacted.slice(-half)}`;
|
|
|
16833
17990
|
function sleep4(ms) {
|
|
16834
17991
|
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
16835
17992
|
}
|
|
16836
|
-
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;
|
|
16837
17994
|
var init_aster_cell_runtime = __esm({
|
|
16838
17995
|
"src/server/aster-cell-runtime.ts"() {
|
|
16839
17996
|
"use strict";
|
|
16840
17997
|
init_version();
|
|
16841
17998
|
init_aster_tools();
|
|
16842
|
-
|
|
17999
|
+
execFileAsync5 = promisify6(execFile7);
|
|
16843
18000
|
CELL_WORKSPACE = "/workspace";
|
|
16844
18001
|
CELL_STATE_DIR = "/workspace/.aster";
|
|
16845
18002
|
CHECKPOINT_MESSAGE_FILE = `${CELL_STATE_DIR}/checkpoint-message`;
|
|
@@ -16946,7 +18103,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
16946
18103
|
await this.installRuntime(cell);
|
|
16947
18104
|
await this.ensureBridge(cell);
|
|
16948
18105
|
await this.control(cell, "POST", "/rpc", {
|
|
16949
|
-
id:
|
|
18106
|
+
id: randomUUID9(),
|
|
16950
18107
|
type: "new_session",
|
|
16951
18108
|
sessionId: input.sessionId,
|
|
16952
18109
|
config: { model: input.model, cwd: CELL_WORKSPACE }
|
|
@@ -16965,7 +18122,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
16965
18122
|
/** Switch the cell session's model between turns. The bridge persists it for session restore. */
|
|
16966
18123
|
async setModel(cellId, model) {
|
|
16967
18124
|
await this.ensureReady(cellId);
|
|
16968
|
-
await this.controlById(cellId, "POST", "/rpc", { id:
|
|
18125
|
+
await this.controlById(cellId, "POST", "/rpc", { id: randomUUID9(), type: "set_model", modelId: model });
|
|
16969
18126
|
}
|
|
16970
18127
|
/** Reinstall the runtime if the cell's image lost it and restart the bridge if it isn't running. */
|
|
16971
18128
|
async ensureReady(cellId) {
|
|
@@ -17028,7 +18185,7 @@ var init_aster_cell_runtime = __esm({
|
|
|
17028
18185
|
const active = this.activeRuns.get(runId);
|
|
17029
18186
|
if (!active) return;
|
|
17030
18187
|
active.stop();
|
|
17031
|
-
void this.controlById(active.cellId, "POST", "/rpc", { id:
|
|
18188
|
+
void this.controlById(active.cellId, "POST", "/rpc", { id: randomUUID9(), type: "abort" }).catch(() => {
|
|
17032
18189
|
});
|
|
17033
18190
|
}
|
|
17034
18191
|
async runTurn(turn, options = {}) {
|
|
@@ -17127,7 +18284,7 @@ ${turn.userText}` : turn.userText;
|
|
|
17127
18284
|
args: event.args ?? {}
|
|
17128
18285
|
});
|
|
17129
18286
|
await this.controlById(cellId, "POST", "/rpc", {
|
|
17130
|
-
id:
|
|
18287
|
+
id: randomUUID9(),
|
|
17131
18288
|
type: "resolve_permission",
|
|
17132
18289
|
requestId: event.requestId,
|
|
17133
18290
|
decision
|
|
@@ -17255,9 +18412,9 @@ ${turn.userText}` : turn.userText;
|
|
|
17255
18412
|
if (!isSafeArtifactPath(path)) throw new Error("invalid_artifact_path");
|
|
17256
18413
|
const cell = await this.provider.getCell(cellId);
|
|
17257
18414
|
const file = shellQuote2(`${ARTIFACTS_DIR}/${path}`);
|
|
17258
|
-
const
|
|
17259
|
-
if (
|
|
17260
|
-
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");
|
|
17261
18418
|
const encoded = await cell.exec(`base64 -w0 -- ${file}`);
|
|
17262
18419
|
if (encoded.exitCode !== 0) throw new Error("artifact_not_found");
|
|
17263
18420
|
return { data: Buffer.from(encoded.stdout.trim(), "base64"), mimeType: artifactMimeType(path), name: path.split("/").pop() ?? path };
|
|
@@ -17336,7 +18493,7 @@ ${text}` : text, { mode: "0600" });
|
|
|
17336
18493
|
}
|
|
17337
18494
|
}
|
|
17338
18495
|
async control(cell, method, path, body) {
|
|
17339
|
-
const requestId =
|
|
18496
|
+
const requestId = randomUUID9();
|
|
17340
18497
|
await cell.mkdir(CELL_CONTROL_DIR, { parents: true });
|
|
17341
18498
|
await writeCellFileChunked(cell, `${CELL_CONTROL_DIR}/${requestId}.json`, JSON.stringify({ method, path, body }));
|
|
17342
18499
|
const result2 = await cell.exec(`${CONTROL_RUNNER} ${requestId}`);
|
|
@@ -17552,7 +18709,7 @@ var init_hotcell_provider = __esm({
|
|
|
17552
18709
|
});
|
|
17553
18710
|
|
|
17554
18711
|
// src/server/routes.ts
|
|
17555
|
-
import { randomUUID as
|
|
18712
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
17556
18713
|
import { URL as URL2 } from "url";
|
|
17557
18714
|
import { readFile as readFile22, unlink as unlink3 } from "fs/promises";
|
|
17558
18715
|
import { resolve as resolve9, basename as basename5 } from "path";
|
|
@@ -17567,6 +18724,27 @@ function createHotcellAsterRuntime() {
|
|
|
17567
18724
|
throw error;
|
|
17568
18725
|
}
|
|
17569
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
|
+
}
|
|
17570
18748
|
function parseOptionalBudgetInteger(body, name, minimum = 1) {
|
|
17571
18749
|
if (!Object.hasOwn(body, name)) return void 0;
|
|
17572
18750
|
const value = body[name];
|
|
@@ -17617,8 +18795,8 @@ async function resolveFiles2(filePatterns, cwd) {
|
|
|
17617
18795
|
const resolved = /* @__PURE__ */ new Set();
|
|
17618
18796
|
for (const pattern of filePatterns) {
|
|
17619
18797
|
try {
|
|
17620
|
-
const
|
|
17621
|
-
if (
|
|
18798
|
+
const stat11 = await import("fs/promises").then((m) => m.stat(resolve9(cwd, pattern)));
|
|
18799
|
+
if (stat11.isFile()) {
|
|
17622
18800
|
resolved.add(resolve9(cwd, pattern));
|
|
17623
18801
|
continue;
|
|
17624
18802
|
}
|
|
@@ -17686,7 +18864,8 @@ function setupRoutes(config2, options = {}) {
|
|
|
17686
18864
|
executor: new ToolExecutor(tools),
|
|
17687
18865
|
sseClients: /* @__PURE__ */ new Set(),
|
|
17688
18866
|
runId: run.id,
|
|
17689
|
-
allowedTools
|
|
18867
|
+
allowedTools,
|
|
18868
|
+
trace: await startSessionTrace(config2, sessionFile, tools, sessionFile.messages)
|
|
17690
18869
|
};
|
|
17691
18870
|
activeSessions.set(run.sessionId, active);
|
|
17692
18871
|
activeRuns.set(run.id, active);
|
|
@@ -17874,7 +19053,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
17874
19053
|
const executor = new ToolExecutor(tools);
|
|
17875
19054
|
const { makeSessionId: makeSessionId2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
17876
19055
|
const sessionId = makeSessionId2(task);
|
|
17877
|
-
const runId =
|
|
19056
|
+
const runId = randomUUID10();
|
|
17878
19057
|
const worktreeManager = new RunWorktreeManager();
|
|
17879
19058
|
let worktree;
|
|
17880
19059
|
if (useWorktree) {
|
|
@@ -17932,7 +19111,10 @@ function setupRoutes(config2, options = {}) {
|
|
|
17932
19111
|
}
|
|
17933
19112
|
throw error;
|
|
17934
19113
|
}
|
|
19114
|
+
const runTrace = await startSessionTrace(config2, sessionFile, tools, messages);
|
|
19115
|
+
traceUserMessage(runTrace, task, 0, makeTurnId());
|
|
17935
19116
|
const active = {
|
|
19117
|
+
trace: runTrace,
|
|
17936
19118
|
sessionFile,
|
|
17937
19119
|
messages,
|
|
17938
19120
|
executor,
|
|
@@ -18114,11 +19296,16 @@ function setupRoutes(config2, options = {}) {
|
|
|
18114
19296
|
const resolvedFiles = await resolveFiles2(files, cwd);
|
|
18115
19297
|
const userContent = await buildUserMessage2(prompt, resolvedFiles, cwd);
|
|
18116
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);
|
|
18117
19302
|
const active = {
|
|
18118
19303
|
sessionFile,
|
|
18119
19304
|
messages,
|
|
18120
19305
|
executor,
|
|
18121
|
-
sseClients: /* @__PURE__ */ new Set()
|
|
19306
|
+
sseClients: /* @__PURE__ */ new Set(),
|
|
19307
|
+
trace,
|
|
19308
|
+
nextTurnId: firstTurnId
|
|
18122
19309
|
};
|
|
18123
19310
|
activeSessions.set(sessionId, active);
|
|
18124
19311
|
launchAgentTurnForSession(active, config2, allowAll);
|
|
@@ -18146,6 +19333,15 @@ function setupRoutes(config2, options = {}) {
|
|
|
18146
19333
|
const resolvedFiles = await resolveFiles2(files, active.sessionFile.cwd);
|
|
18147
19334
|
const userContent = await buildUserMessage2(prompt, resolvedFiles, active.sessionFile.cwd);
|
|
18148
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);
|
|
18149
19345
|
launchAgentTurnForSession(active, config2, allowAll);
|
|
18150
19346
|
json2(res, 202, { sessionId, status: "started" });
|
|
18151
19347
|
return;
|
|
@@ -18160,6 +19356,7 @@ function setupRoutes(config2, options = {}) {
|
|
|
18160
19356
|
wakeScheduler.dispose();
|
|
18161
19357
|
for (const [, active] of activeSessions) {
|
|
18162
19358
|
active.shuttingDown = true;
|
|
19359
|
+
if (active.trace?.enabled) active.trace.emit("session_end", { reason: "exit" }, null);
|
|
18163
19360
|
active.controller?.abort(new Error("server_shutdown"));
|
|
18164
19361
|
for (const client of active.sseClients) {
|
|
18165
19362
|
client.close();
|
|
@@ -18328,6 +19525,9 @@ async function runAgentTurnForSession(active, config2, allowAll) {
|
|
|
18328
19525
|
}
|
|
18329
19526
|
};
|
|
18330
19527
|
const compaction = sessionFileCompactionTarget(sessionFile);
|
|
19528
|
+
const turnId = active.nextTurnId ?? makeTurnId();
|
|
19529
|
+
active.nextTurnId = void 0;
|
|
19530
|
+
active.lastTurnId = turnId;
|
|
18331
19531
|
try {
|
|
18332
19532
|
if (run && runStore) {
|
|
18333
19533
|
const tools = ALL_TOOLS.filter((tool) => active.allowedTools?.has(tool.name));
|
|
@@ -18349,6 +19549,8 @@ async function runAgentTurnForSession(active, config2, allowAll) {
|
|
|
18349
19549
|
preferPullRequests: config2.preferPullRequests,
|
|
18350
19550
|
compaction,
|
|
18351
19551
|
callbacks,
|
|
19552
|
+
trace: active.trace,
|
|
19553
|
+
turnId,
|
|
18352
19554
|
onIterationEnd: async (updatedMessages) => {
|
|
18353
19555
|
sessionFile.messages = updatedMessages;
|
|
18354
19556
|
sessionFile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -18375,7 +19577,9 @@ async function runAgentTurnForSession(active, config2, allowAll) {
|
|
|
18375
19577
|
allowDirectPush: config2.allowDirectPush,
|
|
18376
19578
|
preferPullRequests: config2.preferPullRequests,
|
|
18377
19579
|
compaction,
|
|
18378
|
-
callbacks
|
|
19580
|
+
callbacks,
|
|
19581
|
+
trace: active.trace,
|
|
19582
|
+
turnId
|
|
18379
19583
|
});
|
|
18380
19584
|
}
|
|
18381
19585
|
sessionFile.messages = messages;
|
|
@@ -18427,6 +19631,8 @@ var init_routes = __esm({
|
|
|
18427
19631
|
"use strict";
|
|
18428
19632
|
init_config();
|
|
18429
19633
|
init_llm_auth();
|
|
19634
|
+
init_trace();
|
|
19635
|
+
init_registry2();
|
|
18430
19636
|
init_loop();
|
|
18431
19637
|
init_system_prompt();
|
|
18432
19638
|
init_executor();
|
|
@@ -18559,8 +19765,8 @@ __export(db_exports, {
|
|
|
18559
19765
|
updateMemoryEmbedding: () => updateMemoryEmbedding
|
|
18560
19766
|
});
|
|
18561
19767
|
import Database6 from "better-sqlite3";
|
|
18562
|
-
import { dirname as
|
|
18563
|
-
import { mkdirSync as
|
|
19768
|
+
import { dirname as dirname18 } from "path";
|
|
19769
|
+
import { mkdirSync as mkdirSync10, statSync as statSync4 } from "fs";
|
|
18564
19770
|
function initSchema(db) {
|
|
18565
19771
|
db.exec(`
|
|
18566
19772
|
CREATE TABLE IF NOT EXISTS memories (
|
|
@@ -18644,7 +19850,7 @@ function openMemoryDb(dbPath) {
|
|
|
18644
19850
|
if (dbInstance) {
|
|
18645
19851
|
dbInstance.close();
|
|
18646
19852
|
}
|
|
18647
|
-
|
|
19853
|
+
mkdirSync10(dirname18(dbPath), { recursive: true });
|
|
18648
19854
|
dbInstance = new Database6(dbPath);
|
|
18649
19855
|
dbInstance.pragma("journal_mode = WAL");
|
|
18650
19856
|
dbInstance.pragma("foreign_keys = ON");
|
|
@@ -19778,12 +20984,12 @@ var init_connection = __esm({
|
|
|
19778
20984
|
});
|
|
19779
20985
|
|
|
19780
20986
|
// src/lsp/protocol.ts
|
|
19781
|
-
import { pathToFileURL as pathToFileURL2, fileURLToPath as
|
|
20987
|
+
import { pathToFileURL as pathToFileURL2, fileURLToPath as fileURLToPath5 } from "url";
|
|
19782
20988
|
function toUri(path) {
|
|
19783
20989
|
return pathToFileURL2(path).href;
|
|
19784
20990
|
}
|
|
19785
20991
|
function fromUri(uri) {
|
|
19786
|
-
return
|
|
20992
|
+
return fileURLToPath5(uri);
|
|
19787
20993
|
}
|
|
19788
20994
|
function symbolKindName(kind) {
|
|
19789
20995
|
const names = {
|
|
@@ -20594,7 +21800,7 @@ function makeLspTools(manager) {
|
|
|
20594
21800
|
var init_lsp = __esm({
|
|
20595
21801
|
"src/tools/lsp.ts"() {
|
|
20596
21802
|
"use strict";
|
|
20597
|
-
|
|
21803
|
+
init_paths2();
|
|
20598
21804
|
init_adapter();
|
|
20599
21805
|
}
|
|
20600
21806
|
});
|
|
@@ -20610,11 +21816,11 @@ __export(requesty_catalog_exports, {
|
|
|
20610
21816
|
mapRequestyModel: () => mapRequestyModel
|
|
20611
21817
|
});
|
|
20612
21818
|
import { readFile as readFile23, writeFile as writeFile13, mkdir as mkdir14 } from "fs/promises";
|
|
20613
|
-
import { homedir as
|
|
20614
|
-
import { join as
|
|
21819
|
+
import { homedir as homedir24 } from "os";
|
|
21820
|
+
import { join as join36, dirname as dirname19 } from "path";
|
|
20615
21821
|
function catalogCachePath2() {
|
|
20616
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
20617
|
-
return
|
|
21822
|
+
const xdg = process.env.XDG_CONFIG_HOME || join36(homedir24(), ".config");
|
|
21823
|
+
return join36(xdg, "kimiflare", "requesty-models.json");
|
|
20618
21824
|
}
|
|
20619
21825
|
function isSafeModelId(id) {
|
|
20620
21826
|
return typeof id === "string" && id.length > 0 && !CONTROL_CHARS.test(id);
|
|
@@ -20685,7 +21891,7 @@ async function readCache4() {
|
|
|
20685
21891
|
async function writeCache4(catalog) {
|
|
20686
21892
|
const path = catalogCachePath2();
|
|
20687
21893
|
try {
|
|
20688
|
-
await mkdir14(
|
|
21894
|
+
await mkdir14(dirname19(path), { recursive: true });
|
|
20689
21895
|
const cache = { version: CACHE_VERSION2, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), ...catalog };
|
|
20690
21896
|
await writeFile13(path, JSON.stringify(cache, null, 2), "utf8");
|
|
20691
21897
|
} catch {
|
|
@@ -20777,9 +21983,9 @@ var init_permissions = __esm({
|
|
|
20777
21983
|
|
|
20778
21984
|
// src/sdk/session.ts
|
|
20779
21985
|
import { resolve as resolve10 } from "path";
|
|
20780
|
-
import { homedir as
|
|
20781
|
-
import { join as
|
|
20782
|
-
import { existsSync as
|
|
21986
|
+
import { homedir as homedir25 } from "os";
|
|
21987
|
+
import { join as join37 } from "path";
|
|
21988
|
+
import { existsSync as existsSync7 } from "fs";
|
|
20783
21989
|
async function createAgentSession(opts2) {
|
|
20784
21990
|
const config2 = await resolveSdkConfig(opts2);
|
|
20785
21991
|
if (usesRequesty(llmAuthFromConfig(config2))) await ensureRequestyCatalog();
|
|
@@ -20795,7 +22001,7 @@ async function createAgentSession(opts2) {
|
|
|
20795
22001
|
let memoryManager = null;
|
|
20796
22002
|
const memoryEnabled = opts2.memoryEnabled ?? config2.memoryEnabled ?? false;
|
|
20797
22003
|
if (memoryEnabled) {
|
|
20798
|
-
const dbPath = config2.memoryDbPath ??
|
|
22004
|
+
const dbPath = config2.memoryDbPath ?? join37(homedir25(), ".local", "share", "kimiflare", "memory.db");
|
|
20799
22005
|
memoryManager = new MemoryManager({
|
|
20800
22006
|
dbPath,
|
|
20801
22007
|
...llmAuthFromConfig(config2),
|
|
@@ -20825,7 +22031,7 @@ async function createAgentSession(opts2) {
|
|
|
20825
22031
|
}
|
|
20826
22032
|
let sessionFile;
|
|
20827
22033
|
if (opts2.sessionId) {
|
|
20828
|
-
const filePath =
|
|
22034
|
+
const filePath = join37(sessionsDir(), `${opts2.sessionId}.json`);
|
|
20829
22035
|
try {
|
|
20830
22036
|
sessionFile = await loadSession(filePath);
|
|
20831
22037
|
} catch {
|
|
@@ -20860,6 +22066,19 @@ async function createAgentSession(opts2) {
|
|
|
20860
22066
|
...sessionFile.messages.filter((m) => m.role !== "system")
|
|
20861
22067
|
];
|
|
20862
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
|
+
});
|
|
20863
22082
|
const session = new InternalSession({
|
|
20864
22083
|
sessionFile,
|
|
20865
22084
|
cwd,
|
|
@@ -20873,7 +22092,8 @@ async function createAgentSession(opts2) {
|
|
|
20873
22092
|
permissionHandler: opts2.permissionHandler,
|
|
20874
22093
|
onKimiMdStale: opts2.onKimiMdStale,
|
|
20875
22094
|
hooks,
|
|
20876
|
-
provider: opts2.provider
|
|
22095
|
+
provider: opts2.provider,
|
|
22096
|
+
trace
|
|
20877
22097
|
});
|
|
20878
22098
|
if (isNewSession) {
|
|
20879
22099
|
await session.save().catch((err) => {
|
|
@@ -20906,6 +22126,8 @@ var init_session = __esm({
|
|
|
20906
22126
|
init_context_budget();
|
|
20907
22127
|
init_config2();
|
|
20908
22128
|
init_permissions();
|
|
22129
|
+
init_trace();
|
|
22130
|
+
init_registry2();
|
|
20909
22131
|
InternalSession = class {
|
|
20910
22132
|
sessionId;
|
|
20911
22133
|
cwd;
|
|
@@ -20921,6 +22143,10 @@ var init_session = __esm({
|
|
|
20921
22143
|
onKimiMdStale;
|
|
20922
22144
|
provider;
|
|
20923
22145
|
hooks;
|
|
22146
|
+
trace;
|
|
22147
|
+
traceCostUsd = 0;
|
|
22148
|
+
/** Turn id of the last turn that ran, for next-message outcome signals. */
|
|
22149
|
+
lastTurnId = null;
|
|
20924
22150
|
listeners = /* @__PURE__ */ new Set();
|
|
20925
22151
|
steerQueue = [];
|
|
20926
22152
|
followUpQueue = [];
|
|
@@ -20961,6 +22187,7 @@ var init_session = __esm({
|
|
|
20961
22187
|
this.onKimiMdStale = opts2.onKimiMdStale;
|
|
20962
22188
|
this.provider = opts2.provider;
|
|
20963
22189
|
this.hooks = opts2.hooks;
|
|
22190
|
+
this.trace = opts2.trace;
|
|
20964
22191
|
this.permissionHandler = opts2.permissionHandler ?? createDefaultPermissionHandler({
|
|
20965
22192
|
mode: this.currentMode,
|
|
20966
22193
|
onRequest: (req) => {
|
|
@@ -21014,6 +22241,22 @@ var init_session = __esm({
|
|
|
21014
22241
|
}
|
|
21015
22242
|
const userMessage = { role: "user", content: userContent };
|
|
21016
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;
|
|
21017
22260
|
this.emit({ type: "message.start", messageId, role: "user" });
|
|
21018
22261
|
this.emit({ type: "message.end", messageId });
|
|
21019
22262
|
this.isStreaming = true;
|
|
@@ -21021,7 +22264,7 @@ var init_session = __esm({
|
|
|
21021
22264
|
this.abortController = new AbortController();
|
|
21022
22265
|
let succeeded = false;
|
|
21023
22266
|
try {
|
|
21024
|
-
await this.runTurn(mode, options?.maxToolIterations);
|
|
22267
|
+
await this.runTurn(mode, options?.maxToolIterations, turnId);
|
|
21025
22268
|
succeeded = true;
|
|
21026
22269
|
this.isStreaming = false;
|
|
21027
22270
|
this.emit({ type: "status", status: "idle" });
|
|
@@ -21116,6 +22359,9 @@ var init_session = __esm({
|
|
|
21116
22359
|
}
|
|
21117
22360
|
dispose() {
|
|
21118
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
|
+
}
|
|
21119
22365
|
this.denyPendingPermissions();
|
|
21120
22366
|
this.abortController?.abort();
|
|
21121
22367
|
this.lspManager?.stopAll().catch(() => {
|
|
@@ -21123,7 +22369,7 @@ var init_session = __esm({
|
|
|
21123
22369
|
this.memoryManager?.close();
|
|
21124
22370
|
this.listeners.clear();
|
|
21125
22371
|
}
|
|
21126
|
-
async runTurn(mode, maxToolIterations) {
|
|
22372
|
+
async runTurn(mode, maxToolIterations, turnId = makeTurnId()) {
|
|
21127
22373
|
const signal = this.abortController.signal;
|
|
21128
22374
|
const coauthor = this.config.coauthor !== false ? {
|
|
21129
22375
|
name: this.config.coauthorName || "autopilot",
|
|
@@ -21196,6 +22442,7 @@ var init_session = __esm({
|
|
|
21196
22442
|
});
|
|
21197
22443
|
},
|
|
21198
22444
|
onUsageFinal: (usage, gatewayMeta) => {
|
|
22445
|
+
if (typeof usage.cost === "number") this.traceCostUsd += usage.cost;
|
|
21199
22446
|
this.usage.totalInputTokens += usage.prompt_tokens;
|
|
21200
22447
|
this.usage.totalOutputTokens += usage.completion_tokens;
|
|
21201
22448
|
this.usage.turnCount += 1;
|
|
@@ -21273,6 +22520,8 @@ var init_session = __esm({
|
|
|
21273
22520
|
compaction: this.compaction,
|
|
21274
22521
|
allowDirectPush: this.config.allowDirectPush,
|
|
21275
22522
|
preferPullRequests: this.config.preferPullRequests,
|
|
22523
|
+
trace: this.trace,
|
|
22524
|
+
turnId,
|
|
21276
22525
|
// Steering lands at the next tool boundary, or before the final answer
|
|
21277
22526
|
// ends the turn (the loop continues so the model can respond to it).
|
|
21278
22527
|
drainPendingInput: () => {
|
|
@@ -21281,6 +22530,9 @@ var init_session = __esm({
|
|
|
21281
22530
|
const messageId = `msg_${this.nextMessageId++}`;
|
|
21282
22531
|
this.emit({ type: "message.start", messageId, role: "user" });
|
|
21283
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
|
+
}
|
|
21284
22536
|
return { role: "user", content: text };
|
|
21285
22537
|
});
|
|
21286
22538
|
},
|
|
@@ -21296,7 +22548,7 @@ var init_session = __esm({
|
|
|
21296
22548
|
}
|
|
21297
22549
|
}
|
|
21298
22550
|
});
|
|
21299
|
-
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)))) {
|
|
21300
22552
|
this.messages[0] = {
|
|
21301
22553
|
role: "system",
|
|
21302
22554
|
content: buildSystemPrompt({
|
|
@@ -22108,6 +23360,8 @@ var init_builtins = __esm({
|
|
|
22108
23360
|
{ name: "checkpoint", argHint: "[label]", description: "Save current point in session", source: "builtin" },
|
|
22109
23361
|
{ name: "checkpoints", description: "List checkpoints in current session", source: "builtin" },
|
|
22110
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" },
|
|
22111
23365
|
{ name: "clear", description: "Clear current conversation", source: "builtin" },
|
|
22112
23366
|
{ name: "fresh", description: "Start a fresh session with a summarized plan or continuation context", source: "builtin" },
|
|
22113
23367
|
{ name: "init", description: "Scan repo and write AGENTS.md", source: "builtin" },
|
|
@@ -22574,7 +23828,9 @@ var init_help_menu = __esm({
|
|
|
22574
23828
|
{ command: "/resume", description: "pick a past conversation" },
|
|
22575
23829
|
{ command: "/compact", description: "summarize old turns to free context" },
|
|
22576
23830
|
{ command: "/clear", description: "clear current conversation" },
|
|
22577
|
-
{ 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 }
|
|
22578
23834
|
]
|
|
22579
23835
|
},
|
|
22580
23836
|
{
|
|
@@ -22733,8 +23989,8 @@ __export(app_helpers_exports, {
|
|
|
22733
23989
|
trackRecentFile: () => trackRecentFile
|
|
22734
23990
|
});
|
|
22735
23991
|
import { execSync as execSync3, spawn as spawn6 } from "child_process";
|
|
22736
|
-
import { existsSync as
|
|
22737
|
-
import { join as
|
|
23992
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
23993
|
+
import { join as join38 } from "path";
|
|
22738
23994
|
import { platform as platform3 } from "os";
|
|
22739
23995
|
function replaceQueuedPrompt(queue2, key, full, display) {
|
|
22740
23996
|
const index = queue2.findIndex((item) => item.key === key);
|
|
@@ -22842,12 +24098,12 @@ function buildFilePickerIgnoreList(cwd) {
|
|
|
22842
24098
|
];
|
|
22843
24099
|
const gitignorePatterns = [];
|
|
22844
24100
|
try {
|
|
22845
|
-
const gitignorePath =
|
|
24101
|
+
const gitignorePath = join38(cwd, ".gitignore");
|
|
22846
24102
|
const stats = statSync5(gitignorePath);
|
|
22847
24103
|
if (stats.size > MAX_GITIGNORE_SIZE) {
|
|
22848
24104
|
return hardcoded;
|
|
22849
24105
|
}
|
|
22850
|
-
const content =
|
|
24106
|
+
const content = readFileSync7(gitignorePath, "utf-8");
|
|
22851
24107
|
for (const line of content.split(/\r?\n/)) {
|
|
22852
24108
|
const trimmed = line.trim();
|
|
22853
24109
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -22981,7 +24237,7 @@ function findImagePaths(text) {
|
|
|
22981
24237
|
let match;
|
|
22982
24238
|
while ((match = quotedRegex.exec(text)) !== null) {
|
|
22983
24239
|
const path = match[1] ?? match[2];
|
|
22984
|
-
if (path && isImagePath(path) &&
|
|
24240
|
+
if (path && isImagePath(path) && existsSync8(path)) {
|
|
22985
24241
|
paths.push(path);
|
|
22986
24242
|
}
|
|
22987
24243
|
}
|
|
@@ -22990,7 +24246,7 @@ function findImagePaths(text) {
|
|
|
22990
24246
|
const processed = remaining.replace(/\\ /g, ESCAPED_SPACE);
|
|
22991
24247
|
for (const token of processed.split(/\s+/)) {
|
|
22992
24248
|
const clean = token.replace(new RegExp(ESCAPED_SPACE, "g"), " ").replace(/^["']|["',;:!?]$/g, "").replace(/[.,;:!?]$/, "");
|
|
22993
|
-
if (clean && isImagePath(clean) &&
|
|
24249
|
+
if (clean && isImagePath(clean) && existsSync8(clean) && !paths.includes(clean)) {
|
|
22994
24250
|
paths.push(clean);
|
|
22995
24251
|
}
|
|
22996
24252
|
}
|
|
@@ -23020,9 +24276,9 @@ var init_app_helpers = __esm({
|
|
|
23020
24276
|
|
|
23021
24277
|
// src/ui/bang-command.ts
|
|
23022
24278
|
import { spawn as spawn7 } from "child_process";
|
|
23023
|
-
import { mkdtemp as mkdtemp2, readFile as readFile24, rm as
|
|
24279
|
+
import { mkdtemp as mkdtemp2, readFile as readFile24, rm as rm4 } from "fs/promises";
|
|
23024
24280
|
import { tmpdir as tmpdir4, platform as platform4 } from "os";
|
|
23025
|
-
import { join as
|
|
24281
|
+
import { join as join39 } from "path";
|
|
23026
24282
|
function registerInkInstance(instance) {
|
|
23027
24283
|
inkInstance = instance;
|
|
23028
24284
|
}
|
|
@@ -23125,8 +24381,8 @@ async function runBangCommand(command, cwd) {
|
|
|
23125
24381
|
const origErr = stderr.write;
|
|
23126
24382
|
const readable = stdin.listeners("readable");
|
|
23127
24383
|
const wasRaw = stdin.isTTY ? stdin.isRaw : false;
|
|
23128
|
-
const logDir2 = isWindows ? null : await mkdtemp2(
|
|
23129
|
-
const logPath = logDir2 ?
|
|
24384
|
+
const logDir2 = isWindows ? null : await mkdtemp2(join39(tmpdir4(), "autopilot-bang-"));
|
|
24385
|
+
const logPath = logDir2 ? join39(logDir2, "out.log") : null;
|
|
23130
24386
|
const viaScript = logPath ? scriptInvocation(command, shell, logPath) : null;
|
|
23131
24387
|
inkInstance?.clear();
|
|
23132
24388
|
origOut.call(stdout, `
|
|
@@ -23170,7 +24426,7 @@ $ ${command}
|
|
|
23170
24426
|
if (captured && logPath) {
|
|
23171
24427
|
output = cleanTerminalOutput(await readFile24(logPath, "utf8").catch(() => ""));
|
|
23172
24428
|
}
|
|
23173
|
-
if (logDir2) await
|
|
24429
|
+
if (logDir2) await rm4(logDir2, { recursive: true, force: true }).catch(() => {
|
|
23174
24430
|
});
|
|
23175
24431
|
return { command, exitCode: result2.code, signal: result2.signal, output };
|
|
23176
24432
|
}
|
|
@@ -25357,9 +26613,9 @@ __export(camouflage_view_exports, {
|
|
|
25357
26613
|
validateCommandName: () => validateCommandName
|
|
25358
26614
|
});
|
|
25359
26615
|
import { execFileSync as execFileSync2, spawn as spawn9 } from "child_process";
|
|
25360
|
-
import { readdir as
|
|
25361
|
-
import { homedir as
|
|
25362
|
-
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";
|
|
25363
26619
|
async function loadSdk() {
|
|
25364
26620
|
const sdk = await import("camouflage-tui").catch((err) => {
|
|
25365
26621
|
throw new CamouflageUnavailable(`camouflage-tui is not installed (${err.message})`);
|
|
@@ -25538,10 +26794,10 @@ function truncateText(s, max) {
|
|
|
25538
26794
|
async function listMentionDir(cwd, query) {
|
|
25539
26795
|
const exact = query === "~" || query === "." || query === ".." || query.endsWith("/..");
|
|
25540
26796
|
const dir = exact ? `${query}/` : query.slice(0, query.lastIndexOf("/") + 1);
|
|
25541
|
-
const expanded = dir.startsWith("~") ?
|
|
26797
|
+
const expanded = dir.startsWith("~") ? join40(homedir26(), dir.slice(1)) : dir;
|
|
25542
26798
|
const target = resolve11(cwd, expanded || ".");
|
|
25543
26799
|
try {
|
|
25544
|
-
const items = await
|
|
26800
|
+
const items = await readdir6(target, { withFileTypes: true });
|
|
25545
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 ? "/" : ""}`);
|
|
25546
26802
|
return { dir, entries };
|
|
25547
26803
|
} catch {
|
|
@@ -25619,7 +26875,7 @@ function shortModel(id) {
|
|
|
25619
26875
|
return id.includes("/") ? id.slice(id.indexOf("/") + 1) : id;
|
|
25620
26876
|
}
|
|
25621
26877
|
function shortPath(p) {
|
|
25622
|
-
const home =
|
|
26878
|
+
const home = homedir26();
|
|
25623
26879
|
return p === home ? "~" : p.startsWith(home + "/") ? `~${p.slice(home.length)}` : p;
|
|
25624
26880
|
}
|
|
25625
26881
|
function gitBranch(cwd) {
|
|
@@ -27626,7 +28882,7 @@ var init_tool_view = __esm({
|
|
|
27626
28882
|
"use strict";
|
|
27627
28883
|
init_spinner();
|
|
27628
28884
|
init_diff_view();
|
|
27629
|
-
|
|
28885
|
+
init_paths2();
|
|
27630
28886
|
init_theme_context();
|
|
27631
28887
|
init_narrator();
|
|
27632
28888
|
ToolView = React7.memo(function ToolView2({ evt, verbose, isRepeated, intentTier }) {
|
|
@@ -28373,7 +29629,7 @@ function PermissionModal({ tool, args, onDecide, onFeedback }) {
|
|
|
28373
29629
|
);
|
|
28374
29630
|
const handleFeedbackSubmit = useCallback(
|
|
28375
29631
|
(text) => {
|
|
28376
|
-
onDecide(DENY);
|
|
29632
|
+
onDecide(text.trim() ? { ...DENY, feedback: text.trim() } : DENY);
|
|
28377
29633
|
if (text.trim()) {
|
|
28378
29634
|
onFeedback?.(text);
|
|
28379
29635
|
}
|
|
@@ -28889,7 +30145,7 @@ var init_queue_plan_picker = __esm({
|
|
|
28889
30145
|
// src/agent/jev-context.ts
|
|
28890
30146
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
28891
30147
|
import { readFile as readFile25 } from "fs/promises";
|
|
28892
|
-
import { basename as basename6, join as
|
|
30148
|
+
import { basename as basename6, join as join41, resolve as resolve12 } from "path";
|
|
28893
30149
|
function messageText(message2) {
|
|
28894
30150
|
if (typeof message2.content === "string") return message2.content.trim();
|
|
28895
30151
|
if (!Array.isArray(message2.content)) return "";
|
|
@@ -28911,7 +30167,7 @@ function projectRoot(cwd) {
|
|
|
28911
30167
|
}
|
|
28912
30168
|
async function readPackageMetadata(root) {
|
|
28913
30169
|
try {
|
|
28914
|
-
const parsed = JSON.parse(await readFile25(
|
|
30170
|
+
const parsed = JSON.parse(await readFile25(join41(root, "package.json"), "utf8"));
|
|
28915
30171
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
28916
30172
|
const pkg = parsed;
|
|
28917
30173
|
const metadata = {};
|
|
@@ -28928,7 +30184,7 @@ async function readPackageMetadata(root) {
|
|
|
28928
30184
|
async function readLicenseEvidence(root) {
|
|
28929
30185
|
for (const name of ["LICENSE", "LICENSE.md", "LICENSE.txt", "LICENCE", "COPYING"]) {
|
|
28930
30186
|
try {
|
|
28931
|
-
const content = (await readFile25(
|
|
30187
|
+
const content = (await readFile25(join41(root, name), "utf8")).trim();
|
|
28932
30188
|
if (content) return { source: `project license (${name})`, content: content.slice(0, 700) };
|
|
28933
30189
|
} catch {
|
|
28934
30190
|
}
|
|
@@ -30010,8 +31266,8 @@ var init_frontmatter = __esm({
|
|
|
30010
31266
|
});
|
|
30011
31267
|
|
|
30012
31268
|
// src/skills/loader.ts
|
|
30013
|
-
import { readFile as readFile26, readdir as
|
|
30014
|
-
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";
|
|
30015
31271
|
function normalizeManifest(raw, filePath) {
|
|
30016
31272
|
const name = typeof raw.name === "string" ? raw.name : "";
|
|
30017
31273
|
const description = typeof raw.description === "string" ? raw.description : "";
|
|
@@ -30044,11 +31300,11 @@ async function loadSkillFile(filePath) {
|
|
|
30044
31300
|
}
|
|
30045
31301
|
async function loadSkillsFromDir(dirPath) {
|
|
30046
31302
|
try {
|
|
30047
|
-
const entries = await
|
|
31303
|
+
const entries = await readdir7(dirPath);
|
|
30048
31304
|
const files = [];
|
|
30049
31305
|
for (const entry of entries) {
|
|
30050
|
-
const full =
|
|
30051
|
-
const s = await
|
|
31306
|
+
const full = join42(dirPath, entry);
|
|
31307
|
+
const s = await stat8(full);
|
|
30052
31308
|
if (s.isFile() && extname(entry) === ".md") {
|
|
30053
31309
|
files.push(full);
|
|
30054
31310
|
}
|
|
@@ -30076,11 +31332,11 @@ var init_loader = __esm({
|
|
|
30076
31332
|
|
|
30077
31333
|
// src/skills/manager.ts
|
|
30078
31334
|
import { mkdir as mkdir15, writeFile as writeFile14, unlink as unlink4, readFile as readFile27 } from "fs/promises";
|
|
30079
|
-
import { join as
|
|
31335
|
+
import { join as join43 } from "path";
|
|
30080
31336
|
function getSkillDirs(cwd) {
|
|
30081
31337
|
return {
|
|
30082
|
-
projectDir:
|
|
30083
|
-
globalDir:
|
|
31338
|
+
projectDir: join43(cwd, ".kimiflare", "skills"),
|
|
31339
|
+
globalDir: join43(process.env.HOME ?? "", ".config", "kimiflare", "skills")
|
|
30084
31340
|
};
|
|
30085
31341
|
}
|
|
30086
31342
|
async function listAllSkills(cwd) {
|
|
@@ -30094,7 +31350,7 @@ async function listAllSkills(cwd) {
|
|
|
30094
31350
|
async function createSkill(opts2) {
|
|
30095
31351
|
const dirs = getSkillDirs(opts2.cwd);
|
|
30096
31352
|
const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
|
|
30097
|
-
const filepath =
|
|
31353
|
+
const filepath = join43(dir, `${opts2.name}.md`);
|
|
30098
31354
|
const frontmatter = {
|
|
30099
31355
|
name: opts2.name,
|
|
30100
31356
|
enabled: true,
|
|
@@ -30403,14 +31659,14 @@ __export(tui_report_exports, {
|
|
|
30403
31659
|
getCategoryReportText: () => getCategoryReportText
|
|
30404
31660
|
});
|
|
30405
31661
|
import { readFile as readFile28 } from "fs/promises";
|
|
30406
|
-
import { join as
|
|
30407
|
-
import { homedir as
|
|
31662
|
+
import { join as join44 } from "path";
|
|
31663
|
+
import { homedir as homedir27 } from "os";
|
|
30408
31664
|
function usageDir3() {
|
|
30409
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
30410
|
-
return
|
|
31665
|
+
const xdg = process.env.XDG_DATA_HOME || join44(homedir27(), ".local", "share");
|
|
31666
|
+
return join44(xdg, "kimiflare");
|
|
30411
31667
|
}
|
|
30412
31668
|
function usagePath4() {
|
|
30413
|
-
return
|
|
31669
|
+
return join44(usageDir3(), "usage.json");
|
|
30414
31670
|
}
|
|
30415
31671
|
function today3() {
|
|
30416
31672
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -30479,7 +31735,7 @@ var init_tui_report = __esm({
|
|
|
30479
31735
|
});
|
|
30480
31736
|
|
|
30481
31737
|
// src/ui/slash-commands.ts
|
|
30482
|
-
import { join as
|
|
31738
|
+
import { join as join45 } from "path";
|
|
30483
31739
|
import QRCode from "qrcode";
|
|
30484
31740
|
function executeFreshStart(ctx, planText, overrideMode, opts2 = {}) {
|
|
30485
31741
|
const oldSessionId = ctx.sessionIdRef.current;
|
|
@@ -30570,6 +31826,28 @@ function modesGate(ctx) {
|
|
|
30570
31826
|
]);
|
|
30571
31827
|
return false;
|
|
30572
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
|
+
}
|
|
30573
31851
|
function dispatchSlashCommand(ctx, cmd) {
|
|
30574
31852
|
const raw = cmd.trim();
|
|
30575
31853
|
const [head, ...rest] = raw.split(/\s+/);
|
|
@@ -30579,7 +31857,7 @@ function dispatchSlashCommand(ctx, cmd) {
|
|
|
30579
31857
|
if (!handler) return false;
|
|
30580
31858
|
return handler(ctx, rest, arg);
|
|
30581
31859
|
}
|
|
30582
|
-
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;
|
|
30583
31861
|
var init_slash_commands = __esm({
|
|
30584
31862
|
"src/ui/slash-commands.ts"() {
|
|
30585
31863
|
"use strict";
|
|
@@ -30602,7 +31880,7 @@ var init_slash_commands = __esm({
|
|
|
30602
31880
|
init_executor();
|
|
30603
31881
|
init_recommended();
|
|
30604
31882
|
init_settings();
|
|
30605
|
-
|
|
31883
|
+
init_types2();
|
|
30606
31884
|
init_update_check();
|
|
30607
31885
|
init_version();
|
|
30608
31886
|
init_app_helpers();
|
|
@@ -31389,7 +32667,7 @@ ${lines.join("\n")}` }]);
|
|
|
31389
32667
|
void (async () => {
|
|
31390
32668
|
try {
|
|
31391
32669
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
31392
|
-
const file = await loadSession(
|
|
32670
|
+
const file = await loadSession(join45(sessionsDir3(), `${currentId}.json`));
|
|
31393
32671
|
const cps = file.checkpoints ?? [];
|
|
31394
32672
|
if (cps.length === 0) {
|
|
31395
32673
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "no checkpoints in this session" }]);
|
|
@@ -31434,7 +32712,7 @@ ${lines.join("\n")}` }]);
|
|
|
31434
32712
|
try {
|
|
31435
32713
|
ctx.ensureSessionId();
|
|
31436
32714
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
31437
|
-
const filePath =
|
|
32715
|
+
const filePath = join45(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
|
|
31438
32716
|
await addCheckpoint(filePath, cp);
|
|
31439
32717
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `checkpoint saved: "${label}"` }]);
|
|
31440
32718
|
} catch (e) {
|
|
@@ -32025,6 +33303,8 @@ project: ${projectSettingsPath(cwd)}`
|
|
|
32025
33303
|
});
|
|
32026
33304
|
return true;
|
|
32027
33305
|
};
|
|
33306
|
+
handleGood = (ctx, rest) => rateLastTurn(ctx, "good", rest.join(" ").trim());
|
|
33307
|
+
handleBad = (ctx, rest) => rateLastTurn(ctx, "bad", rest.join(" ").trim());
|
|
32028
33308
|
handlers = {
|
|
32029
33309
|
"/exit": handleExit,
|
|
32030
33310
|
"/clear": handleClear,
|
|
@@ -32048,6 +33328,8 @@ project: ${projectSettingsPath(cwd)}`
|
|
|
32048
33328
|
"/resume": handleResume,
|
|
32049
33329
|
"/checkpoint": handleCheckpoint,
|
|
32050
33330
|
"/compact": handleCompact,
|
|
33331
|
+
"/good": handleGood,
|
|
33332
|
+
"/bad": handleBad,
|
|
32051
33333
|
"/init": handleInit,
|
|
32052
33334
|
"/update": handleUpdate,
|
|
32053
33335
|
"/mcp": handleMcp,
|
|
@@ -32379,7 +33661,7 @@ __export(openrouter_oauth_exports, {
|
|
|
32379
33661
|
pkceChallenge: () => pkceChallenge,
|
|
32380
33662
|
startLoopbackListener: () => startLoopbackListener
|
|
32381
33663
|
});
|
|
32382
|
-
import { createHash as
|
|
33664
|
+
import { createHash as createHash5, randomBytes as randomBytes3 } from "crypto";
|
|
32383
33665
|
import { createServer as createServer3 } from "http";
|
|
32384
33666
|
import { hostname } from "os";
|
|
32385
33667
|
function createPkce() {
|
|
@@ -32387,7 +33669,7 @@ function createPkce() {
|
|
|
32387
33669
|
return { verifier, challenge: pkceChallenge(verifier) };
|
|
32388
33670
|
}
|
|
32389
33671
|
function pkceChallenge(verifier) {
|
|
32390
|
-
return
|
|
33672
|
+
return createHash5("sha256").update(verifier).digest("base64url");
|
|
32391
33673
|
}
|
|
32392
33674
|
function openRouterAuthPageUrl() {
|
|
32393
33675
|
return (process.env.OPENROUTER_AUTH_URL?.trim() || "https://openrouter.ai/auth").replace(/\/+$/, "");
|
|
@@ -32899,14 +34181,14 @@ var init_frontmatter2 = __esm({
|
|
|
32899
34181
|
|
|
32900
34182
|
// src/commands/loader.ts
|
|
32901
34183
|
import { open, realpath as realpath6 } from "fs/promises";
|
|
32902
|
-
import { homedir as
|
|
32903
|
-
import { join as
|
|
34184
|
+
import { homedir as homedir28 } from "os";
|
|
34185
|
+
import { join as join46, relative as relative10, sep as sep6 } from "path";
|
|
32904
34186
|
function projectCommandsDir(cwd = process.cwd()) {
|
|
32905
|
-
return
|
|
34187
|
+
return join46(cwd, ".kimiflare", "commands");
|
|
32906
34188
|
}
|
|
32907
34189
|
function globalCommandsDir() {
|
|
32908
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
32909
|
-
return
|
|
34190
|
+
const xdg = process.env.XDG_CONFIG_HOME || join46(homedir28(), ".config");
|
|
34191
|
+
return join46(xdg, "kimiflare", "commands");
|
|
32910
34192
|
}
|
|
32911
34193
|
async function loadCustomCommands(cwd = process.cwd()) {
|
|
32912
34194
|
const warnings = [];
|
|
@@ -33050,7 +34332,7 @@ var init_loader2 = __esm({
|
|
|
33050
34332
|
init_mode();
|
|
33051
34333
|
init_glob();
|
|
33052
34334
|
init_config();
|
|
33053
|
-
|
|
34335
|
+
init_paths2();
|
|
33054
34336
|
init_frontmatter2();
|
|
33055
34337
|
MAX_COMMAND_FILE_BYTES = 256 * 1024;
|
|
33056
34338
|
}
|
|
@@ -33060,7 +34342,7 @@ var init_loader2 = __esm({
|
|
|
33060
34342
|
import { exec } from "child_process";
|
|
33061
34343
|
import { open as open2, realpath as realpath7 } from "fs/promises";
|
|
33062
34344
|
import { isAbsolute as isAbsolute6, relative as relative11, resolve as resolvePathJoin, basename as pathBasename } from "path";
|
|
33063
|
-
import { promisify as
|
|
34345
|
+
import { promisify as promisify7 } from "util";
|
|
33064
34346
|
function tokenizeArgs(s) {
|
|
33065
34347
|
return [...s.matchAll(ARG_TOKEN_RE)].map((match) => {
|
|
33066
34348
|
const token = match[0];
|
|
@@ -33212,8 +34494,8 @@ var execAsync, ARG_TOKEN_RE, POSITIONAL_RE, HAS_POSITIONAL, SHELL_RE, FILE_RE, D
|
|
|
33212
34494
|
var init_renderer2 = __esm({
|
|
33213
34495
|
"src/commands/renderer.ts"() {
|
|
33214
34496
|
"use strict";
|
|
33215
|
-
|
|
33216
|
-
execAsync =
|
|
34497
|
+
init_paths2();
|
|
34498
|
+
execAsync = promisify7(exec);
|
|
33217
34499
|
ARG_TOKEN_RE = /(?:"[^"]*"|'[^']*'|[^\s"']+)/g;
|
|
33218
34500
|
POSITIONAL_RE = /\$(\d+)/g;
|
|
33219
34501
|
HAS_POSITIONAL = /\$\d+/;
|
|
@@ -33288,11 +34570,11 @@ var init_wcag = __esm({
|
|
|
33288
34570
|
});
|
|
33289
34571
|
|
|
33290
34572
|
// src/ui/theme-loader.ts
|
|
33291
|
-
import { readFile as readFile29, readdir as
|
|
33292
|
-
import { join as
|
|
33293
|
-
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";
|
|
33294
34576
|
function projectThemesDir(cwd = process.cwd()) {
|
|
33295
|
-
return
|
|
34577
|
+
return join47(cwd, ".kimiflare", "themes");
|
|
33296
34578
|
}
|
|
33297
34579
|
function isHexColor(c) {
|
|
33298
34580
|
return /^#[0-9a-fA-F]{6}$/.test(c);
|
|
@@ -33376,12 +34658,12 @@ async function loadThemesFromDir(dir, source) {
|
|
|
33376
34658
|
const errors = [];
|
|
33377
34659
|
let files;
|
|
33378
34660
|
try {
|
|
33379
|
-
files = await
|
|
34661
|
+
files = await readdir8(dir);
|
|
33380
34662
|
} catch {
|
|
33381
34663
|
return { themes, errors };
|
|
33382
34664
|
}
|
|
33383
34665
|
for (const file of files.filter((f) => f.endsWith(".json"))) {
|
|
33384
|
-
const path =
|
|
34666
|
+
const path = join47(dir, file);
|
|
33385
34667
|
let raw;
|
|
33386
34668
|
try {
|
|
33387
34669
|
raw = await readFile29(path, "utf-8");
|
|
@@ -33530,8 +34812,8 @@ var init_theme_loader = __esm({
|
|
|
33530
34812
|
"use strict";
|
|
33531
34813
|
init_wcag();
|
|
33532
34814
|
init_theme();
|
|
33533
|
-
USER_THEMES_DIR =
|
|
33534
|
-
process.env.XDG_CONFIG_HOME ||
|
|
34815
|
+
USER_THEMES_DIR = join47(
|
|
34816
|
+
process.env.XDG_CONFIG_HOME || join47(homedir29(), ".config"),
|
|
33535
34817
|
"kimiflare",
|
|
33536
34818
|
"themes"
|
|
33537
34819
|
);
|
|
@@ -35689,6 +36971,61 @@ function useSessionManager(deps) {
|
|
|
35689
36971
|
setLogSessionId(sessionIdRef.current);
|
|
35690
36972
|
return sessionIdRef.current;
|
|
35691
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]);
|
|
35692
37029
|
const saveSessionSafe = useCallback6(async () => {
|
|
35693
37030
|
const d = depsRef.current;
|
|
35694
37031
|
if (!d.cfg) return;
|
|
@@ -35727,11 +37064,18 @@ function useSessionManager(deps) {
|
|
|
35727
37064
|
setResumeProgress(0);
|
|
35728
37065
|
setResumeStage("Loading session file\u2026");
|
|
35729
37066
|
startSmoothProgress(0, 10, 400);
|
|
35730
|
-
const
|
|
37067
|
+
const loaded = checkpointId ? await loadSessionFromCheckpoint(filePath, checkpointId) : null;
|
|
37068
|
+
const file = loaded ? loaded.file : await loadSession(filePath);
|
|
35731
37069
|
setResumeProgress(10);
|
|
35732
37070
|
setResumeStage("Restoring artifact store\u2026");
|
|
35733
37071
|
startSmoothProgress(10, 20, 400);
|
|
35734
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 = [];
|
|
35735
37079
|
sessionIdRef.current = file.id;
|
|
35736
37080
|
setLogSessionId(file.id);
|
|
35737
37081
|
sessionCreatedAtRef.current = file.createdAt;
|
|
@@ -35907,6 +37251,9 @@ ${summary}`
|
|
|
35907
37251
|
[checkpointSession, doResumeSession]
|
|
35908
37252
|
);
|
|
35909
37253
|
const resetSession = useCallback6(() => {
|
|
37254
|
+
traceReverted("clear", turnHistoryRef.current.slice(-2));
|
|
37255
|
+
turnHistoryRef.current = [];
|
|
37256
|
+
endTrace("clear");
|
|
35910
37257
|
sessionIdRef.current = null;
|
|
35911
37258
|
setLogSessionId(null);
|
|
35912
37259
|
sessionCreatedAtRef.current = null;
|
|
@@ -35914,7 +37261,7 @@ ${summary}`
|
|
|
35914
37261
|
const d = depsRef.current;
|
|
35915
37262
|
d.sessionStateRef.current = emptySessionState();
|
|
35916
37263
|
d.artifactStoreRef.current = new ArtifactStore();
|
|
35917
|
-
}, []);
|
|
37264
|
+
}, [endTrace, traceReverted]);
|
|
35918
37265
|
return {
|
|
35919
37266
|
sessionIdRef,
|
|
35920
37267
|
sessionCreatedAtRef,
|
|
@@ -35936,7 +37283,13 @@ ${summary}`
|
|
|
35936
37283
|
doResumeSession,
|
|
35937
37284
|
handleResumePick,
|
|
35938
37285
|
handleCheckpointPick,
|
|
35939
|
-
resetSession
|
|
37286
|
+
resetSession,
|
|
37287
|
+
traceRef,
|
|
37288
|
+
ensureTrace,
|
|
37289
|
+
ensureTraceStarted,
|
|
37290
|
+
endTrace,
|
|
37291
|
+
turnHistoryRef,
|
|
37292
|
+
noteTurnEnded
|
|
35940
37293
|
};
|
|
35941
37294
|
}
|
|
35942
37295
|
var init_use_session_manager = __esm({
|
|
@@ -35948,6 +37301,7 @@ var init_use_session_manager = __esm({
|
|
|
35948
37301
|
init_recall_inject();
|
|
35949
37302
|
init_usage_tracker();
|
|
35950
37303
|
init_log_sink();
|
|
37304
|
+
init_trace();
|
|
35951
37305
|
init_app_helpers();
|
|
35952
37306
|
init_continuation_summary();
|
|
35953
37307
|
init_config();
|
|
@@ -35986,9 +37340,9 @@ var init_resume_progress = __esm({
|
|
|
35986
37340
|
});
|
|
35987
37341
|
|
|
35988
37342
|
// src/agent/supervisor.ts
|
|
35989
|
-
import { readdir as
|
|
35990
|
-
import { createHash as
|
|
35991
|
-
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";
|
|
35992
37346
|
var TurnSupervisor;
|
|
35993
37347
|
var init_supervisor = __esm({
|
|
35994
37348
|
"src/agent/supervisor.ts"() {
|
|
@@ -36375,13 +37729,13 @@ var init_input_handlers = __esm({
|
|
|
36375
37729
|
});
|
|
36376
37730
|
|
|
36377
37731
|
// src/init/context-generator.ts
|
|
36378
|
-
import { existsSync as
|
|
36379
|
-
import { join as
|
|
37732
|
+
import { existsSync as existsSync9, statSync as statSync6 } from "fs";
|
|
37733
|
+
import { join as join49 } from "path";
|
|
36380
37734
|
function detectFlavor(cwd) {
|
|
36381
37735
|
for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
|
|
36382
37736
|
if (flavor === "generic") continue;
|
|
36383
37737
|
for (const sig of signatures) {
|
|
36384
|
-
const path =
|
|
37738
|
+
const path = join49(cwd, sig);
|
|
36385
37739
|
if (sig.includes("*")) {
|
|
36386
37740
|
try {
|
|
36387
37741
|
const parts = sig.split("*");
|
|
@@ -36393,7 +37747,7 @@ function detectFlavor(cwd) {
|
|
|
36393
37747
|
}
|
|
36394
37748
|
} catch {
|
|
36395
37749
|
}
|
|
36396
|
-
} else if (
|
|
37750
|
+
} else if (existsSync9(path)) {
|
|
36397
37751
|
return flavor;
|
|
36398
37752
|
}
|
|
36399
37753
|
}
|
|
@@ -36402,14 +37756,14 @@ function detectFlavor(cwd) {
|
|
|
36402
37756
|
}
|
|
36403
37757
|
function findFile(cwd, candidates) {
|
|
36404
37758
|
for (const c of candidates) {
|
|
36405
|
-
if (
|
|
37759
|
+
if (existsSync9(join49(cwd, c))) return c;
|
|
36406
37760
|
}
|
|
36407
37761
|
return null;
|
|
36408
37762
|
}
|
|
36409
37763
|
function findSourceRoots(cwd) {
|
|
36410
37764
|
const roots = [];
|
|
36411
37765
|
for (const r of SOURCE_ROOT_CANDIDATES) {
|
|
36412
|
-
const p =
|
|
37766
|
+
const p = join49(cwd, r);
|
|
36413
37767
|
try {
|
|
36414
37768
|
const s = statSync6(p);
|
|
36415
37769
|
if (s.isDirectory()) roots.push(r);
|
|
@@ -36420,9 +37774,9 @@ function findSourceRoots(cwd) {
|
|
|
36420
37774
|
}
|
|
36421
37775
|
function findCiConfig(cwd) {
|
|
36422
37776
|
for (const c of CI_PATHS) {
|
|
36423
|
-
if (
|
|
37777
|
+
if (existsSync9(join49(cwd, c))) {
|
|
36424
37778
|
try {
|
|
36425
|
-
const s = statSync6(
|
|
37779
|
+
const s = statSync6(join49(cwd, c));
|
|
36426
37780
|
return s.isDirectory() ? c : c;
|
|
36427
37781
|
} catch {
|
|
36428
37782
|
}
|
|
@@ -36559,7 +37913,7 @@ function analyzeProject(cwd) {
|
|
|
36559
37913
|
ciConfig: findCiConfig(cwd),
|
|
36560
37914
|
readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
|
|
36561
37915
|
sourceRoots: findSourceRoots(cwd),
|
|
36562
|
-
hasGit:
|
|
37916
|
+
hasGit: existsSync9(join49(cwd, ".git"))
|
|
36563
37917
|
};
|
|
36564
37918
|
}
|
|
36565
37919
|
function bashDiscoveryCommands(profile) {
|
|
@@ -36724,7 +38078,7 @@ Aim for 100\u2013200 lines total. Use markdown tables where they save space.
|
|
|
36724
38078
|
}
|
|
36725
38079
|
function buildInitPrompt(cwd) {
|
|
36726
38080
|
const existingName = ["AGENTS.md", "KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
|
|
36727
|
-
(n) =>
|
|
38081
|
+
(n) => existsSync9(join49(cwd, n))
|
|
36728
38082
|
);
|
|
36729
38083
|
const isRefresh = existingName !== void 0;
|
|
36730
38084
|
const targetFilename = existingName ?? "AGENTS.md";
|
|
@@ -36807,8 +38161,8 @@ var init_context_generator = __esm({
|
|
|
36807
38161
|
});
|
|
36808
38162
|
|
|
36809
38163
|
// src/init/run-init.ts
|
|
36810
|
-
import { existsSync as
|
|
36811
|
-
import { join as
|
|
38164
|
+
import { existsSync as existsSync10 } from "fs";
|
|
38165
|
+
import { join as join50 } from "path";
|
|
36812
38166
|
async function runInit(deps) {
|
|
36813
38167
|
const {
|
|
36814
38168
|
cfg,
|
|
@@ -37006,7 +38360,7 @@ async function runInit(deps) {
|
|
|
37006
38360
|
}
|
|
37007
38361
|
}
|
|
37008
38362
|
});
|
|
37009
|
-
if (
|
|
38363
|
+
if (existsSync10(join50(cwd, targetFilename))) {
|
|
37010
38364
|
if (cacheStableRef.current) {
|
|
37011
38365
|
messagesRef.current[1] = {
|
|
37012
38366
|
role: "system",
|
|
@@ -37098,11 +38452,11 @@ var init_run_init = __esm({
|
|
|
37098
38452
|
});
|
|
37099
38453
|
|
|
37100
38454
|
// src/skills/discovery.ts
|
|
37101
|
-
import { readdir as
|
|
37102
|
-
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";
|
|
37103
38457
|
async function dirExists(path) {
|
|
37104
38458
|
try {
|
|
37105
|
-
const s = await
|
|
38459
|
+
const s = await stat10(path);
|
|
37106
38460
|
return s.isDirectory();
|
|
37107
38461
|
} catch {
|
|
37108
38462
|
return false;
|
|
@@ -37110,7 +38464,7 @@ async function dirExists(path) {
|
|
|
37110
38464
|
}
|
|
37111
38465
|
async function fileExists(path) {
|
|
37112
38466
|
try {
|
|
37113
|
-
const s = await
|
|
38467
|
+
const s = await stat10(path);
|
|
37114
38468
|
return s.isFile();
|
|
37115
38469
|
} catch {
|
|
37116
38470
|
return false;
|
|
@@ -37118,20 +38472,20 @@ async function fileExists(path) {
|
|
|
37118
38472
|
}
|
|
37119
38473
|
async function scanSkillDir(dirPath, source) {
|
|
37120
38474
|
if (!await dirExists(dirPath)) return [];
|
|
37121
|
-
const entries = await
|
|
38475
|
+
const entries = await readdir10(dirPath, { withFileTypes: true });
|
|
37122
38476
|
const files = [];
|
|
37123
38477
|
for (const entry of entries) {
|
|
37124
38478
|
if (!entry.isFile()) continue;
|
|
37125
38479
|
if (!SKILL_EXTENSIONS.has(extname2(entry.name))) continue;
|
|
37126
|
-
files.push({ filePath:
|
|
38480
|
+
files.push({ filePath: join51(dirPath, entry.name), source });
|
|
37127
38481
|
}
|
|
37128
38482
|
return files;
|
|
37129
38483
|
}
|
|
37130
38484
|
async function discoverSkills(cwd) {
|
|
37131
|
-
const agentsSkills = await scanSkillDir(
|
|
37132
|
-
const agentsMd = await fileExists(
|
|
37133
|
-
const githubSkills = await scanSkillDir(
|
|
37134
|
-
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");
|
|
37135
38489
|
const ordered = [...agentsSkills, ...agentsMd, ...githubSkills, ...kimiflareSkills];
|
|
37136
38490
|
return ordered;
|
|
37137
38491
|
}
|
|
@@ -37148,9 +38502,9 @@ var init_discovery = __esm({
|
|
|
37148
38502
|
});
|
|
37149
38503
|
|
|
37150
38504
|
// src/skills/parser.ts
|
|
37151
|
-
import { createHash as
|
|
38505
|
+
import { createHash as createHash7 } from "crypto";
|
|
37152
38506
|
function sha2562(input) {
|
|
37153
|
-
return
|
|
38507
|
+
return createHash7("sha256").update(input).digest("hex");
|
|
37154
38508
|
}
|
|
37155
38509
|
function computeContentHash(rawText, parserVersion) {
|
|
37156
38510
|
return sha2562(`${rawText}
|
|
@@ -37341,8 +38695,8 @@ var init_skills = __esm({
|
|
|
37341
38695
|
});
|
|
37342
38696
|
|
|
37343
38697
|
// src/ui/run-startup-tasks.ts
|
|
37344
|
-
import { existsSync as
|
|
37345
|
-
import { join as
|
|
38698
|
+
import { existsSync as existsSync11 } from "fs";
|
|
38699
|
+
import { join as join52 } from "path";
|
|
37346
38700
|
function runStartupTasks(deps) {
|
|
37347
38701
|
const {
|
|
37348
38702
|
cfg,
|
|
@@ -37356,6 +38710,7 @@ function runStartupTasks(deps) {
|
|
|
37356
38710
|
} = deps;
|
|
37357
38711
|
const auth = llmAuthFromConfig(cfg);
|
|
37358
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 }));
|
|
37359
38714
|
void Promise.resolve().then(() => (init_log_sink(), log_sink_exports)).then(({ pruneOldLogs: pruneOldLogs2, isLogSinkEnabled: isLogSinkEnabled2 }) => {
|
|
37360
38715
|
if (!isLogSinkEnabled2()) return;
|
|
37361
38716
|
try {
|
|
@@ -37364,7 +38719,7 @@ function runStartupTasks(deps) {
|
|
|
37364
38719
|
}
|
|
37365
38720
|
});
|
|
37366
38721
|
if (cfg.memoryEnabled) {
|
|
37367
|
-
const dbPath = cfg.memoryDbPath ??
|
|
38722
|
+
const dbPath = cfg.memoryDbPath ?? join52(process.cwd(), ".kimiflare", "memory.db");
|
|
37368
38723
|
const manager = new MemoryManager({
|
|
37369
38724
|
dbPath,
|
|
37370
38725
|
...auth,
|
|
@@ -37396,7 +38751,7 @@ function runStartupTasks(deps) {
|
|
|
37396
38751
|
});
|
|
37397
38752
|
const cwd = process.cwd();
|
|
37398
38753
|
sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
|
|
37399
|
-
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)))) {
|
|
37400
38755
|
const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
|
|
37401
38756
|
const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
|
|
37402
38757
|
if (driftCount >= 5) {
|
|
@@ -37407,7 +38762,7 @@ function runStartupTasks(deps) {
|
|
|
37407
38762
|
memoryManagerRef.current?.close();
|
|
37408
38763
|
memoryManagerRef.current = null;
|
|
37409
38764
|
}
|
|
37410
|
-
const skillDbPath = cfg.memoryDbPath ??
|
|
38765
|
+
const skillDbPath = cfg.memoryDbPath ?? join52(process.cwd(), ".kimiflare", "memory.db");
|
|
37411
38766
|
const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
|
|
37412
38767
|
initSkillsSchema(skillDb);
|
|
37413
38768
|
void indexSkills({
|
|
@@ -37652,7 +39007,8 @@ async function runCompact(deps) {
|
|
|
37652
39007
|
pendingToolCallsRef,
|
|
37653
39008
|
hooks,
|
|
37654
39009
|
sessionId,
|
|
37655
|
-
supervisorRef
|
|
39010
|
+
supervisorRef,
|
|
39011
|
+
trace
|
|
37656
39012
|
} = deps;
|
|
37657
39013
|
if (busy || supervisorRef.current.isRunning) {
|
|
37658
39014
|
setEvents((e) => [
|
|
@@ -37688,6 +39044,7 @@ async function runCompact(deps) {
|
|
|
37688
39044
|
if (result2.metrics.rawTurnsRemoved === 0) {
|
|
37689
39045
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "nothing to compact yet" }]);
|
|
37690
39046
|
} else {
|
|
39047
|
+
traceCompiledCompaction(trace, result2);
|
|
37691
39048
|
messagesRef.current = result2.newMessages;
|
|
37692
39049
|
sessionStateRef.current = result2.newState;
|
|
37693
39050
|
setEvents(
|
|
@@ -37706,6 +39063,7 @@ async function runCompact(deps) {
|
|
|
37706
39063
|
await saveSessionSafe();
|
|
37707
39064
|
}
|
|
37708
39065
|
} else {
|
|
39066
|
+
const before = messagesRef.current;
|
|
37709
39067
|
const result2 = await summarizeMessagesViaLlm({
|
|
37710
39068
|
...llmAuthFromConfig(cfg),
|
|
37711
39069
|
model: cfg.model,
|
|
@@ -37715,6 +39073,7 @@ async function runCompact(deps) {
|
|
|
37715
39073
|
if (result2.replacedCount === 0) {
|
|
37716
39074
|
setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "nothing to compact yet" }]);
|
|
37717
39075
|
} else {
|
|
39076
|
+
traceLlmCompaction(trace, before, result2);
|
|
37718
39077
|
messagesRef.current = result2.newMessages;
|
|
37719
39078
|
setEvents(
|
|
37720
39079
|
(e) => compactEventsVisual(
|
|
@@ -37755,12 +39114,13 @@ var init_run_compact = __esm({
|
|
|
37755
39114
|
init_logger();
|
|
37756
39115
|
init_app_helpers();
|
|
37757
39116
|
init_llm_auth();
|
|
39117
|
+
init_compaction();
|
|
37758
39118
|
}
|
|
37759
39119
|
});
|
|
37760
39120
|
|
|
37761
39121
|
// src/commands/save.ts
|
|
37762
39122
|
import { mkdir as mkdir16, writeFile as writeFile15, unlink as unlink5 } from "fs/promises";
|
|
37763
|
-
import { dirname as
|
|
39123
|
+
import { dirname as dirname21 } from "path";
|
|
37764
39124
|
async function saveCustomCommand(opts2) {
|
|
37765
39125
|
const dir = opts2.source === "project" ? projectCommandsDir(opts2.cwd) : globalCommandsDir();
|
|
37766
39126
|
const filepath = `${dir}/${opts2.name}.md`;
|
|
@@ -37771,7 +39131,7 @@ async function saveCustomCommand(opts2) {
|
|
|
37771
39131
|
if (opts2.effort) data.effort = opts2.effort;
|
|
37772
39132
|
const frontmatter = serializeFrontmatter(data);
|
|
37773
39133
|
const content = frontmatter + opts2.template;
|
|
37774
|
-
await mkdir16(
|
|
39134
|
+
await mkdir16(dirname21(filepath), { recursive: true });
|
|
37775
39135
|
await writeFile15(filepath, content, "utf8");
|
|
37776
39136
|
return { filepath };
|
|
37777
39137
|
}
|
|
@@ -37894,9 +39254,9 @@ __export(app_exports, {
|
|
|
37894
39254
|
});
|
|
37895
39255
|
import React25, { useState as useState27, useRef as useRef9, useEffect as useEffect11, useCallback as useCallback8, useMemo as useMemo6 } from "react";
|
|
37896
39256
|
import { Box as Box40, Text as Text41, useApp, useInput as useInput19, useWindowSize as useWindowSize2, render } from "ink";
|
|
37897
|
-
import { existsSync as
|
|
37898
|
-
import { join as
|
|
37899
|
-
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";
|
|
37900
39260
|
import { jsx as jsx42, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
37901
39261
|
function App({
|
|
37902
39262
|
initialCfg,
|
|
@@ -38153,6 +39513,8 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38153
39513
|
}, []);
|
|
38154
39514
|
const sessionStartRecallRef = useRef9(null);
|
|
38155
39515
|
const kimiMdStaleNudgedRef = useRef9(false);
|
|
39516
|
+
const currentTurnIdRef = useRef9(null);
|
|
39517
|
+
const denyFeedbackRef = useRef9(false);
|
|
38156
39518
|
const sessionPlanRef = useRef9(null);
|
|
38157
39519
|
const planCompletePlanRef = useRef9(null);
|
|
38158
39520
|
const sessionMgr = useSessionManager({
|
|
@@ -38190,7 +39552,11 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38190
39552
|
doResumeSession,
|
|
38191
39553
|
handleResumePick,
|
|
38192
39554
|
handleCheckpointPick,
|
|
38193
|
-
resetSession
|
|
39555
|
+
resetSession,
|
|
39556
|
+
traceRef,
|
|
39557
|
+
ensureTraceStarted,
|
|
39558
|
+
turnHistoryRef,
|
|
39559
|
+
noteTurnEnded
|
|
38194
39560
|
} = sessionMgr;
|
|
38195
39561
|
const pendingTextRef = useRef9(/* @__PURE__ */ new Map());
|
|
38196
39562
|
const flushTimeoutRef = useRef9(null);
|
|
@@ -38216,7 +39582,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38216
39582
|
const exactDirectory = query === "~" || query === "." || query === ".." || query.endsWith("/..");
|
|
38217
39583
|
const prefix = exactDirectory ? `${query.replace(/\/$/, "")}/` : slash >= 0 ? query.slice(0, slash + 1) : "";
|
|
38218
39584
|
const directory = exactDirectory ? query : slash >= 0 ? query.slice(0, slash + 1) : query;
|
|
38219
|
-
const expanded = directory.startsWith("~") ?
|
|
39585
|
+
const expanded = directory.startsWith("~") ? join53(homedir30(), directory.slice(1)) : directory;
|
|
38220
39586
|
const target = resolve14(cwd, expanded || ".");
|
|
38221
39587
|
const entries2 = await glob("*", {
|
|
38222
39588
|
cwd: target,
|
|
@@ -38477,6 +39843,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38477
39843
|
store
|
|
38478
39844
|
});
|
|
38479
39845
|
if (result2.metrics.rawTurnsRemoved > 0) {
|
|
39846
|
+
traceCompiledCompaction(traceRef.current, result2, currentTurnIdRef.current);
|
|
38480
39847
|
sessionStateRef.current = result2.newState;
|
|
38481
39848
|
setEvents((e) => [
|
|
38482
39849
|
...e,
|
|
@@ -38522,6 +39889,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38522
39889
|
signal
|
|
38523
39890
|
});
|
|
38524
39891
|
if (result2.replacedCount > 0) {
|
|
39892
|
+
traceLlmCompaction(traceRef.current, messages, result2, currentTurnIdRef.current);
|
|
38525
39893
|
setEvents((e) => [
|
|
38526
39894
|
...e,
|
|
38527
39895
|
{
|
|
@@ -38695,7 +40063,8 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38695
40063
|
pendingToolCallsRef,
|
|
38696
40064
|
hooks: hooksManagerRef.current,
|
|
38697
40065
|
sessionId: sessionIdRef.current,
|
|
38698
|
-
supervisorRef
|
|
40066
|
+
supervisorRef,
|
|
40067
|
+
trace: traceRef.current
|
|
38699
40068
|
});
|
|
38700
40069
|
}, [cfg, busy, saveSessionSafe]);
|
|
38701
40070
|
const runInit2 = useCallback8(async () => {
|
|
@@ -38875,7 +40244,9 @@ ${wcagWarnings.join("\n")}` }
|
|
|
38875
40244
|
compiledContextRef,
|
|
38876
40245
|
lastApiErrorRef,
|
|
38877
40246
|
activeScopeRef,
|
|
38878
|
-
sessionPlanRef
|
|
40247
|
+
sessionPlanRef,
|
|
40248
|
+
traceRef,
|
|
40249
|
+
turnHistoryRef
|
|
38879
40250
|
}), [
|
|
38880
40251
|
exit,
|
|
38881
40252
|
busy,
|
|
@@ -39178,6 +40549,8 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39178
40549
|
}
|
|
39179
40550
|
}
|
|
39180
40551
|
messagesRef.current.push({ role: "user", content });
|
|
40552
|
+
const traceUserSource = denyFeedbackRef.current ? "deny_feedback" : "typed";
|
|
40553
|
+
denyFeedbackRef.current = false;
|
|
39181
40554
|
await saveSessionSafe();
|
|
39182
40555
|
if (compiledContextRef.current) {
|
|
39183
40556
|
const { ids, recalled } = recallArtifacts(messagesRef.current, artifactStoreRef.current, sessionStateRef.current);
|
|
@@ -39191,7 +40564,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39191
40564
|
}
|
|
39192
40565
|
}
|
|
39193
40566
|
turnCounterRef.current += 1;
|
|
39194
|
-
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) {
|
|
39195
40568
|
setEvents((e) => [
|
|
39196
40569
|
...e,
|
|
39197
40570
|
{ kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps autopilot (formerly kimiflare) stay accurate as your project evolves." }
|
|
@@ -39215,7 +40588,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39215
40588
|
};
|
|
39216
40589
|
ensureSessionId();
|
|
39217
40590
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
39218
|
-
const filePath =
|
|
40591
|
+
const filePath = join53(sessionsDir3(), `${sessionIdRef.current}.json`);
|
|
39219
40592
|
await addCheckpoint(filePath, cp);
|
|
39220
40593
|
}
|
|
39221
40594
|
const summary = await generateContinuationSummary({
|
|
@@ -39464,6 +40837,34 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39464
40837
|
offered: turnTools.some((tool) => tool.name === "subagent"),
|
|
39465
40838
|
guidance: delegationGuidance.kind
|
|
39466
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
|
+
}
|
|
39467
40868
|
supervisorRef.current.startTurn(
|
|
39468
40869
|
{
|
|
39469
40870
|
...llmAuthFromConfig(cfg),
|
|
@@ -39509,6 +40910,8 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39509
40910
|
},
|
|
39510
40911
|
mode: modeRef.current,
|
|
39511
40912
|
cacheStable: cacheStableRef.current,
|
|
40913
|
+
trace,
|
|
40914
|
+
turnId,
|
|
39512
40915
|
onFileChange: (path, content2) => {
|
|
39513
40916
|
if (content2) {
|
|
39514
40917
|
lspManagerRef.current.notifyChange(path, content2);
|
|
@@ -39523,6 +40926,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39523
40926
|
},
|
|
39524
40927
|
{
|
|
39525
40928
|
onDone: () => {
|
|
40929
|
+
noteTurnEnded(turnId);
|
|
39526
40930
|
void (async () => {
|
|
39527
40931
|
await saveSessionSafe();
|
|
39528
40932
|
if (turnScope.signal.aborted) {
|
|
@@ -39550,6 +40954,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39550
40954
|
store
|
|
39551
40955
|
});
|
|
39552
40956
|
if (result2.metrics.rawTurnsRemoved > 0) {
|
|
40957
|
+
traceCompiledCompaction(traceRef.current, result2, turnId);
|
|
39553
40958
|
messagesRef.current = result2.newMessages;
|
|
39554
40959
|
sessionStateRef.current = result2.newState;
|
|
39555
40960
|
setEvents((e) => [
|
|
@@ -39565,6 +40970,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39565
40970
|
}
|
|
39566
40971
|
} else {
|
|
39567
40972
|
try {
|
|
40973
|
+
const before = messagesRef.current;
|
|
39568
40974
|
const result2 = await summarizeMessagesViaLlm({
|
|
39569
40975
|
...llmAuthFromConfig(cfg),
|
|
39570
40976
|
model: cfg.model,
|
|
@@ -39572,6 +40978,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39572
40978
|
signal: turnScope.signal
|
|
39573
40979
|
});
|
|
39574
40980
|
if (result2.replacedCount > 0) {
|
|
40981
|
+
traceLlmCompaction(traceRef.current, before, result2, turnId);
|
|
39575
40982
|
messagesRef.current = result2.newMessages;
|
|
39576
40983
|
setEvents((e) => [
|
|
39577
40984
|
...e,
|
|
@@ -39637,6 +41044,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39637
41044
|
keepLastTurns: 1
|
|
39638
41045
|
});
|
|
39639
41046
|
if (result2.metrics.rawTurnsRemoved > 0) {
|
|
41047
|
+
traceCompiledCompaction(traceRef.current, result2, turnId);
|
|
39640
41048
|
messagesRef.current = result2.newMessages;
|
|
39641
41049
|
sessionStateRef.current = result2.newState;
|
|
39642
41050
|
setEvents((e) => [
|
|
@@ -39652,6 +41060,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39652
41060
|
}
|
|
39653
41061
|
} else {
|
|
39654
41062
|
try {
|
|
41063
|
+
const before = messagesRef.current;
|
|
39655
41064
|
const result2 = await summarizeMessagesViaLlm({
|
|
39656
41065
|
...llmAuthFromConfig(cfg),
|
|
39657
41066
|
model: cfg.model,
|
|
@@ -39660,6 +41069,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39660
41069
|
signal: turnScope.signal
|
|
39661
41070
|
});
|
|
39662
41071
|
if (result2.replacedCount > 0) {
|
|
41072
|
+
traceLlmCompaction(traceRef.current, before, result2, turnId);
|
|
39663
41073
|
messagesRef.current = result2.newMessages;
|
|
39664
41074
|
setEvents((e) => [
|
|
39665
41075
|
...e,
|
|
@@ -39700,7 +41110,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39700
41110
|
};
|
|
39701
41111
|
ensureSessionId();
|
|
39702
41112
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
39703
|
-
const filePath =
|
|
41113
|
+
const filePath = join53(sessionsDir3(), `${sessionIdRef.current}.json`);
|
|
39704
41114
|
await addCheckpoint(filePath, cp);
|
|
39705
41115
|
}
|
|
39706
41116
|
const summary = await generateContinuationSummary({
|
|
@@ -39751,6 +41161,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39751
41161
|
}
|
|
39752
41162
|
},
|
|
39753
41163
|
onError: async (e) => {
|
|
41164
|
+
noteTurnEnded(turnId);
|
|
39754
41165
|
if (e.name === "AbortError") {
|
|
39755
41166
|
for (const [tcId, tcName] of pendingToolCallsRef.current) {
|
|
39756
41167
|
messagesRef.current.push({
|
|
@@ -39782,7 +41193,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
39782
41193
|
}
|
|
39783
41194
|
);
|
|
39784
41195
|
},
|
|
39785
|
-
[cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateResponseMeta, setShowPlanCompletePicker, runUserShellCommand]
|
|
41196
|
+
[cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateResponseMeta, setShowPlanCompletePicker, runUserShellCommand, ensureTraceStarted]
|
|
39786
41197
|
);
|
|
39787
41198
|
useEffect11(() => {
|
|
39788
41199
|
if (!busy && queuePlanDraft === null && supervisorRef.current.phase === "idle") {
|
|
@@ -40283,7 +41694,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40283
41694
|
lsp: {
|
|
40284
41695
|
servers: cfg?.lspServers ?? {},
|
|
40285
41696
|
scope: lspScope,
|
|
40286
|
-
hasProjectDir:
|
|
41697
|
+
hasProjectDir: existsSync12(join53(process.cwd(), ".kimiflare"))
|
|
40287
41698
|
}
|
|
40288
41699
|
});
|
|
40289
41700
|
});
|
|
@@ -40340,7 +41751,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40340
41751
|
onCommandDelete: handleCommandDelete2,
|
|
40341
41752
|
lspServers: cfg?.lspServers ?? {},
|
|
40342
41753
|
lspScope,
|
|
40343
|
-
hasProjectDir:
|
|
41754
|
+
hasProjectDir: existsSync12(join53(process.cwd(), ".kimiflare")),
|
|
40344
41755
|
onLspSave: handleLspSave2,
|
|
40345
41756
|
themes: themeList(),
|
|
40346
41757
|
onPickTheme: handleThemePick,
|
|
@@ -40411,6 +41822,7 @@ ${result2.output ?? "(output not captured)"}`,
|
|
|
40411
41822
|
args: perm.args,
|
|
40412
41823
|
onDecide: decidePermission2,
|
|
40413
41824
|
onFeedback: (text) => {
|
|
41825
|
+
denyFeedbackRef.current = true;
|
|
40414
41826
|
submitRef.current(text);
|
|
40415
41827
|
}
|
|
40416
41828
|
}
|
|
@@ -40676,6 +42088,8 @@ var init_app = __esm({
|
|
|
40676
42088
|
init_run_startup_tasks();
|
|
40677
42089
|
init_manager_init();
|
|
40678
42090
|
init_run_compact();
|
|
42091
|
+
init_trace();
|
|
42092
|
+
init_registry2();
|
|
40679
42093
|
init_distill();
|
|
40680
42094
|
init_plan_resolver();
|
|
40681
42095
|
init_user_shell();
|
|
@@ -40821,6 +42235,8 @@ init_glob();
|
|
|
40821
42235
|
init_permissions_evaluator();
|
|
40822
42236
|
init_classify();
|
|
40823
42237
|
init_subagent_policy();
|
|
42238
|
+
init_trace();
|
|
42239
|
+
init_registry2();
|
|
40824
42240
|
import { readFile as readFile15 } from "fs/promises";
|
|
40825
42241
|
import { resolve as resolve6, basename as basename3 } from "path";
|
|
40826
42242
|
async function resolveSession(opts2) {
|
|
@@ -40860,8 +42276,8 @@ async function resolveFiles(filePatterns, cwd) {
|
|
|
40860
42276
|
const resolved = /* @__PURE__ */ new Set();
|
|
40861
42277
|
for (const pattern of filePatterns) {
|
|
40862
42278
|
try {
|
|
40863
|
-
const
|
|
40864
|
-
if (
|
|
42279
|
+
const stat11 = await import("fs/promises").then((m) => m.stat(resolve6(cwd, pattern)));
|
|
42280
|
+
if (stat11.isFile()) {
|
|
40865
42281
|
resolved.add(resolve6(cwd, pattern));
|
|
40866
42282
|
continue;
|
|
40867
42283
|
}
|
|
@@ -40955,24 +42371,43 @@ async function runPrintMode(opts2) {
|
|
|
40955
42371
|
});
|
|
40956
42372
|
}
|
|
40957
42373
|
const messages = [];
|
|
42374
|
+
const systemPrompt = buildSystemPrompt({ cwd, tools: workerTools, model: opts2.model, preferPullRequests: opts2.preferPullRequests });
|
|
40958
42375
|
if (isNew || sessionFile.messages.length === 0) {
|
|
40959
|
-
messages.push({
|
|
40960
|
-
role: "system",
|
|
40961
|
-
content: buildSystemPrompt({ cwd, tools: workerTools, model: opts2.model, preferPullRequests: opts2.preferPullRequests })
|
|
40962
|
-
});
|
|
42376
|
+
messages.push({ role: "system", content: systemPrompt });
|
|
40963
42377
|
} else {
|
|
40964
42378
|
const nonSystem = sessionFile.messages.filter(
|
|
40965
42379
|
(m) => m.role !== "system" || typeof m.content === "string" && m.content.startsWith("[compiled session state]")
|
|
40966
42380
|
);
|
|
40967
|
-
messages.push({
|
|
40968
|
-
role: "system",
|
|
40969
|
-
content: buildSystemPrompt({ cwd, tools: workerTools, model: opts2.model, preferPullRequests: opts2.preferPullRequests })
|
|
40970
|
-
});
|
|
42381
|
+
messages.push({ role: "system", content: systemPrompt });
|
|
40971
42382
|
messages.push(...nonSystem);
|
|
40972
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";
|
|
40973
42400
|
const files = opts2.files ? await resolveFiles(opts2.files, cwd) : [];
|
|
40974
42401
|
const { content: userContent } = await buildUserMessage(opts2.prompt, files, cwd);
|
|
40975
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
|
+
}
|
|
40976
42411
|
const controller = new AbortController();
|
|
40977
42412
|
process.on("SIGINT", () => controller.abort());
|
|
40978
42413
|
const format = opts2.format ?? "text";
|
|
@@ -40996,6 +42431,7 @@ async function runPrintMode(opts2) {
|
|
|
40996
42431
|
onUsageFinal: (u, meta) => {
|
|
40997
42432
|
const lookup = opts2.openrouterApiKey && !auth.customEndpoint && meta?.generationId ? { apiKey: opts2.openrouterApiKey, meta } : void 0;
|
|
40998
42433
|
usageWrites.push(recordUsage(sessionFile.id, u, lookup, opts2.model).catch(() => void 0));
|
|
42434
|
+
if (typeof u.cost === "number") traceCostUsd += u.cost;
|
|
40999
42435
|
},
|
|
41000
42436
|
onReasoningDelta: opts2.showReasoning ? (delta) => {
|
|
41001
42437
|
if (format === "text") {
|
|
@@ -41137,7 +42573,9 @@ async function runPrintMode(opts2) {
|
|
|
41137
42573
|
// --continue sessions grow across runs; archive older turns onto the
|
|
41138
42574
|
// session file (saved below) instead of failing on context size.
|
|
41139
42575
|
compaction: sessionFileCompactionTarget(sessionFile),
|
|
41140
|
-
callbacks
|
|
42576
|
+
callbacks,
|
|
42577
|
+
trace,
|
|
42578
|
+
turnId
|
|
41141
42579
|
});
|
|
41142
42580
|
} catch (err) {
|
|
41143
42581
|
if (err instanceof BudgetExhaustedError) {
|
|
@@ -41167,9 +42605,12 @@ async function runPrintMode(opts2) {
|
|
|
41167
42605
|
process.exitCode = 1;
|
|
41168
42606
|
return;
|
|
41169
42607
|
}
|
|
42608
|
+
traceEndReason = "crash";
|
|
41170
42609
|
throw err;
|
|
41171
42610
|
} finally {
|
|
41172
42611
|
await Promise.all(usageWrites);
|
|
42612
|
+
trace.emit("session_end", { reason: traceEndReason, total_cost_usd: traceCostUsd || void 0 }, null);
|
|
42613
|
+
await trace.flush();
|
|
41173
42614
|
}
|
|
41174
42615
|
sessionFile.messages = messages;
|
|
41175
42616
|
sessionFile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -41483,7 +42924,9 @@ To use Requesty instead, set REQUESTY_API_KEY or run autopilot auth requesty`
|
|
|
41483
42924
|
dir: opts.dir,
|
|
41484
42925
|
title: opts.title,
|
|
41485
42926
|
permissions: cfg.permissions,
|
|
41486
|
-
workerProfile: opts.workerProfile
|
|
42927
|
+
workerProfile: opts.workerProfile,
|
|
42928
|
+
trace: cfg.trace,
|
|
42929
|
+
traceConfigSnapshot: cfg
|
|
41487
42930
|
});
|
|
41488
42931
|
return;
|
|
41489
42932
|
}
|